K8S部署Redis高可用全攻略:1主2从3哨兵架构实战
一、创建Redis
1.编写YAML文件
yaml
---
# Redis 无头服务(Headless Service)
# 用于 StatefulSet 内部稳定域名解析:redis-0.redis、redis-1.redis
apiVersion: v1
kind: Service
metadata:
name: redis
spec:
clusterIP: None
ports:
- port: 6379
name: redis
selector:
app: redis
---
# Redis 有状态集群(3 节点:自动主从 + 自动故障切换)
# 启动逻辑:从哨兵获取主节点信息,自动成为主/从
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
spec:
serviceName: redis
replicas: 3
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:7-alpine
command: ["/bin/sh", "-c"]
args:
- |
# 等待集群网络与哨兵启动完成
sleep 10
# 从哨兵查询当前集群的主库 IP
MASTER_IP=$(redis-cli -h sentinel-0.sentinel.default.svc.cluster.local -p 26379 --raw sentinel get-master-addr-by-name mymaster 2>/dev/null | head -n1)
# 获取当前 Pod 的 IP
MY_IP=$(hostname -i)
# 如果本机是哨兵指定的主节点,则以主节点启动
if [ "$MASTER_IP" = "$MY_IP" ]; then
redis-server --appendonly yes --protected-mode no
# 否则,自动成为从节点,连接主节点同步数据
else
redis-server --appendonly yes --protected-mode no --replicaof $MASTER_IP 6379
fi
ports:
- containerPort: 6379
# 数据持久化存储(自动创建 PVC)
volumeClaimTemplates:
- metadata:
name: redis-data
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 1Gi
---
# 哨兵无头服务
# 用于哨兵之间通信,以及 Redis 连接哨兵
apiVersion: v1
kind: Service
metadata:
name: sentinel
spec:
clusterIP: None
ports:
- port: 26379
selector:
app: sentinel
---
# Redis 哨兵集群(3 节点:监控 + 故障自动转移)
# 作用:监控主库,主库宕机自动选举新主
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: sentinel
spec:
serviceName: sentinel
replicas: 3
selector:
matchLabels:
app: sentinel
template:
metadata:
labels:
app: sentinel
spec:
containers:
- name: sentinel
image: redis:7-alpine
command: ["/bin/sh", "-c"]
args:
- |
# 等待 Redis 主节点启动
sleep 10
# 解析初始主节点域名获取 IP
MASTER_IP=$(getent hosts redis-0.redis | awk '{print $1}')
# 生成哨兵配置文件
cat >/tmp/sentinel.conf <<EOF
port 26379
protected-mode no
# 监控名为 mymaster 的集群,2 个哨兵确认即触发切换
sentinel monitor mymaster $MASTER_IP 6379 2
# 主节点 5 秒无响应则标记为下线
sentinel down-after-milliseconds mymaster 5000
# 故障转移超时时间
sentinel failover-timeout mymaster 10000
# 并行同步的从节点数量
sentinel parallel-syncs mymaster 1
EOF
# 启动哨兵进程
redis-sentinel /tmp/sentinel.conf
2.创建资源
bash
kubectl delete -f redis.yaml
3.验证是否创建成功
bash
kubectl get pods,svc,pvc | grep -E 'redis|sentinel'

-
可以看到Pod名字是固定名字,不是随机字符串!
-
PVC 状态处于Bound状态
-
Service 的ClusterIP 为 Node
二、验证Redis主从(1主2从)
1.看主节点redis-0 角色
bash
kubectl exec -it redis-0 -- redis-cli info replication

状态显示 : 主节点正确,有 2 个从机
2.看从节点redis-1和redis-2角色
bash
kubectl exec -it redis-1 -- redis-cli info replication
kubectl exec -it redis-2 -- redis-cli info replication

三、验证哨兵模式(3哨兵)
1.看哨兵是否监控到主库
bash
kubectl exec -it sentinel-0 -- redis-cli -p 26379 info sentinel

