Kubernetes环境部署Redis集群
文章目录
- 序
- 一、Redis 版本推荐:优先选择 7.0 + 版本
- 二、K8s 环境必配参数:cluster-announce-ip
- 配置字典
- 一、ConfigMap 整体结构说明
- 二、`redis-cluster.conf`配置解析(按功能分类)
- 1. 网络与容器适配配置
- 2. Redis Cluster 核心配置
- 3. 认证与安全配置
- 4. 持久化配置(数据安全保障)
- 5. 内存管控配置
- 三、`init-redis-cluster.sh`配置解析
- Headless Service配置
- 一、两个端口的作用
- 二、Headless Service(`clusterIP: None`)的作用
- Redis Statefulsets
- 一、StatefulSet 对 Redis Cluster 的核心价值
- 二、核心配置解析(聚焦节点亲和性、健康检查、ServiceName)
- 1. `serviceName: redis-cluster`:为 Redis 节点提供“稳定网络标识”
- 2. 节点亲和性(`podAntiAffinity`):提升集群抗风险能力
- 3. 健康检查(`livenessProbe` 与 `readinessProbe`):保障节点可用性
- 如何安装?
- 集群测试
序
一、Redis 版本推荐:优先选择 7.0 + 版本
在 Kubernetes 环境部署 Redis Cluster 时,建议优先选用 Redis 7.0 及以上版本。核心原因在于版本功能对 K8s 动态网络环境的适配性差异:
Redis 7.0 之前的版本,使用redis-cli组建集群时存在局限性 —— 仅支持通过 “IP + 端口” 的形式指定节点地址,无法识别 Kubernetes 中 Pod 的名称(或 Pod 域名)。而 K8s 环境中,Pod 重启(如故障恢复、配置更新)后会分配新的 IP 地址,若集群依赖固定 IP,IP变更会直接导致节点失联、集群拓扑混乱,最终引发集群失效。
二、K8s 环境必配参数:cluster-announce-ip
无论选用哪个版本,在 Kubernetes 环境部署 Redis Cluster 时,必须额外配置cluster-announce-ip参数,明确节点的 “稳定通告地址”(通常为 Pod 域名,如$(POD_NAME).$(SERVICE_NAME))。
配置字典
kind: ConfigMap
apiVersion: v1
metadata:name: redis-cluster-cm
data:init-redis-cluster.sh: |2-#!/bin/bashset -e# 直接在脚本中声明变量REDIS_PASSWORD="ClusterRedis#20251112"SERVICE_NAME="redis-cluster"NODES=("redis-cluster-0.$SERVICE_NAME:6379""redis-cluster-1.$SERVICE_NAME:6379""redis-cluster-2.$SERVICE_NAME:6379""redis-cluster-3.$SERVICE_NAME:6379""redis-cluster-4.$SERVICE_NAME:6379""redis-cluster-5.$SERVICE_NAME:6379")echo "检查所有 Redis 节点是否就绪..."all_ready=1for node in "${NODES[@]}"; dohost=$(echo "$node" | cut -d: -f1)port=$(echo "$node" | cut -d: -f2)echo "检查节点: $host:$port"if [ "$(redis-cli -h $host -p $port -a $REDIS_PASSWORD ping 2>/dev/null)" != "PONG" ]; thenecho "节点 $node 未就绪"all_ready=0breakelseecho "节点 $node 已就绪"fidoneif [ $all_ready -ne 1 ]; thenecho "有节点未就绪,退出"exit 0fiecho "检查集群状态..."cluster_state=$(redis-cli -h ${NODES[0]%:*} -p ${NODES[0]#*:} -a $REDIS_PASSWORD cluster info 2>/dev/null | grep "cluster_state" | cut -d: -f2 | tr -d '[:space:]')if [ "$cluster_state" = "ok" ]; thenecho "集群已初始化,状态正常"exit 0fiecho "开始初始化 Redis 集群..."redis-cli -a $REDIS_PASSWORD \--cluster create \--cluster-replicas 1 \${NODES[@]} echo "集群初始化完成"redis-cluster.conf: |bind 0.0.0.0port 6379daemonize noprotected-mode nodir /datacluster-announce-bus-port 16379cluster-enabled yescluster-node-timeout 15000cluster-config-file /data/nodes.confrequirepass ClusterRedis#20251112masterauth ClusterRedis#20251112# backup settings# aof backup modesave 3600 1stop-writes-on-bgsave-error noappendonly yesappendfilename "appendonly.aof"appendfsync everysecno-appendfsync-on-rewrite noauto-aof-rewrite-percentage 100auto-aof-rewrite-min-size 64mbaof-load-truncated yesmaxmemory 16Gmaxmemory-policy allkeys-lru
一、ConfigMap 整体结构说明
该 ConfigMap 包含两类关键资源:
-
redis-cluster.conf:Redis Cluster 核心运行配置,定义集群模式、网络、持久化、内存等核心规则; -
init-redis-cluster.sh:K8s 环境下的集群初始化脚本,负责节点就绪检查、集群状态判断、集群创建,适配 K8s Pod 动态启动特性。
二、redis-cluster.conf配置解析(按功能分类)
1. 网络与容器适配配置
| 配置项 | 配置内容 | 设计原因(为何这么配) |
|---|---|---|
bind 0.0.0.0 | 允许所有 IP 访问 | K8s 中 Pod IP 为动态分配,且集群节点需跨 Pod 通信(如主从同步、故障检测),不能绑定固定 IP,否则其他节点无法访问。 |
port 6379 | 客户端通信端口 | 遵循 Redis 默认客户端端口,降低业务接入成本;同时与初始化脚本中节点地址的端口保持一致,避免端口 mismatch。 |
daemonize no | 禁止守护进程模式 | K8s 容器要求进程前台运行(若用守护进程,容器会认为 “主进程退出” 而重启),此配置确保 Redis 进程作为容器主进程持续运行。 |
protected-mode no | 关闭保护模式 | 保护模式默认拒绝 “非本地 IP 连接”,但 K8s 中节点通信依赖跨 Pod IP,且已配置密码认证(requirepass),关闭保护模式可避免连接被拦截。 |
2. Redis Cluster 核心配置
| 配置项 | 配置内容 | 设计原因 |
|---|---|---|
cluster-enabled yes | 启用集群模式 | 核心开关,若为no则 Redis 仅为单机模式,无法实现主从复制、分片存储等集群能力。 |
cluster-node-timeout 15000 | 节点超时时间 15 秒 | 1. 快速故障检测:节点失联 15 秒后标记为 “故障”,避免业务长时间访问无效节点;2. 避免误判:网络抖动(如 1-2 秒)不会触发故障转移,平衡可靠性与灵敏度。 |
cluster-config-file /data/nodes.conf | 集群信息存储路径 | 1. nodes.conf是 Redis 自动维护的集群元数据文件(记录节点 IP、角色、分片信息);2. /data目录通常挂载 K8s 存储卷(如 PVC),避免 Pod 重启后集群元数据丢失(若存于临时目录,Pod 重建后集群信息清空,导致集群崩溃)。 |
cluster-announce-bus-port 16379 | 集群总线端口 | Redis Cluster 依赖 “总线端口” 实现节点间高效通信(如故障检测、配置同步、主从切换指令),需主动声明端口,确保跨 Pod 通信无阻塞(默认总线端口 = 客户端端口 + 10000,此处显式配置便于运维排查)。 |
3. 认证与安全配置
| 配置项 | 配置内容 | 设计原因 |
|---|---|---|
requirepass ClusterRedis#20251112 | 客户端连接密码 | 防止未授权访问(如 K8s 集群内其他无关 Pod 恶意连接),密码含大小写、数字、特殊字符,满足基础安全复杂度要求。 |
masterauth ClusterRedis#20251112 | 主从同步密码 | 从节点向主节点同步数据时需认证,与requirepass保持一致:1. 避免主从同步因认证失败中断;2. 主从切换后,新主节点仍能正常接收其他从节点的同步请求。 |
4. 持久化配置(数据安全保障)
| 配置项 | 配置内容 | 设计原因 |
|---|---|---|
appendonly yes | 启用 AOF 持久化 | AOF(Append Only File)记录所有写操作,比 RDB(快照)更能保证数据不丢失(RDB 可能丢失最后一次快照后的操作),适合对数据一致性要求较高的场景。 |
save 3600 1 | RDB 快照规则(3600 秒 1 次写操作触发) | 1. 结合 AOF 实现 “双持久化”:RDB 做全量备份(便于快速恢复),AOF 做增量备份(减少数据丢失);2. 3600 秒(1 小时)触发一次,避免频繁快照占用 CPU/IO 资源。 |
stop-writes-on-bgsave-error no | RDB 失败时不停止写操作 | 若设为yes,RDB 备份失败会导致 Redis 拒绝所有写请求,影响业务;设为no可优先保证业务可用性,同时依赖 AOF 兜底数据安全。 |
appendfsync everysec | AOF 每秒刷盘一次 | 平衡性能与数据安全:- always(每次写操作刷盘)性能差;- no(操作系统自动刷盘)数据丢失风险高;- everysec仅可能丢失 1 秒内数据,满足多数业务的可用性要求。 |
auto-aof-rewrite-percentage 100+auto-aof-rewrite-min-size 64mb | AOF 重写规则 | 1. AOF 文件大小超过 64MB 且比上次重写后增大 100% 时触发重写,避免 AOF 文件无限膨胀(过大的 AOF 会导致恢复时间变长、占用磁盘空间);2. 64MB 为合理阈值,避免小文件频繁重写。 |
aof-load-truncated yes | 允许加载截断的 AOF 文件 | AOF 文件末尾若因异常(如断电)截断,Redis 仍尝试加载有效部分,减少因小故障导致 Redis 启动失败的概率(若为no,截断 AOF 会直接导致启动报错)。 |
5. 内存管控配置
| 配置项 | 配置内容 | 设计原因 |
|---|---|---|
maxmemory 16G | 最大内存限制 16GB | 1. 避免 Redis 内存溢出导致 Pod 被 K8s OOM(Out Of Memory)驱逐;2. 16GB 为业务适配值(需根据实际缓存 / 存储需求调整,如小集群设 4GB、大集群设 32GB)。 |
maxmemory-policy allkeys-lru | 内存满时淘汰策略(LRU) | 1. allkeys-lru:淘汰 “所有键中最近最少使用” 的键,适合缓存场景(如业务需优先保留热点数据);2. 若为存储场景(如 Redis 存业务数据),可改为noeviction(内存满时拒绝写操作),避免误删核心数据。 |
三、init-redis-cluster.sh配置解析
#!/bin/bash
# 1. 指定脚本解释器为bash,确保在K8s容器(通常为Linux环境)中可正常执行
set -e
# 2. 容错配置:脚本中任一命令返回非0状态码(执行失败)时,立即退出脚本
# 避免错误延续(如某节点未就绪仍强行创建集群,导致集群初始化失败)# -------------------------- 变量定义部分 --------------------------
# 3. Redis密码变量:统一管理客户端连接与主从同步密码,避免硬编码分散
# 密码含大小写、数字、特殊字符(ClusterRedis#20251112),满足基础安全复杂度,防止未授权访问
REDIS_PASSWORD="ClusterRedis#20251112"
# 4. K8s Service名变量:对应Redis集群的Service名称(redis-cluster)
# 后续节点地址拼接时使用Service域名(如redis-cluster-0.redis-cluster),适配K8s Pod动态IP特性
SERVICE_NAME="redis-cluster"
# 5. 集群节点列表:定义6个节点(3主3从架构,Redis Cluster最小高可用架构)
# 地址格式为“Pod名.Service名:端口”,K8s中通过该域名可稳定访问各Pod(无需依赖动态IP)
NODES=("redis-cluster-0.$SERVICE_NAME:6379""redis-cluster-1.$SERVICE_NAME:6379""redis-cluster-2.$SERVICE_NAME:6379""redis-cluster-3.$SERVICE_NAME:6379""redis-cluster-4.$SERVICE_NAME:6379""redis-cluster-5.$SERVICE_NAME:6379"
)# -------------------------- 节点就绪检查部分 --------------------------
echo "检查所有 Redis 节点是否就绪..."
all_ready=1
# 6. 循环遍历所有节点:确保集群初始化前,所有节点均已启动并能正常响应
for node in "${NODES[@]}"; do# 7. 拆分节点地址为“主机名”和“端口”:从节点字符串(如xxx:6379)中提取关键信息,用于后续ping检查host=$(echo "$node" | cut -d: -f1)port=$(echo "$node" | cut -d: -f2)echo "检查节点: $host:$port"# 8. Redis节点可用性检查:通过redis-cli ping命令判断节点是否存活# -h $host:指定节点主机名(K8s Service域名)# -p $port:指定Redis客户端端口(6379,与配置文件一致)# -a $REDIS_PASSWORD:携带密码认证(避免未授权访问拦截)# 2>/dev/null:屏蔽错误输出(如连接超时的报错),仅通过“PONG”判断状态,避免日志冗余if [ "$(redis-cli -h $host -p $port -a $REDIS_PASSWORD ping 2>/dev/null)" != "PONG" ]; thenecho "节点 $node 未就绪"all_ready=0breakelseecho "节点 $node 已就绪"fi
done# 9. 未就绪节点处理:若存在未就绪节点,脚本以0状态码退出(非错误退出)
# 原因:K8s会将非0退出视为“脚本执行失败”并重启Pod,此处用exit 0避免无效重启
# 后续Pod重启时,脚本会重新执行检查,直到所有节点就绪
if [ $all_ready -ne 1 ]; thenecho "有节点未就绪,退出"exit 0
fi# -------------------------- 集群状态判断部分 --------------------------
echo "检查集群状态..."
# 10. 获取集群状态:通过第一个节点的cluster info命令获取集群状态(cluster_state)
# 原因:Redis Cluster中所有节点的集群状态全局一致,无需遍历所有节点,减少检查开销
# ${NODES[0]%:*}:提取第一个节点的主机名(删除字符串最后一个“:”及后面内容)
# ${NODES[0]#*:}:提取第一个节点的端口(删除字符串第一个“:”及前面内容)
# tr -d '[:space:]':去除结果中的空格,避免空格导致状态判断失效
cluster_state=$(redis-cli -h ${NODES[0]%:*} -p ${NODES[0]#*:} -a $REDIS_PASSWORD cluster info 2>/dev/null | grep "cluster_state" | cut -d: -f2 | tr -d '[:space:]')# 11. 已初始化集群处理:若集群状态为“ok”(已完成初始化),脚本退出
# 原因:K8s Pod可能因重启(如配置更新、节点故障恢复)重新执行脚本
# 重复执行--cluster create会导致集群元数据混乱(如节点角色冲突),需避免
if [ "$cluster_state" = "ok" ]; thenecho "集群已初始化,状态正常"exit 0
fi# -------------------------- 集群初始化部分 --------------------------
echo "开始初始化 Redis 集群..."
# 12. 集群创建命令:核心指令,完成3主3从集群的自动初始化
# -a $REDIS_PASSWORD:携带密码,确保初始化过程中认证通过
# --cluster create:触发Redis Cluster创建流程
# --cluster-replicas 1:指定“1主1从”架构,6个节点自动分配为3主3从(按节点顺序:0/1/2为主,3/4/5为从)
# 优势:无需手动指定主从关系,适配K8s自动化部署场景,减少运维操作
redis-cli -a $REDIS_PASSWORD \--cluster create \--cluster-replicas 1 \${NODES[@]} echo "集群初始化完成"
Headless Service配置
- service-headless.yaml
kind: Service
apiVersion: v1
metadata:name: redis-clusterspec:ports:- name: redisprotocol: TCPport: 6379targetPort: 6379- name: electionprotocol: TCPport: 16379targetPort: 16379selector:app: redis-clusterclusterIP: Nonetype: ClusterIP
一、两个端口的作用
- 6379 端口(名称:redis)
-
用途:Redis 客户端通信端口,供 K8s 集群内业务 Pod 访问 Redis 集群(如数据读写);
-
对应关系:与 Redis 配置文件
redis-cluster.conf中port 6379一致,Service 端口与 Pod 目标端口直接映射。
- 16379 端口(名称:election)
-
用途:Redis 集群总线端口,供节点间内部通信(心跳检测、故障选举、主从数据同步),是集群高可用的核心端口;
-
对应关系:与配置文件
cluster-announce-bus-port 16379呼应,确保节点间状态交互不中断。
二、Headless Service(clusterIP: None)的作用
-
精准服务发现:DNS 解析直接返回所有 Redis 节点 Pod 的真实地址(而非统一负载 IP),适配集群初始化时 “节点互认” 需求,与 ConfigMap 中节点地址格式(
Pod名.服务名:端口)匹配,Headless Service 创建后,会在 Kubernetes 集群内部的 DNS 服务器中生成特定的 DNS 记录。这些记录包含了服务名称、命名空间等信息,格式通常为
<service-name>.<namespace>.svc.cluster.local, 在本文中,该值被设置为redis-cluster-0.redis-cluster。客户端通过解析这些 DNS 记录,可直接获取后端 Pod 的 IP 地址列表,实现无代理的直连访问,有效避免传统 Service 负载均衡层带来的性能损耗和网络延迟。
-
取消负载均衡:无统一集群 IP,请求直接路由到目标 Pod,满足 Redis 节点 “点对点通信”(如从节点固定连主节点、心跳检测定位具体节点);
-
稳定地址映射:Pod 重启后 IP 变化,但 DNS 域名(
Pod名.redis-cluster)不变,确保nodes.conf中集群元数据不失效,节点快速重连; -
双端口保障:同时映射 6379 和 16379 端口,无转发损耗,兼顾业务访问与集群内部通信。
Redis Statefulsets
kind: StatefulSet
apiVersion: apps/v1
metadata:name: redis-clusterlabels:app: redis-cluster
spec:replicas: 6selector:matchLabels:app: redis-clustertemplate:metadata:labels:app: redis-clusterspec:volumes:- name: redis-confconfigMap:name: redis-cluster-cmitems:- key: redis-cluster.confpath: redis-cluster.confdefaultMode: 420- name: redis-initconfigMap:name: redis-cluster-cmitems:- key: init-redis-cluster.shpath: init-redis-cluster.shdefaultMode: 420containers:- name: redis-clusterimage: 'redis:7.2.6-amd64'args:- /etc/redis/redis-cluster.conf- '--cluster-announce-ip "$(POD_NAME).$(POD_SERVICE_NAME)"'ports:- name: rediscontainerPort: 6379protocol: TCP- name: electioncontainerPort: 16379protocol: TCPenv:- name: TZvalue: Asia/Shanghai- name: POD_NAMEvalueFrom:fieldRef:apiVersion: v1fieldPath: metadata.name- name: POD_SERVICE_NAMEvalue: redis-clusterresources:limits:cpu: '1'memory: 2Girequests:cpu: 500mmemory: 1GivolumeMounts:- name: redis-confmountPath: /etc/redis- name: redis-initmountPath: /redis-init- name: redis-cluster-datamountPath: /datalivenessProbe:tcpSocket:port: redisinitialDelaySeconds: 16timeoutSeconds: 1periodSeconds: 3successThreshold: 1failureThreshold: 2readinessProbe:tcpSocket:port: redisinitialDelaySeconds: 16timeoutSeconds: 1periodSeconds: 3successThreshold: 1failureThreshold: 2imagePullPolicy: IfNotPresentrestartPolicy: Alwaysaffinity:podAntiAffinity:preferredDuringSchedulingIgnoredDuringExecution:- weight: 100podAffinityTerm:labelSelector:matchExpressions:- key: appoperator: Invalues:- redis-clustertopologyKey: kubernetes.io/hostnameschedulerName: default-schedulervolumeClaimTemplates:- kind: PersistentVolumeClaimapiVersion: v1metadata:name: redis-cluster-dataspec:accessModes:- ReadWriteManyresources:requests:storage: 10GistorageClassName: nfsvolumeMode: FilesystemserviceName: redis-cluster
一、StatefulSet 对 Redis Cluster 的核心价值
StatefulSet 是 Kubernetes 中管理有状态应用的控制器,其核心能力是为 Pod 提供“稳定的网络标识”和“独立的持久化存储”。对于 Redis Cluster 而言,这种特性至关重要——集群需要固定的节点身份(主从关系绑定)、持久化的集群元数据(nodes.conf)和数据文件(RDB/AOF),而 StatefulSet 恰好满足这些需求,是集群节点的“载体框架”。
二、核心配置解析(聚焦节点亲和性、健康检查、ServiceName)
1. serviceName: redis-cluster:为 Redis 节点提供“稳定网络标识”
- 配置含义:指定 StatefulSet 关联的 Headless Service 名称(与前文定义的
redis-clusterService 一致)。 - 作用:
StatefulSet 会为每个 Pod 生成固定的 DNS 域名,格式为{pod-name}.{serviceName}.{namespace}.svc(例如redis-cluster-0.redis-cluster.default.svc)。
这对 Redis Cluster 至关重要:- Redis 节点间需要通过固定地址通信(主从同步、故障检测),而 Pod 的 IP 是动态变化的,固定域名可避免 IP 变更导致的节点失联;
- 与容器启动参数
--cluster-announce-ip "$(POD_NAME).$(POD_SERVICE_NAME)"呼应,确保节点向集群宣告的地址是稳定的域名(而非临时 IP),集群元数据(nodes.conf)中记录的地址永久有效。
2. 节点亲和性(podAntiAffinity):提升集群抗风险能力
- 配置含义:
affinity:podAntiAffinity:preferredDuringSchedulingIgnoredDuringExecution: # 优先调度策略(非强制)- weight: 100 # 权重100(最高优先级)podAffinityTerm:labelSelector: # 匹配标签为 app=redis-cluster 的 PodmatchExpressions:- key: appoperator: Invalues: [redis-cluster]topologyKey: kubernetes.io/hostname # 按主机(Node)划分拓扑域 - 作用:
让 6 个 Redis Pod尽量分散在不同的 Kubernetes 节点上(而非集中在少数节点)。
对 Redis Cluster 的意义:- 避免“单点故障”放大:若多个 Redis 节点(尤其是主从节点对)集中在同一主机,当该主机宕机时,可能导致多个主节点或其从节点同时下线,集群分片数据丢失风险升高;
- 均衡资源负载:分散部署可避免单节点 CPU/内存/IO 压力过大,保障 Redis 性能稳定。
3. 健康检查(livenessProbe 与 readinessProbe):保障节点可用性
-
配置共性:均通过
tcpSocket检测 6379 端口(Redis 客户端端口),参数如下:initialDelaySeconds: 16:容器启动 16 秒后开始检测(给 Redis 初始化、加载配置和数据的时间);periodSeconds: 3:每 3 秒检测一次(高频检测,快速发现问题);timeoutSeconds: 1:超时 1 秒即判定失败;failureThreshold: 2:连续 2 次失败触发处理。
-
作用差异:
- 存活探针(
livenessProbe):
检测 Redis 进程是否“存活”。若失败,Kubernetes 会重启容器(例如 Redis 进程崩溃时,自动重启恢复),确保节点不长期处于“死亡”状态。 - 就绪探针(
readinessProbe):
检测 Redis 节点是否“可用”。若失败,Kubernetes 会将该 Pod 从 Service endpoints 中移除(不接收流量),避免业务访问“未就绪”节点(例如 Redis 正在加载 AOF 数据、集群初始化中)。
- 存活探针(
如何安装?
将所有yaml文件放置在一个文件夹下,e.g. redis-cluster
kubectl apply -f redis-cluster/*.yaml# exec 进入容器
kubectl exec -it redis-cluster-0 -- /bin/bash# 初始化集群
cd /redis-init
bash init-redis-cluster.sh
集群测试
查看集群状态、测试数据读写,进入任意一个pod执行:```bash
# kubectl -n test exec -it redis-cluster-sts-0 -- bash
root@redis-cluster-sts-0:/data# redis-cli -a ClusterRedis#20251112 -c Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
# CLUSTER NODES 查看集群节点,正常
127.0.0.1:6379> CLUSTER NODES
# CLUSTER INFO 查看集群信息:cluster_state:ok表示集群正常
127.0.0.1:6379> CLUSTER INFO# 写入,读取key, 正常
127.0.0.1:6379> set boo foo
127.0.0.1:6379> get boo
"foo"
