You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Kubernetes中部署无持久化Redis Cluster并实现Pod重启自动恢复

解决方案:基于StatefulSet+Headless Service+Redis 6+主机名公告的无持久化Redis Cluster

要解决Pod重启IP变化导致集群失效的问题,核心是用稳定的DNS名称代替IP来管理Redis节点,结合StatefulSet的固定DNS特性和Redis 6+的主机名公告功能,就能实现Pod重启后的自动恢复,完美适配EmptyDir无持久化的需求。

关键原理

  1. Headless Service:为StatefulSet的每个Pod提供固定的DNS记录(比如redis-cluster-0.redis-headless.svc.cluster.local),不管Pod重启多少次,DNS名称始终指向当前Pod的新IP。
  2. Redis 6+的cluster-announce-hostname:让Redis节点在集群中公告自己的主机名而非IP,其他节点通过主机名通信,IP变化完全不影响集群拓扑。
  3. 智能启动脚本:处理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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 07:52:05