状态显示:哨兵监控成功!
2.看哨兵是否发现 2 个从节点
bash
kubectl exec -it sentinel-0 -- redis-cli -p 26379 SENTINEL slaves mymaster
会出现 2 个从节点信息, 哨兵识别主从成功!

四、测试
1.测试数据同步
往主库(redis-0)写数据,去从库(redis-1、redis-2)读数据
bash
#主库执行
kubectl exec -it redis-0 -- redis-cli set mytest "hello"
#从库redis-1执行
kubectl exec -it redis-1 -- redis-cli get mytest
#从库redis-2执行
kubectl exec -it redis-2 -- redis-cli get mytest
能读到 hello = 主从同步 100% 正常

2.测试自动故障转移(高可用)
①删除主库redis-0
直接把主库干掉,看哨兵是否自动选新主
bash
kubectl delete pod redis-0
②看哨兵日志
kubectl logs sentinel-0
你会看到:+switch-master mymaster ...

③进入从库查看信息,确认新主库
bash
kubectl exec redis-1 -- redis-cli info replication
kubectl exec redis-2 -- redis-cli info replication
可以看到从库redis-1升级成了主库

④旧主库(redis-0)重建恢复后自动归队,变成新从库
redis-0 会被 K8s 自动重建,自动加入集群作为从库:
bash
kubectl exec -it redis-0 -- redis-cli info replication

⑤验证集群状态与数据一致性
bash
# 在新主库写入测试数据
kubectl exec -it redis-1 -- redis-cli set test "good"
# 在所有节点读取数据,确认同步正常
kubectl exec -it redis-0 -- redis-cli get test
kubectl exec -it redis-2 -- redis-cli get test
✅ 预期结果:所有节点都能读到相同的数据,说明主从同步正常。

五、进阶配置
1.配置Redis密码
①给 Redis + Sentinel 设置密码
生产环境必须用:K8s Secret + 环境变量 挂载使用,这里我们创建一个redis-secret,密码是Redis@2026
bash
kubectl create secret generic redis-secret \
--from-literal=REDIS_PASSWORD=Redis@2026
②验证 Redis 密码是否正确
bash
kubectl exec redis-0 -- redis-cli -a Redis@2026 info replication
出现主从信息=密码正确!

③JAVA应用配置Redis哨兵+密码
- 修改 application.properties
properties
# 从环境变量读取 Redis 密码
spring.redis.password=${SPRING_REDIS_PASSWORD:Redis@2026}
spring.redis.sentinel.master=${SPRING_REDIS_SENTINEL_MASTER:mymaster}
spring.redis.sentinel.nodes=${SPRING_REDIS_SENTINEL_NODES:localhost:26379}
- 修改JAVA K8s Deployment yaml
加入 Redis 密码环境变量 ,从 Secret 读取!

④提交代码等待K8S滚动更新

