在Kubernetes中部署无持久化Redis Cluster并实现Pod重启自动恢复
解决方案:基于StatefulSet+Headless Service+Redis 6+主机名公告的无持久化Redis Cluster
要解决Pod重启IP变化导致集群失效的问题,核心是用稳定的DNS名称代替IP来管理Redis节点,结合StatefulSet的固定DNS特性和Redis 6+的主机名公告功能,就能实现Pod重启后的自动恢复,完美适配EmptyDir无持久化的需求。
关键原理
- Headless Service:为StatefulSet的每个Pod提供固定的DNS记录(比如
redis-cluster-0.redis-headless.svc.cluster.local),不管Pod重启多少次,DNS名称始终指向当前Pod的新IP。 - Redis 6+的
cluster-announce-hostname:让Redis节点在集群中公告自己的主机名而非IP,其他节点通过主机名通信,IP变化完全不影响集群拓扑。 - 智能启动脚本:处理Pod启动时的集群初始化、旧节点清理和新节点加入逻辑,适配EmptyDir重启数据丢失的特性。
具体实现步骤
1. 创建Headless Service
这个服务专门为StatefulSet Pod提供稳定的DNS解析:
apiVersion: v1 kind: Service metadata: name: redis-headless spec: clusterIP: None selector: app: redis-cluster ports: - name: redis port: 6379 targetPort: 6379 - name: cluster-bus port: 16379 targetPort: 16379
2. 编写初始化/启动脚本
创建一个ConfigMap存储启动脚本,处理集群初始化、节点加入和旧节点清理:
apiVersion: v1 kind: ConfigMap metadata: name: redis-cluster-scripts data: start-redis.sh: | #!/bin/bash set -eo pipefail # 获取当前Pod的DNS名称 POD_NAME=$(hostname) DNS_NAME="${POD_NAME}.redis-headless.svc.cluster.local" CLUSTER_INIT_NODE="redis-cluster-0.redis-headless.svc.cluster.local:6379" # 启动Redis服务(使用主机名公告,核心配置) redis-server --port 6379 \ --cluster-enabled yes \ --cluster-config-file /data/nodes.conf \ --cluster-node-timeout 5000 \ --appendonly no \ --protected-mode no \ --cluster-announce-hostname "${DNS_NAME}" \ --cluster-announce-port 6379 \ --cluster-announce-bus-port 16379 & # 等待Redis启动完成 sleep 5 until redis-cli -h "${DNS_NAME}" ping; do echo "Waiting for Redis to start..." sleep 2 done # 处理集群逻辑 if [ "${POD_NAME}" = "redis-cluster-0" ]; then # 第一个节点:初始化3主3从的集群拓扑 echo "Initializing Redis Cluster..." redis-cli --cluster create \ redis-cluster-0.redis-headless.svc.cluster.local:6379 \ redis-cluster-1.redis-headless.svc.cluster.local:6379 \ redis-cluster-2.redis-headless.svc.cluster.local:6379 \ redis-cluster-3.redis-headless.svc.cluster.local:6379 \ redis-cluster-4.redis-headless.svc.cluster.local:6379 \ redis-cluster-5.redis-headless.svc.cluster.local:6379 \ --cluster-replicas 1 --cluster-yes else # 非第一个节点:先清理集群中绑定当前DNS的旧节点(Pod重启后旧节点已下线) OLD_NODE_ID=$(redis-cli -h "${CLUSTER_INIT_NODE}" cluster nodes | grep "${DNS_NAME}" | awk '{print $1}') if [ -n "${OLD_NODE_ID}" ]; then echo "Removing old node ${OLD_NODE_ID} from cluster..." redis-cli --cluster del-node "${CLUSTER_INIT_NODE}" "${OLD_NODE_ID}" sleep 3 fi # 加入新节点到集群 echo "Adding ${DNS_NAME} to cluster..." redis-cli --cluster add-node "${DNS_NAME}:6379" "${CLUSTER_INIT_NODE}" # 自动将3-5号Pod设置为0-2号主节点的从节点(可选,按需调整) if [[ "${POD_NAME}" =~ "-3" || "${POD_NAME}" =~ "-4" || "${POD_NAME}" =~ "-5" ]]; then echo "Setting ${DNS_NAME} as replica..." MASTER_INDEX=$(( ${POD_NAME##*-} - 3 )) MASTER_DNS="redis-cluster-${MASTER_INDEX}.redis-headless.svc.cluster.local" MASTER_ID=$(redis-cli -h "${CLUSTER_INIT_NODE}" cluster nodes | grep "${MASTER_DNS}" | grep "master" | awk '{print $1}') redis-cli --cluster replicate "${CLUSTER_INIT_NODE}" "${MASTER_ID}" "${DNS_NAME}:6379" fi fi # 保持进程运行,防止Pod退出 wait
3. 创建StatefulSet
使用EmptyDir作为存储卷,挂载脚本和Redis临时数据目录:
apiVersion: apps/v1 kind: StatefulSet metadata: name: redis-cluster spec: serviceName: redis-headless replicas: 6 podManagementPolicy: Parallel # 并行启动Pod,加快集群初始化速度 selector: matchLabels: app: redis-cluster template: metadata: labels: app: redis-cluster spec: containers: - name: redis image: redis:7-alpine # 必须用Redis 6+版本 command: ["/scripts/start-redis.sh"] ports: - containerPort: 6379 name: redis - containerPort: 16379 name: cluster-bus volumeMounts: - name: scripts mountPath: /scripts - name: data mountPath: /data volumes: - name: scripts configMap: name: redis-cluster-scripts defaultMode: 0755 # 给脚本执行权限 - name: data emptyDir: {} # 无持久化存储,Pod重启数据丢失
关键注意事项
- Redis版本要求:必须使用Redis 6或更高版本,因为
cluster-announce-hostname是Redis 6才引入的功能。 - 场景限制:EmptyDir的数据在Pod删除或节点故障时会彻底丢失,所以这种方案仅适合对数据持久性要求低的场景(比如缓存层、临时数据存储)。
- 重启恢复逻辑:Pod重启后,脚本会自动清理集群中绑定该DNS的旧下线节点,然后以新节点身份重新加入集群,Redis Cluster会自动重新平衡槽位和副本关系。
内容的提问来源于stack exchange,提问作者Irshad K
相关产品推荐
相关产品推荐

