Files
redis-cluster/templates/statefulset.yaml
Corrado Mulas a407903a8d First Commit
2026-07-23 12:09:41 +02:00

312 lines
13 KiB
YAML

{{- $root := . -}}
{{- $fullname := include "redis-cluster.fullname" . -}}
{{- if gt (len $fullname) 53 -}}
{{- fail (printf "release fullname %q is too long; use fullnameOverride with at most 53 characters" $fullname) -}}
{{- end -}}
{{- if ge (int .Values.cluster.replicasPerShard) (int .Values.cluster.shards) -}}
{{- fail "cluster.replicasPerShard must be lower than cluster.shards so crossed placement never puts a shard replica with its own primary" -}}
{{- end -}}
{{- $headless := include "redis-cluster.headlessFqdn" . -}}
{{- $nodeCount := include "redis-cluster.nodeCount" . -}}
{{- $allHosts := list -}}
{{- $primaryHosts := list -}}
{{- $assignments := list -}}
{{- $addressAssignments := list -}}
{{- range $shard := until (int .Values.cluster.shards) -}}
{{- range $member := until (int (add (int $.Values.cluster.replicasPerShard) 1)) -}}
{{- $memberName := printf "%s-s%d-n%d" $fullname $shard $member | trunc 63 | trimSuffix "-" -}}
{{- $podName := printf "%s-0" $memberName -}}
{{- $host := printf "%s-0.%s" $memberName $headless -}}
{{- $allHosts = append $allHosts $host -}}
{{- $addressAssignments = append $addressAssignments (printf "%s=%s" $podName $host) -}}
{{- if eq $member 0 -}}
{{- $primaryHosts = append $primaryHosts $host -}}
{{- else -}}
{{- $primaryName := printf "%s-s%d-n0" $fullname $shard | trunc 63 | trimSuffix "-" -}}
{{- $primaryHost := printf "%s-0.%s" $primaryName $headless -}}
{{- $assignments = append $assignments (printf "%s=%s" $host $primaryHost) -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- $coordinator := first $primaryHosts -}}
{{- range $shard := until (int .Values.cluster.shards) }}
{{- range $member := until (int (add (int $.Values.cluster.replicasPerShard) 1)) }}
{{- $memberName := printf "%s-s%d-n%d" $fullname $shard $member | trunc 63 | trimSuffix "-" }}
{{- $memberIndex := add (mul $shard (add (int $.Values.cluster.replicasPerShard) 1)) $member }}
{{- $announceHostname := printf "%s-0.%s" $memberName $headless }}
{{- $externalServiceName := printf "%s-x" $memberName }}
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: {{ $memberName }}
namespace: {{ $.Release.Namespace }}
labels:
{{- include "redis-cluster.labels" $root | nindent 4 }}
redis-cluster.shard: {{ $shard | quote }}
redis-cluster.member: {{ $member | quote }}
spec:
serviceName: {{ $fullname }}-headless
replicas: 1
podManagementPolicy: Parallel
# Separate one-member StatefulSets would otherwise all roll concurrently.
# OnDelete makes upgrades explicit and safe; see README.md.
updateStrategy:
type: OnDelete
selector:
matchLabels:
{{- include "redis-cluster.selectorLabels" $root | nindent 6 }}
redis-cluster.shard: {{ $shard | quote }}
redis-cluster.member: {{ $member | quote }}
template:
metadata:
labels:
{{- include "redis-cluster.selectorLabels" $root | nindent 8 }}
redis-cluster.shard: {{ $shard | quote }}
redis-cluster.member: {{ $member | quote }}
{{- with $.Values.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") $root | sha256sum }}
{{- if not $.Values.istio.injectSidecar }}
sidecar.istio.io/inject: "false"
{{- else }}
proxy.istio.io/config: '{ "holdApplicationUntilProxyStarts": true }'
{{- end }}
{{- with $.Values.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
serviceAccountName: {{ include "redis-cluster.serviceAccountName" $root }}
automountServiceAccountToken: false
terminationGracePeriodSeconds: {{ $.Values.terminationGracePeriodSeconds }}
{{- with $.Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with $.Values.priorityClassName }}
priorityClassName: {{ . }}
{{- end }}
securityContext:
{{- toYaml $.Values.podSecurityContext | nindent 8 }}
affinity:
{{- if gt $member 0 }}
# Cross replicas onto the worker holding another shard's canonical
# primary. For 3x1 this produces A:P0+R2, B:P1+R0, C:P2+R1.
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
{{- include "redis-cluster.selectorLabels" $root | nindent 18 }}
redis-cluster.shard: {{ mod (add $shard $member) (int $.Values.cluster.shards) | quote }}
redis-cluster.member: "0"
topologyKey: kubernetes.io/hostname
{{- end }}
podAntiAffinity:
# Hard invariants: copies of one shard never share a worker, and
# equivalent member identities are spread across workers. In the
# canonical topology, this permits at most one primary per worker.
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
{{- include "redis-cluster.selectorLabels" $root | nindent 18 }}
redis-cluster.shard: {{ $shard | quote }}
topologyKey: kubernetes.io/hostname
- labelSelector:
matchLabels:
{{- include "redis-cluster.selectorLabels" $root | nindent 18 }}
redis-cluster.member: {{ $member | quote }}
topologyKey: kubernetes.io/hostname
{{- with $.Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with $.Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with $.Values.topologySpreadConstraints }}
topologySpreadConstraints:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if $.Values.externalAccess.enabled }}
initContainers:
- name: discover-external-endpoint
image: "{{ $.Values.externalAccess.endpointDiscovery.image.repository }}:{{ $.Values.externalAccess.endpointDiscovery.image.tag }}"
imagePullPolicy: {{ $.Values.externalAccess.endpointDiscovery.image.pullPolicy }}
command: ["/bin/sh", "/scripts/discover-external-endpoint.sh"]
securityContext:
{{- toYaml $.Values.containerSecurityContext | nindent 12 }}
env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: EXTERNAL_SERVICE_NAME
value: {{ $externalServiceName | quote }}
resources:
{{- toYaml $.Values.externalAccess.endpointDiscovery.resources | nindent 12 }}
volumeMounts:
- name: scripts
mountPath: /scripts
- name: external-endpoint
mountPath: /external-endpoint
- name: kube-api-access
mountPath: /var/run/secrets/kubernetes.io/serviceaccount
readOnly: true
{{- end }}
containers:
- name: redis
image: "{{ $.Values.image.repository }}:{{ $.Values.image.tag }}"
imagePullPolicy: {{ $.Values.image.pullPolicy }}
command: ["/bin/sh", "/scripts/start-redis.sh"]
securityContext:
{{- toYaml $.Values.containerSecurityContext | nindent 12 }}
env:
{{- include "redis-cluster.commonEnv" $root | nindent 12 }}
- name: COORDINATOR_HOST
value: {{ $coordinator | quote }}
- name: ALL_NODE_HOSTS
value: {{ join "," $allHosts | quote }}
- name: PRIMARY_HOSTS
value: {{ join "," $primaryHosts | quote }}
- name: REPLICA_ASSIGNMENTS
value: {{ join "," $assignments | quote }}
- name: NODE_ADDRESS_ASSIGNMENTS
value: {{ join "," $addressAssignments | quote }}
- name: ANNOUNCE_HOSTNAME
value: {{ $announceHostname | quote }}
- name: ANNOUNCE_PORT
value: {{ ternary $.Values.externalAccess.port $.Values.redis.port $.Values.externalAccess.enabled | quote }}
- name: EXTERNAL_ACCESS_ENABLED
value: {{ $.Values.externalAccess.enabled | quote }}
ports:
- name: tcp-redis
containerPort: {{ $.Values.redis.port }}
- name: tcp-cluster
containerPort: {{ $.Values.cluster.busPort }}
startupProbe:
exec:
command: ["/bin/sh", "/scripts/startup-redis.sh"]
initialDelaySeconds: {{ $.Values.probes.redis.startup.initialDelaySeconds }}
periodSeconds: {{ $.Values.probes.redis.startup.periodSeconds }}
timeoutSeconds: {{ $.Values.probes.redis.startup.timeoutSeconds }}
failureThreshold: {{ $.Values.probes.redis.startup.failureThreshold }}
livenessProbe:
exec:
command: ["/bin/sh", "/scripts/liveness-redis.sh"]
periodSeconds: {{ $.Values.probes.redis.liveness.periodSeconds }}
timeoutSeconds: {{ $.Values.probes.redis.liveness.timeoutSeconds }}
failureThreshold: {{ $.Values.probes.redis.liveness.failureThreshold }}
readinessProbe:
exec:
command: ["/bin/sh", "/scripts/readiness-redis.sh"]
periodSeconds: {{ $.Values.probes.redis.readiness.periodSeconds }}
timeoutSeconds: {{ $.Values.probes.redis.readiness.timeoutSeconds }}
failureThreshold: {{ $.Values.probes.redis.readiness.failureThreshold }}
lifecycle:
preStop:
exec:
command: ["/bin/sh", "/scripts/prestop-redis.sh"]
resources:
{{- toYaml $.Values.redis.resources | nindent 12 }}
volumeMounts:
- name: data
mountPath: /data
- name: scripts
mountPath: /scripts
- name: config
mountPath: /etc/redis-ro
- name: redis-runtime
mountPath: /etc/redis-runtime
{{- if $.Values.externalAccess.enabled }}
- name: external-endpoint
mountPath: /external-endpoint
readOnly: true
{{- end }}
{{- if $.Values.metrics.enabled }}
- name: metrics
image: "{{ $.Values.metrics.image.repository }}:{{ $.Values.metrics.image.tag }}"
imagePullPolicy: {{ $.Values.metrics.image.pullPolicy }}
securityContext:
{{- toYaml $.Values.containerSecurityContext | nindent 12 }}
env:
- name: REDIS_ADDR
value: "redis://localhost:{{ $.Values.redis.port }}"
{{- if $.Values.auth.enabled }}
- name: REDIS_PASSWORD
valueFrom:
secretKeyRef:
name: {{ include "redis-cluster.secretName" $root }}
key: {{ include "redis-cluster.secretKey" $root }}
{{- end }}
ports:
- name: http-metrics
containerPort: {{ $.Values.metrics.port }}
livenessProbe:
tcpSocket:
port: http-metrics
initialDelaySeconds: 10
periodSeconds: 15
readinessProbe:
tcpSocket:
port: http-metrics
periodSeconds: 15
resources:
{{- toYaml $.Values.metrics.resources | nindent 12 }}
{{- end }}
volumes:
- name: scripts
configMap:
name: {{ $fullname }}
defaultMode: 0555
- name: config
configMap:
name: {{ $fullname }}
items:
- key: redis.conf
path: redis.conf
- name: redis-runtime
emptyDir: {}
{{- if $.Values.externalAccess.enabled }}
- name: external-endpoint
emptyDir: {}
- name: kube-api-access
projected:
defaultMode: 0444
sources:
- serviceAccountToken:
path: token
expirationSeconds: 3600
- configMap:
name: kube-root-ca.crt
items:
- key: ca.crt
path: ca.crt
{{- end }}
{{- if not $.Values.persistence.enabled }}
- name: data
emptyDir: {}
{{- end }}
{{- if $.Values.persistence.enabled }}
volumeClaimTemplates:
- metadata:
name: data
{{- with $.Values.persistence.annotations }}
annotations:
{{- toYaml . | nindent 10 }}
{{- end }}
spec:
accessModes:
{{- toYaml $.Values.persistence.accessModes | nindent 10 }}
{{- with $.Values.persistence.storageClass }}
storageClassName: {{ . | quote }}
{{- end }}
resources:
requests:
storage: {{ $.Values.persistence.size }}
{{- end }}
{{- end }}
{{- end }}