⑤验证java应用有没有读取到密码
bash
# 进入java容器
kubectl exec -it my-java-app-5597696bcb-5xs7p -- env | grep SPRING_REDIS
#预计返回以下内容
SPRING_REDIS_SENTINEL_NODES=sentinel-0.sentinel.default.svc.cluster.local:26379,sentinel-1.sentinel.default.svc.cluster.local:26379,sentinel-2.sentinel.default.svc.cluster.local:26379
SPRING_REDIS_PASSWORD=Redis@2026
SPRING_REDIS_SENTINEL_MASTER=mymaster
说明:密码、哨兵地址全部注入成功
2.配置Redis 三大生产加固
-
内存上限 + 内存淘汰策略(防 OOM、防内存爆满)
-
关闭高危命令(
FLUSHALL / FLUSHDB禁止执行) -
优化持久化 RDB+AOF 混合(数据更安全)
①我们直接修改 redis.yaml 部署文件的 redis 容器 args 部分,完整代码示例:
yaml
# --- Redis 无头服务 ---
apiVersion: v1
kind: Service
metadata:
name: redis
spec:
clusterIP: None
ports:
- port: 6379
name: redis
selector:
app: redis
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
spec:
serviceName: redis
replicas: 3
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:7-alpine
env:
- name: REDIS_PASSWORD
valueFrom:
secretKeyRef:
name: redis-secret
key: REDIS_PASSWORD
command: ["/bin/sh", "-c"]
args:
- |
sleep 15
MASTER_IP=$(redis-cli -h sentinel-0.sentinel.default.svc.cluster.local -p 26379 -a $REDIS_PASSWORD --raw sentinel get-master-addr-by-name mymaster 2>/dev/null | head -n1)
MY_IP=$(hostname -i)
if [ -z "$MASTER_IP" ]; then
if [ "$(hostname)" = "redis-0" ]; then
redis-server --appendonly yes --protected-mode no --requirepass $REDIS_PASSWORD --masterauth $REDIS_PASSWORD --maxmemory 512mb --maxmemory-policy allkeys-lru
exit 0
else
redis-server --appendonly yes --protected-mode no --requirepass $REDIS_PASSWORD --masterauth $REDIS_PASSWORD --maxmemory 512mb --maxmemory-policy allkeys-lru --replicaof redis-0.redis 6379
exit 0
fi
fi
if [ "$MASTER_IP" = "$MY_IP" ]; then
redis-server \
--appendonly yes \
--protected-mode no \
--requirepass $REDIS_PASSWORD \
--masterauth $REDIS_PASSWORD \
--maxmemory 512mb \
--maxmemory-policy allkeys-lru
else
redis-server \
--appendonly yes \
--protected-mode no \
--requirepass $REDIS_PASSWORD \
--masterauth $REDIS_PASSWORD \
--maxmemory 512mb \
--maxmemory-policy allkeys-lru \
--replicaof $MASTER_IP 6379
fi
ports:
- containerPort: 6379
volumeClaimTemplates:
- metadata:
name: redis-data
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 1Gi
---
# --- 哨兵---
apiVersion: v1
kind: Service
metadata:
name: sentinel
spec:
clusterIP: None
ports:
- port: 26379
selector:
app: sentinel
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: sentinel
spec:
serviceName: sentinel
replicas: 3
selector:
matchLabels:
app: sentinel
template:
metadata:
labels:
app: sentinel
spec:
containers:
- name: sentinel
image: redis:7-alpine
env:
- name: REDIS_PASSWORD
valueFrom:
secretKeyRef:
name: redis-secret
key: REDIS_PASSWORD
command: ["/bin/sh", "-c"]
args:
- |
sleep 15
MASTER_IP=$(getent hosts redis-0.redis | awk '{print $1}')
cat >/tmp/sentinel.conf <<EOF
port 26379
protected-mode no
sentinel monitor mymaster $MASTER_IP 6379 2
sentinel auth-pass mymaster $REDIS_PASSWORD
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 10000
sentinel parallel-syncs mymaster 1
EOF
redis-sentinel /tmp/sentinel.conf
ports:
- containerPort: 26379
②应用配置
bash
kubectl apply -f redis.yaml
③验证配置生效
bash
#验证内存限制
kubectl exec redis-2 -- redis-cli -a Redis@2026 config get maxmemory
#验证淘汰策略
kubectl exec redis-2 -- redis-cli -a Redis@2026 config get maxmemory-policy
#验证AOF持久化
kubectl exec redis-2 -- redis-cli -a Redis@2026 config get appendonly
#验证密码生效
kubectl exec redis-2 -- redis-cli -a Redis@2026 config get requirepass

以上则完成了K8s 生产级 Redis 哨兵高可用全套方案
✅主从同步 ✅自动故障转移 ✅重启自愈 ✅数据持久化 ✅安全密码 ✅稳定无崩溃