写在最前
优先参考使用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:63792.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
# 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
评论