Files
SODEW/sodew-bash-main/assets/k3s/templates/redis.yaml
T
2026-08-18 09:40:08 +02:00

417 lines
14 KiB
YAML

{{- if .Values.redisHelm }}
---
apiVersion: v1
kind: ConfigMap
metadata: { name: "{{ .Values.redis }}-config", namespace: "{{ .Values.namespace }}" }
data:
redis.conf: |
bind 0.0.0.0
port 6379
dir /data
# --- ACL ---
aclfile /data-acl/{{ .Values.redisFileUsersAcl }}
# --- all in one DB ---
databases 1
# --- network ---
protected-mode yes
tcp-backlog 1024
tcp-keepalive 300
timeout 0
# --- connections ---
maxclients {{ .Values.profiles.redis.maxClients }}
# --- memory (tune) ---
maxmemory {{ .Values.profiles.redis.maxmemory }}
maxmemory-policy allkeys-lfu
maxmemory-samples 5
maxmemory-eviction-tenacity 10
maxmemory-clients 5%
client-query-buffer-limit 256mb
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
# --- replication ---
replica-serve-stale-data yes
replica-read-only yes
repl-backlog-size 64mb
repl-backlog-ttl 3600
min-replicas-to-write 0
#min-replicas-max-lag 10
# --- persistence ---
appendonly yes
appendfsync everysec
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync yes
save ""
# --- log ---
slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
---
apiVersion: v1
kind: PersistentVolume
metadata: { name: "{{ .Values.redis }}-users-pv" }
spec:
capacity: { storage: 1Gi }
volumeMode: Filesystem
accessModes: [ ReadWriteMany ]
persistentVolumeReclaimPolicy: Retain
hostPath: { path: "{{ .Values.redisPath }}", type: DirectoryOrCreate }
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata: { name: "{{ .Values.redis }}-users-pvc", namespace: "{{ .Values.namespace }}" }
spec:
volumeName: "{{ .Values.redis }}-users-pv"
volumeMode: Filesystem
accessModes: [ ReadWriteMany ]
resources: { requests: { storage: 1Gi } }
storageClassName: ""
---
apiVersion: apps/v1
kind: StatefulSet
metadata: { name: "{{ .Values.redis }}", namespace: "{{ .Values.namespace }}" }
spec:
serviceName: "{{ .Values.redis }}"
replicas: 2
selector: { matchLabels: { app: "{{ .Values.redis }}" } }
persistentVolumeClaimRetentionPolicy: { whenDeleted: Delete, whenScaled: Retain }
template:
metadata: { labels: { app: "{{ .Values.redis }}" } }
spec:
terminationGracePeriodSeconds: 30
initContainers:
- name: init-redis-acl
image: redis:8.6-alpine
resources:
requests: { cpu: "10m", memory: "32Mi" }
limits: { cpu: "100m", memory: "128Mi" }
env:
- { name: POD_NAME, valueFrom: { fieldRef: { fieldPath: metadata.name } } }
- { name: REDIS_PASSWORD, valueFrom: { secretKeyRef: { name: "{{ .Values.redis }}-secret", key: root-password } } }
command: ["/bin/sh","-c"]
args:
- |
set -eu
ACL="/data-acl/{{ .Values.redisFileUsersAcl }}"
HASH=$(printf '%s' "$REDIS_PASSWORD" | sha256sum | awk '{print $1}')
mkdir -p /data-acl
if [ "$POD_NAME" = "{{ .Values.redis }}-0" ]; then
tmp="${ACL}.tmp"
if [ -f "$ACL" ]; then
awk '!(tolower($1)=="user" && $2=="default")' "$ACL" > "$tmp"
else
: > "$tmp"
fi
# default user is used by replication auth and HAProxy health checks
printf 'user default on sanitize-payload #%s ~* &* +@all\n' "$HASH" >> "$tmp"
mv "$tmp" "$ACL"
chmod 600 "$ACL"
else
i=0
while [ ! -f "$ACL" ] && [ $i -lt 300 ]; do
sleep 1
i=$((i+1))
done
[ -f "$ACL" ] || exit 1
fi
volumeMounts:
- { name: "{{ .Values.redis }}-users-volume", mountPath: /data-acl }
containers:
- name: "{{ .Values.redis }}"
image: redis:8.6-alpine
resources:
requests: { cpu: "{{ .Values.profiles.redis.cpuRequest }}", memory: "{{ .Values.profiles.redis.memoryRequest }}" }
limits: { cpu: "{{ .Values.profiles.redis.cpuLimit }}", memory: "{{ .Values.profiles.redis.memoryLimit }}" }
ports:
- { name: redis, containerPort: 6379 }
env:
- { name: POD_NAME, valueFrom: { fieldRef: { fieldPath: metadata.name } } }
- { name: POD_IP, valueFrom: { fieldRef: { fieldPath: status.podIP } } }
- { name: REDIS_PASSWORD, valueFrom: { secretKeyRef: { name: "{{ .Values.redis }}-secret", key: root-password } } }
- { name: SENTINEL_HOST, value: "{{ .Values.redisSentinel }}" }
- { name: SENTINEL_PORT, value: "26379" }
- { name: MASTER_NAME, value: "mymaster" }
command: ["/bin/sh","-c"]
args:
- |
set -eu
POD_DNS_SHORT="${POD_NAME}.{{ .Values.redis }}"
POD_DNS_FQDN="${POD_NAME}.{{ .Values.redis }}.{{ .Values.namespace }}.svc.cluster.local"
get_master() {
REDISCLI_AUTH="$REDIS_PASSWORD" \
redis-cli -h "$SENTINEL_HOST" -p "$SENTINEL_PORT" \
SENTINEL get-master-addr-by-name "$MASTER_NAME" 2>/dev/null | tr -d '\r'
}
out=""
i=0
while [ $i -lt 20 ]; do
out="$(get_master || true)"
[ -n "$out" ] && break
i=$((i+1))
sleep 1
done
master_host="$(printf '%s\n' "$out" | sed -n '1p')"
master_port="$(printf '%s\n' "$out" | sed -n '2p')"
[ -n "$master_port" ] || master_port="6379"
is_me_master() {
[ "$master_host" = "$POD_IP" ] || [ "$master_host" = "$POD_DNS_SHORT" ] || [ "$master_host" = "$POD_DNS_FQDN" ]
}
if [ -n "$master_host" ]; then
if is_me_master; then
exec redis-server /etc/redis/redis.conf --masteruser default --masterauth "$REDIS_PASSWORD"
else
exec redis-server /etc/redis/redis.conf --replicaof "$master_host" "$master_port" --masteruser default --masterauth "$REDIS_PASSWORD"
fi
else
# bootstrap if sentinel is not ready yet
if [ "$POD_NAME" = "{{ .Values.redis }}-0" ]; then
exec redis-server /etc/redis/redis.conf
else
exec redis-server /etc/redis/redis.conf --replicaof {{ .Values.redis }}-0.{{ .Values.redis }} 6379 --masteruser default --masterauth "$REDIS_PASSWORD"
fi
fi
volumeMounts:
- { name: "{{ .Values.redis }}-data-volume", mountPath: /data }
- { name: "{{ .Values.redis }}-config-volume", mountPath: /etc/redis }
- { name: "{{ .Values.redis }}-users-volume", mountPath: /data-acl }
volumes:
- name: "{{ .Values.redis }}-config-volume"
configMap: { name: "{{ .Values.redis }}-config" }
- name: "{{ .Values.redis }}-users-volume"
persistentVolumeClaim: { claimName: "{{ .Values.redis }}-users-pvc" }
volumeClaimTemplates:
- metadata: { name: "{{ .Values.redis }}-data-volume" }
spec:
accessModes: [ ReadWriteOnce ]
resources: { requests: { storage: 10Gi } }
---
apiVersion: v1
kind: Service
metadata: { name: "{{ .Values.redis }}", namespace: "{{ .Values.namespace }}" }
spec:
clusterIP: None
selector: { app: "{{ .Values.redis }}" }
ports:
- { name: redis, protocol: TCP, port: 6379, targetPort: 6379 }
# -------------------------
# Sentinel (1) with PVC
# -------------------------
---
apiVersion: v1
kind: ConfigMap
metadata: { name: "{{ .Values.redisSentinel }}-config", namespace: "{{ .Values.namespace }}" }
data:
sentinel.conf.tmpl: |
bind 0.0.0.0
port 26379
dir /data
sentinel deny-scripts-reconfig yes
sentinel resolve-hostnames yes
sentinel announce-hostnames yes
# quorum=1 (single sentinel)
sentinel monitor mymaster {{ .Values.redis }}-0.{{ .Values.redis }} 6379 1
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
sentinel parallel-syncs mymaster 1
sentinel auth-user mymaster default
sentinel auth-pass mymaster __PASSWORD__
requirepass __PASSWORD__
---
apiVersion: apps/v1
kind: StatefulSet
metadata: { name: "{{ .Values.redisSentinel }}", namespace: "{{ .Values.namespace }}" }
spec:
replicas: 1
serviceName: "{{ .Values.redisSentinel }}"
selector: { matchLabels: { app: "{{ .Values.redisSentinel }}" } }
persistentVolumeClaimRetentionPolicy: { whenDeleted: Delete, whenScaled: Retain }
template:
metadata: { labels: { app: "{{ .Values.redisSentinel }}" } }
spec:
containers:
- name: "{{ .Values.redisSentinel }}"
image: redis:8.6-alpine
resources:
requests: { cpu: "50m", memory: "128Mi" }
limits: { cpu: "200m", memory: "256Mi" }
ports:
- { name: sentinel, containerPort: 26379 }
env:
- { name: REDIS_PASSWORD, valueFrom: { secretKeyRef: { name: "{{ .Values.redis }}-secret", key: root-password } } }
command: ["/bin/sh","-c"]
args:
- |
set -eu
CONF=/data/sentinel.conf
if [ ! -f "$CONF" ]; then
pwd_escaped=$(printf '%s' "$REDIS_PASSWORD" | sed -e 's/[\/&]/\\&/g')
sed "s/__PASSWORD__/${pwd_escaped}/g" /tmpl/sentinel.conf.tmpl > "$CONF"
chmod 600 "$CONF"
fi
exec redis-server "$CONF" --sentinel
volumeMounts:
- { name: "{{ .Values.redisSentinel }}-tmpl", mountPath: /tmpl }
- { name: "{{ .Values.redisSentinel }}-data", mountPath: /data }
volumes:
- name: "{{ .Values.redisSentinel }}-tmpl"
configMap: { name: "{{ .Values.redisSentinel }}-config" }
volumeClaimTemplates:
- metadata: { name: "{{ .Values.redisSentinel }}-data" }
spec:
accessModes: [ ReadWriteOnce ]
resources: { requests: { storage: 1Gi } }
---
apiVersion: v1
kind: Service
metadata: { name: "{{ .Values.redisSentinel }}", namespace: "{{ .Values.namespace }}" }
spec:
selector: { app: "{{ .Values.redisSentinel }}" }
ports:
- { name: sentinel, port: 26379, targetPort: 26379 }
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: "{{ .Values.redisSentinel }}-allow-only-checker", namespace: "{{ .Values.namespace }}" }
spec:
podSelector: { matchLabels: { app: "{{ .Values.redisSentinel }}" } }
policyTypes:
- Ingress
ingress:
- from:
- podSelector: { matchLabels: { app: "{{ .Values.redis }}" } }
- podSelector: { matchLabels: { app: "{{ .Values.redisSentinel }}" } }
ports:
- { protocol: TCP, port: 26379 }
# -------------------------
# HAProxy: single master endpoint
# -------------------------
---
apiVersion: v1
kind: ConfigMap
metadata: { name: "{{ .Values.redis }}-haproxy-config", namespace: "{{ .Values.namespace }}" }
data:
haproxy.cfg: |
global
log stdout format raw local0
maxconn 20000
resolvers kubedns
nameserver dns1 10.43.0.10:53
accepted_payload_size 8192
resolve_retries 3
timeout resolve 1s
timeout retry 1s
hold valid 10s
hold obsolete 30s
defaults
mode tcp
log global
option tcplog
option log-health-checks
timeout connect 5s
timeout client 1m
timeout server 1m
timeout check 1s
frontend fe_redis_master
bind *:6379
default_backend be_redis_master
backend be_redis_master
mode tcp
balance first
option tcp-check
option srvtcpka
timeout queue 2s
timeout connect 2s
timeout check 3s
timeout server 10m
tcp-check connect
tcp-check send-lf "AUTH default $REDIS_PASSWORD\r\n"
tcp-check expect string +OK
tcp-check send INFO\ replication\r\n
tcp-check expect string role:master
tcp-check send QUIT\r\n
tcp-check expect string +OK
server redis0 {{ .Values.redis }}-0.{{ .Values.redis }}.{{ .Values.namespace }}.svc.cluster.local:6379 check resolvers kubedns resolve-prefer ipv4 init-addr libc,none inter 5s fall 2 rise 2
server redis1 {{ .Values.redis }}-1.{{ .Values.redis }}.{{ .Values.namespace }}.svc.cluster.local:6379 check resolvers kubedns resolve-prefer ipv4 init-addr libc,none inter 5s fall 2 rise 2
---
apiVersion: apps/v1
kind: Deployment
metadata: { name: "{{ .Values.redis }}-haproxy", namespace: "{{ .Values.namespace }}" }
spec:
replicas: 1
selector: { matchLabels: { app: "{{ .Values.redis }}-haproxy" } }
template:
metadata:
labels: { app: "{{ .Values.redis }}-haproxy" }
spec:
containers:
- name: "{{ .Values.redis }}-haproxy"
image: haproxy:2.9-alpine
resources:
requests: { cpu: "50m", memory: "64Mi" }
limits: { cpu: "500m", memory: "256Mi" }
ports:
- { name: redis, containerPort: 6379 }
env:
- { name: REDIS_PASSWORD, valueFrom: { secretKeyRef: { name: "{{ .Values.redis }}-secret", key: root-password } } }
command: ["/bin/sh","-c"]
args:
- |
set -eu
haproxy -c -f /usr/local/etc/haproxy/haproxy.cfg
exec haproxy -f /usr/local/etc/haproxy/haproxy.cfg -db
readinessProbe: { tcpSocket: { port: 6379 }, initialDelaySeconds: 1, periodSeconds: 2, failureThreshold: 3 }
livenessProbe: { tcpSocket: { port: 6379 }, initialDelaySeconds: 10, periodSeconds: 10, failureThreshold: 3 }
volumeMounts:
# - { name: cfg, mountPath: /usr/local/etc/haproxy/haproxy.cfg, subPath: haproxy.cfg }
- { name: cfg, mountPath: /usr/local/etc/haproxy }
volumes:
- name: cfg
configMap: { name: "{{ .Values.redis }}-haproxy-config" }
---
apiVersion: v1
kind: Service
metadata: { name: "{{ .Values.redis }}-master", namespace: "{{ .Values.namespace }}" }
spec:
selector: { app: "{{ .Values.redis }}-haproxy" }
ports:
- { name: redis, port: 6379, targetPort: 6379 }
{{- end }}