写在最前

优先参考使用calico注解能力来实现,使用 K8S 或 Docker 快速部署 redis6, calico固定IP 集群,如果当前系统网络插件不适用则可以继续往下参考。

在 Kubernetes 中部署 Redis 6 的 3 主 3 从的分片集群,真正的难点是 Redis Cluster 强依赖节点的固定 IP。Redis 在初始化时会把每个节点的 IP 写进集群元数据,一旦 IP 变化,主从关系就会失效,集群直接崩坏。而 Kubernetes 的 Pod IP 天然是不稳定的,即便使用 StatefulSet 与稳定名称,只要发生调度漂移,Redis 就会出现“IP 属于新节点,数据却来自旧节点 PVC”的矛盾,导致集群结构损坏。因此,StatefulSet + PVC 并不能解决问题,反而会制造混乱。

要让 Redis Cluster 在 K8s 中稳定运行,必须把 Redis 的“身份”与宿主机绑定,而不是与 Pod 绑定。采用 hostNetwork,让 Redis 使用宿主机 IP;再使用 hostPath,把 Redis 数据目录存放在节点本地磁盘,使数据天然跟随节点。这种方式让 Redis 不再依赖 Pod,而是依赖节点本身:在哪个节点启动,就读取哪个节点的数据,其对外 IP 永远不变,从而不会破坏集群元数据。

本质上,Redis 变成了“节点级服务”,不是“容器级服务”。容器只是一个壳,真正关键的是节点的 IP 和节点的数据。我们让 Redis 成为“运行于宿主机,由 Kubernetes 启动的服务进程”,而不再是“会漂移的容器”。Kubernetes 不提供漂移高可用,Redis 的高可用仍由自身的 Cluster 机制保证。只要不是同一主从节点服务器同时宕机,Redis 就能自动选主继续工作。

通过给节点打上 redis-cluster 标签,可以确保这些 Redis 实例始终固定部署在特定节点上。最终得到的是一个“在 Kubernetes 中托管,但其行为更像传统节点级部署”的 Redis Cluster。它牺牲了 Kubernetes 的弹性与漂移,但换来 Redis Cluster 所需要的 IP 稳定性与数据一致性。

1. docker 部署

2. kubernetes 部署

2.1 configmap

kind: ConfigMap
apiVersion: v1
metadata:
  name: redis-config
  namespace: default
  annotations:
    kubesphere.io/creator: admin
data:
  redis.conf: |-
    port 6379
    bind 0.0.0.0
    # 允许作为集群节点
    cluster-enabled yes
    # 节点的 cluster 配置文件(会自动创建)
    cluster-config-file nodes.conf
    # 超时时间
    cluster-node-timeout 5000
    # 开启 AOF
    appendonly yes
 
    # 自行变更,不可使用弱密码
    requirepass 123456
    masterauth 123456

2.2 deployment

kind: Deployment
apiVersion: apps/v1
metadata:
  name: redis-cluster
  namespace: default
  annotations:
    deployment.kubernetes.io/revision: '3'
    kubesphere.io/creator: admin
spec:
  replicas: 6
  selector:
    matchLabels:
      app: redis-cluster
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: redis-cluster
      annotations:
        kubesphere.io/creator: admin
        kubesphere.io/imagepullsecrets: '{}'
        kubesphere.io/restartedAt: '2025-11-27T08:06:05.331Z'
    spec:
      volumes:
        - name: redis-config
          configMap:
            name: redis-config
            defaultMode: 420
        - name: redis-data
          hostPath:
            path: /data/redis-cluster
            type: DirectoryOrCreate
      containers:
        - name: redis
          image: 'redis:6.2.19'
          command:
            - redis-server
          args:
            - /etc/redis/redis.conf
          ports:
            - name: http-0
              containerPort: 6379
              protocol: TCP
          resources: {}
          volumeMounts:
            - name: redis-config
              mountPath: /etc/redis/redis.conf
              subPath: redis.conf
            - name: redis-data
              mountPath: /data
          terminationMessagePath: /dev/termination-log
          terminationMessagePolicy: File
          imagePullPolicy: IfNotPresent
      restartPolicy: Always
      terminationGracePeriodSeconds: 30
      dnsPolicy: ClusterFirstWithHostNet
      nodeSelector:
        redis-cluster: 'true'
      hostNetwork: true
      securityContext: {}
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: app
                    operator: In
                    values:
                      - redis-cluster
              topologyKey: kubernetes.io/hostname
      schedulerName: default-scheduler
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 25%
      maxSurge: 25%
  revisionHistoryLimit: 10
  progressDeadlineSeconds: 600

2.3 初始化集群

自行填写主机ip与redis密码

redis-cli -a xxxxxxxxxx \
--cluster create \
--cluster-replicas 1 \
172.31.0.11:6379 \
172.31.0.12:6379 \
172.31.0.13:6379  \
172.31.0.14:6379 \
172.31.0.15:6379 \
172.31.0.16:6379

2.4 自定义coredns

因为如果用StatefulSet的集群内域名,它实际会出现不一致,例如当前容器运行状态如下,但是将副本调整为0再调整为6,他的IP就彻底变了

172.31.0.11 > redis-0

172.31.0.12 > redis-1

172.31.0.13 > redis-2

172.31.0.14 > redis-3

172.31.0.15 > redis-4

172.31.0.16 > redis-5


这种情况你用集群内域名重启后程序就会解析到其他的节点IP它就不是原始的IP了,本来redis-0解析到172.31.0.11的,但是重启后这个容器就跑到了172.31.0.14,这个节点上的hostpath数据可是redis-3的,可能会有意料之外的问题,所以我们需要自定义域名解析到节点IP而不使用StatefulSet的集群内域名,你可以看到我上面使用的是Deployment连service我都不创建,容器名也不敏感

172.31.0.11 > redis-1

172.31.0.12 > redis-3

172.31.0.13 > redis-2

172.31.0.14 > redis-0

172.31.0.15 > redis-5

172.31.0.16 > redis-4

Kubernetes 之自定义 CoreDNS 解析

# redis集群
172.31.0.11 app-redis1.default.svc.cluster.local
172.31.0.12 app-redis2.default.svc.cluster.local
172.31.0.13 app-redis3.default.svc.cluster.local
172.31.0.14 app-redis4.default.svc.cluster.local
172.31.0.15 app-redis5.default.svc.cluster.local
172.31.0.16 app-redis6.default.svc.cluster.local

写在最后