Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions api/v1alpha1/valkeycluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,54 @@ type ValkeyClusterSpec struct {
// +kubebuilder:default=Managed
// +optional
PodDisruptionBudget PDBPolicy `json:"podDisruptionBudget,omitempty"`

// ExternalAccess configures reachability of the cluster from outside Kubernetes.
// When omitted, the cluster is internal-only and behaves identically to a cluster
// without this field. Requires Valkey 9.0+.
// +optional
ExternalAccess *ExternalAccessSpec `json:"externalAccess,omitempty"`
}

// ExternalAccessSpec defines how a ValkeyCluster is exposed to clients outside the
// Kubernetes cluster. Node-to-node traffic always stays on internal pod IPs; only
// the client-facing endpoint is affected.
type ExternalAccessSpec struct {
// Enabled turns on external access for the cluster.
// +optional
Enabled bool `json:"enabled,omitempty"`

// ServiceType is the type of the per-shard Service. NodePort ports are allocated
// by Kubernetes; LoadBalancer frontend ports are derived from the node index.
// +kubebuilder:default=NodePort
// +kubebuilder:validation:Enum=NodePort;LoadBalancer
// +optional
ServiceType corev1.ServiceType `json:"serviceType,omitempty"`

// ExternalTrafficPolicy sets the externalTrafficPolicy of the per-shard Service.
// Use Local to preserve the client source IP, which requires DNS to resolve a
// shard hostname to the nodes hosting that shard's pods.
// +kubebuilder:validation:Enum=Cluster;Local
// +optional
ExternalTrafficPolicy corev1.ServiceExternalTrafficPolicy `json:"externalTrafficPolicy,omitempty"`

// ServiceAnnotations are applied to each per-shard Service, for example to
// configure external-dns or a cloud load-balancer controller.
// +optional
ServiceAnnotations map[string]string `json:"serviceAnnotations,omitempty"`

// HostnamePrefix is prepended to each shard hostname, which is announced as
// "<hostnamePrefix>-<shardIndex>.<domain>". Set a unique prefix per cluster when
// several clusters share one domain. Has no effect unless domain is set.
// +kubebuilder:default=shard
// +kubebuilder:validation:Pattern=`^[a-z0-9]([-a-z0-9]*[a-z0-9])?$`
// +optional
HostnamePrefix string `json:"hostnamePrefix,omitempty"`

// Domain is the DNS domain under which shard hostnames are announced. When set,
// each node announces the hostname "<hostnamePrefix>-<shardIndex>.<domain>" to
// clients in addition to its IP. The hostname must resolve to the shard's Service.
// +optional
Domain string `json:"domain,omitempty"`
Comment on lines +189 to +193
}

// TLSConfig defines the TLS configuration for ValkeyCluster.
Expand Down Expand Up @@ -212,6 +260,25 @@ type ValkeyClusterStatus struct {
// +listMapKey=type
// +optional
Conditions []metav1.Condition `json:"conditions,omitempty"`

// ExternalEndpoints lists the externally-reachable endpoint of each shard,
// populated when external access is enabled.
// +listType=map
// +listMapKey=shardIndex
// +optional
ExternalEndpoints []ShardEndpoint `json:"externalEndpoints,omitempty"`
}

// ShardEndpoint describes the externally-reachable endpoint of a single shard.
type ShardEndpoint struct {
// ShardIndex is the index of the shard this endpoint belongs to.
ShardIndex int32 `json:"shardIndex"`

// NodePorts are the external ports of the shard's nodes, indexed by node index
// (NodePorts[0] is the node-index 0 port). The address to reach each port
// depends on the Service type and the user's DNS configuration.
// +optional
NodePorts []int32 `json:"nodePorts,omitempty"`
Comment on lines +277 to +281
}

