417 lines
14 KiB
YAML
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 }}
|