[Design] External access for ValkeyCluster (Ingress/LoadBalancer/NodePort) #259
Replies: 1 comment
|
I took this design forward and implemented it under #276, following the direction sketched here: per-shard Services, reading back allocated ports, and publishing endpoints in status, plus a Gateway API path. The core idea that made it safe is that Valkey 9.0 separates the node-to-node announce settings ( It ships as a stack of small, backward-compatible PRs behind an opt-in
The SNI/TLSRoute routing you raised isn't in this first cut, so it would be a natural follow-up. Feedback welcome on #276. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
External access for ValkeyCluster (Ingress/LoadBalancer/NodePort)
Summary
This is Proposal for adding external access for
ValkeyClusterthat exposes a cluster to topology-aware clients outside Kubernetes Cluster via one of three mechanisms — Ingress (SNI), LoadBalancer, or NodePort.For all three types the operator creates the per-shard
Services, reads back the actual allocation (NodePort number / LoadBalancer IP), advertises it to clients, and publishes every shard's external endpoint instatus. The only objects a user still creates are the Gateway /TLSRoute/ Ingress for the SNI case (these are ingress-implementation specific — Envoy Gateway, Istio, NGINX, OpenShift — so they stay out of the operator).Why this is needed
A valkey cluster client is topology aware. It uses
CLUSTER SLOTS/CLUSTER NODESto discover the shards , then connects directly to endpoint each shard advertises. Today every node runsvalkey-server --cluster-announce-ip $(POD_IP), so clients discover the pod IPs of the shards - unroutable from outside the cluster.cluster-announce-hostname+ port). The cluster bus (:16379) stays on pod IPs (cluster-announce-ip $(POD_IP)untouched); the bus is never exposed externally.CLUSTER SLOTS→ connect directly to each shard's advertised endpoint. So every shard must advertise an externally reachablehost:port.Proposed CRD spec
Validations:
advertisedHostrequired forIngress;Ingressrequiresspec.tls;advertisedHostoptional forLoadBalancerandNodePort.What the operator creates / announces / surfaces per type
cluster-announce-hostnameorcluster-announce-ip+ port)status.externalAccess.endpoints[]cluster-announce-hostname:advertisedHost(per-shard SNI hostname) +cluster-announce-tls-port:advertisedPort(6380)TLSRoute(or Ingressssl-passthrough), CNAME to ingress LB IPtype=LoadBalancerService per shard; waits for cloud to assign IP, then uses itcluster-announce-ip: LB IP from Service status (default) orcluster-announce-hostname:advertisedHostif set; +cluster-announce-tls-portorcluster-announce-port:advertisedPortbased on tls enabled or notadvertisedHosttype=NodePortService per shard (requestsstartPort+i; falls back to dynamic if taken); uses the actual allocated port from Service speccluster-announce-ip:node IP/cluster-announce-hostname:advertisedHostfronting nodes) +cluster-announce-tls-portorcluster-announce-port: actual allocated nodePort per shardadvertisedHost+ actual nodePortStatus CRD updates
Per type,
endpoints[]reports respectively: Ingress → the per-shard SNI hostnames (clients/DNS point these at the Gateway IP); LoadBalancer → the per-shard LB IPs; NodePort → theadvertisedHostornodeIP+ the actual nodePort per shard.Operator changes when external access is enabled
type(ClusterIP / LoadBalancer / NodePort), one per shard, selectingvalkey.io/shard-index: "<i>".spec.ports[].nodePort; LoadBalancer → pollstatus.loadBalancer.ingress[]until an IP/hostname appears (requeue meanwhile); Ingress → resolveadvertisedHosttemplate (no wait).ValkeyNode→ configvalkey-serverwith (--cluster-announce-hostnameor--cluster-announce-ip) +--cluster-announce-(tls-)port.cluster-preferred-endpoint-type hostnamein config based on service type.status.externalAccesswith every shard's resolved endpoints/IPs/ports based on service type.within operator we need to create services based on the type user selects in spec
externalAccess.typeA- Ingress
B — LoadBalancer (one LB per shard)
C — NodePort (port-per-shard)
The three approaches
spec.tls)Client-side Routes example (only needed for Ingress/SNI)
For LoadBalancer and NodePort the operator creates the Services — users create nothing (optionally DNS). For Ingress the user provides the SNI routes and points DNS at it, as these are implementation-specific (Envoy Gateway vs Isitio vs NGINX vs Openshift Routes etc.)
DNS/CNAME: for each shard create a CNAME record
my-cluster-{shard}.example.com-> Gateway LB IP. NGINX alternative: an Ingress per shard withnginx.ingress.kubernetes.io/ssl-passthrough: "true". extra CNAMEmy-cluster.example.com-> Gateway LB IP. This allows clients to connect tomy-cluster.example.com:6380, not per shard.References
nginx, istio, openshiftRoute.Contribution
All reactions