const (
Expand Down
5 changes: 5 additions & 0 deletions api/v1alpha1/valkeynode_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,11 @@ type ValkeyNodeSpec struct {
// the rest take effect on the next pod roll.
// +optional
Config map[string]string `json:"config,omitempty"`

// ExternalAccess is copied verbatim from the owning cluster's Spec.ExternalAccess
// and configures how this node is exposed to clients outside the Kubernetes cluster.
// +optional
ExternalAccess *ExternalAccessSpec `json:"externalAccess,omitempty"`
}

// ValkeyNodeStatus defines the observed state of ValkeyNode.
Expand Down
59 changes: 59 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

78 changes: 78 additions & 0 deletions config/crd/bases/valkey.io_valkeyclusters.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2571,6 +2571,55 @@ spec:
type: object
type: object
type: object
externalAccess:
description: |-
ExternalAccess configures reachability of the cluster from outside Kubernetes.
When omitted, the cluster is internal-only and behaves identically to a cluster
without this field. Requires Valkey 9.0+.
properties:
domain:
description: |-
Domain is the DNS domain under which shard hostnames are announced. When set,
each node announces the hostname "<hostnamePrefix>-<shardIndex>.<domain>" to
clients in addition to its IP. The hostname must resolve to the shard's Service.
type: string
enabled:
description: Enabled turns on external access for the cluster.
type: boolean
externalTrafficPolicy:
description: |-
ExternalTrafficPolicy sets the externalTrafficPolicy of the per-shard Service.
Use Local to preserve the client source IP, which requires DNS to resolve a
shard hostname to the nodes hosting that shard's pods.
enum:
- Cluster
- Local
type: string
hostnamePrefix:
default: shard
description: |-
HostnamePrefix is prepended to each shard hostname, which is announced as
"<hostnamePrefix>-<shardIndex>.<domain>". Set a unique prefix per cluster when
several clusters share one domain. Has no effect unless domain is set.
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
serviceAnnotations:
additionalProperties:
type: string
description: |-
ServiceAnnotations are applied to each per-shard Service, for example to
configure external-dns or a cloud load-balancer controller.
type: object
serviceType:
default: NodePort
description: |-
ServiceType is the type of the per-shard Service. NodePort ports are allocated
by Kubernetes; LoadBalancer frontend ports are derived from the node index.
enum:
- NodePort
- LoadBalancer
type: string
type: object
image:
description: Override the default Valkey image
type: string
Expand Down Expand Up @@ -3142,6 +3191,35 @@ spec:
x-kubernetes-list-map-keys:
- type
x-kubernetes-list-type: map
externalEndpoints:
description: |-
ExternalEndpoints lists the externally-reachable endpoint of each shard,
populated when external access is enabled.
items:
description: ShardEndpoint describes the externally-reachable endpoint
of a single shard.
properties:
nodePorts:
description: |-
NodePorts are the external ports of the shard's nodes, indexed by node index
(NodePorts[0] is the node-index 0 port). The address to reach each port
depends on the Service type and the user's DNS configuration.
items:
format: int32
type: integer
type: array
shardIndex:
description: ShardIndex is the index of the shard this endpoint
belongs to.
format: int32
type: integer
required:
- shardIndex
type: object
type: array
x-kubernetes-list-map-keys:
- shardIndex
x-kubernetes-list-type: map
message:
description: Message provides human-readable details about the current
state.
Expand Down
48 changes: 48 additions & 0 deletions config/crd/bases/valkey.io_valkeynodes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2580,6 +2580,54 @@ spec:
type: object
type: object
type: object
externalAccess:
description: |-
ExternalAccess is copied verbatim from the owning cluster's Spec.ExternalAccess
and configures how this node is exposed to clients outside the Kubernetes cluster.
properties:
domain:
description: |-
Domain is the DNS domain under which shard hostnames are announced. When set,
each node announces the hostname "<hostnamePrefix>-<shardIndex>.<domain>" to
clients in addition to its IP. The hostname must resolve to the shard's Service.
type: string
enabled:
description: Enabled turns on external access for the cluster.
type: boolean
externalTrafficPolicy:
description: |-
ExternalTrafficPolicy sets the externalTrafficPolicy of the per-shard Service.
Use Local to preserve the client source IP, which requires DNS to resolve a
shard hostname to the nodes hosting that shard's pods.
enum:
- Cluster
- Local
type: string
hostnamePrefix:
default: shard
description: |-
HostnamePrefix is prepended to each shard hostname, which is announced as
"<hostnamePrefix>-<shardIndex>.<domain>". Set a unique prefix per cluster when
several clusters share one domain. Has no effect unless domain is set.
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
serviceAnnotations:
additionalProperties:
type: string
description: |-
ServiceAnnotations are applied to each per-shard Service, for example to
configure external-dns or a cloud load-balancer controller.
type: object
serviceType:
default: NodePort
description: |-
ServiceType is the type of the per-shard Service. NodePort ports are allocated
by Kubernetes; LoadBalancer frontend ports are derived from the node index.
enum:
- NodePort
- LoadBalancer
type: string
type: object
image:
description: Image is the Valkey container image to use.
type: string
Expand Down
1 change: 1 addition & 0 deletions config/samples/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
resources:
- v1alpha1_valkeycluster.yaml
- v1alpha1_valkeycluster-persistent.yaml
- v1alpha1_valkeycluster-external-access.yaml
# +kubebuilder:scaffold:manifestskustomizesamples
11 changes: 11 additions & 0 deletions config/samples/v1alpha1_valkeycluster-external-access.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: valkey.io/v1alpha1
kind: ValkeyCluster
metadata:
name: cluster-external-access-sample
spec:
shards: 3
replicas: 1
externalAccess:
enabled: true
serviceType: NodePort
domain: valkey.example.com
30 changes: 30 additions & 0 deletions docs/valkeycluster.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

- [Config](#config)
- [Containers](#containers)
- [External access](#external-access)
- [Metrics](#metrics)
- [Persistence](#persistence)
- [Pod disruption budget](#pod-disruption-budget)
Expand Down Expand Up @@ -63,6 +64,35 @@ containers:

`containers` patches the pod's container list using strategic merge patch. Containers named `server` or `metrics-exporter` are merged by name; anything else is appended as a sidecar.

### External access

```yaml
externalAccess:
enabled: true
serviceType: NodePort
domain: valkey.example.com
```

`externalAccess` configures reachability of the cluster from outside Kubernetes. When omitted, the cluster is internal-only and behaves identically to a cluster without this field. Requires Valkey 9.0+.

Enabling external access announces a human-readable node name (the ValkeyNode name, e.g. `cluster-sample-1-2`) so cluster events such as failures reference it alongside the node ID. Node-to-node traffic (gossip and replication) always stays on internal pod IPs.

The operator creates one Service per shard, selecting that shard's pods and exposing one port per node. Each port targets a single node, so a client can reach a specific primary or replica.

| Field | Description |
|---|---|
| `serviceType` | `NodePort` (default) or `LoadBalancer`. |
| `externalTrafficPolicy` | `Cluster` (default) or `Local`. Use `Local` to preserve the client source IP. |
| `serviceAnnotations` | Applied to each per-shard Service, e.g. for external-dns or a cloud load-balancer controller. |
| `hostnamePrefix` | Prefix for shard hostnames (default `shard`). Set a unique prefix per cluster when several clusters share one domain. |
| `domain` | DNS domain for shard hostnames. When set, each node announces `<hostnamePrefix>-<shardIndex>.<domain>` to clients. |

With `NodePort`, Kubernetes allocates the external ports; the operator reads them back and reports them per shard under `status.externalEndpoints` (indexed by node). With `LoadBalancer`, each shard's node ports are `6379 + nodeIndex`.

When `domain` is set, each shard announces the hostname `<hostnamePrefix>-<shardIndex>.<domain>` (e.g. `shard-0.valkey.example.com`). You are responsible for the DNS records that resolve these hostnames to the shard Services. The hostname is announced only as metadata until clients are switched to it; the cluster bus continues to use pod IPs.

When TLS is enabled, the certificate must cover both the internal Service FQDN (`valkey-<cluster>.<namespace>.svc.cluster.local`) and every shard hostname (`<hostnamePrefix>-0.<domain>` … `<hostnamePrefix>-N.<domain>`). Widen the certificate before scaling out.

### Metrics

```yaml
Expand Down
Loading