K8S部署redis cluster
Redis Cluster介绍
Redis 是一个开源的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。Redis 通过 哨兵(Sentinel) 和自动分区(Cluster)提供高可用性(high availability)。在Redis集群中,节点负责保存数据并获取集群状态,包括将键映射到正确的节点。集群节点还能够自动发现其他节点、检测不工作的节点并在需要时将副本节点提升为主节点,以便在发生故障时继续运行。所有集群节点都使用 TCP 总线和二进制协议(称为Redis 集群总线)连接起来。每个节点都使用集群总线连接到集群中的每个其他节点。节点使用 gossip 协议传播有关集群的信息,以便发现新节点,发送 ping 数据包以确保所有其他节点正常工作,并发送集群消息以表示特定情况。由于集群节点无法代理请求,因此客户端可能会使用重定向错误-MOVED和重定向到其他节点-ASK。理论上,客户端可以自由地将请求发送到集群中的所有节点,并在需要时进行重定向,因此客户端无需保存集群的状态。但是,能够缓存键和节点之间的映射的客户端可以以合理的方式提高性能。
————————————————————————————————————
集群部署架构
集群拓补图
文档环境说明
节点角色 | 软件版本 | K8S版本 | 备注 |
---|---|---|---|
Master1 | Redis 6.2.12 | 1.23.6 | 角色非固定 |
Master2 | Redis 6.2.12 | 1.23.6 | 角色非固定 |
Master3 | Redis 6.2.12 | 1.23.6 | 角色非固定 |
Slave1 | Redis 6.2.12 | 1.23.6 | 角色非固定 |
Slave2 | Redis 6.2.12 | 1.23.6 | 角色非固定 |
Slave3 | Redis 6.2.12 | 1.23.6 | 角色非固定 |
参考文档:redis cluster官方文档
redis集群部署
创建namespace
kubectl create ns redis-clu-9
redis-pv.yaml (基于nfs)
容量根据业务需要修改,如果集群已经存在可用pv资源,可不用创建这个yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-redis-cluster-pv1
spec:
capacity:
storage: 1000M
accessModes:
- ReadWriteMany
nfs:
server: 192.XXX.XXX.240
path: "/home/nfsdata/redis-cluster/pv1"
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-redis-cluster-pv2
spec:
capacity:
storage: 1000M
accessModes:
- ReadWriteMany
nfs:
server: 192.XXX.XXX.240
path: "/home/nfsdata/redis-cluster/pv2"
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-redis-cluster-pv3
spec:
capacity:
storage: 1000M
accessModes:
- ReadWriteMany
nfs:
server: 192.XXX.XXX.240
path: "/home/nfsdata/redis-cluster/pv3"
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-redis-cluster-pv4
namespace: redis-clu-9
spec:
capacity:
storage: 1000M
accessModes:
- ReadWriteMany
nfs:
server: 192.XXX.XXX.240
path: "/home/nfsdata/redis-cluster/pv4“
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-redis-cluster-pv5
spec:
capacity:
storage: 1000M
accessModes:
- ReadWriteMany
nfs:
server: 192.XXX.XXX.240
path: "/home/nfsdata/redis-cluster/pv5“
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-redis-cluster-pv6
spec:
capacity:
storage: 1000M
accessModes:
- ReadWriteMany
nfs:
server: 192.XXX.XXX.240
path: "/home/nfsdata/redis-cluster/pv6“
redis集群configmap
kubectl apply -f redis-configmap.yaml -n redis-clu-9
cat redis-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: redis-conf
namespace: redis-clu-9
data:
fix-pod-ip.sh: |
#!/bin/sh
CLUSTER_CONFIG="/var/lib/redis/nodes.conf"
if [ -f ${CLUSTER_CONFIG} ]; then
if [ -z "${POD_IP}" ]; then
echo "Unable to determine Pod IP address!"
exit 1
fi
echo "Updating pod IP to ${POD_IP} in ${CLUSTER_CONFIG}"
sed -i -e '/myself/ s/[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}/'${POD_IP}'/' ${CLUSTER_CONFIG}
count=`grep -c ${POD_IP} ${CLUSTER_CONFIG}`
if [[ $count > 0 ]];then
echo "Successful updated pod Ip to ${POD_IP} in ${CLUSTER_CONFIG}"
fi
fi
exec "$@"
redis.conf: |
# 在这里粘贴你的 redis.conf 文件的内容
# 或者你可以使用 kubectl create configmap --dry-run -o yaml ... 来生成
#开启集群模式
cluster-enabled yes
# 监听ip
bind 0.0.0.0
port 6379
#保护模式
protected-mode no
#redis后台运行
#daemonize yes
#设置客户端连接的超时时间,避免长时间占用连接资源
timeout 300
#设置集群节点之间通信的超时时间
cluster-node-timeout 5000
#指定集群配置文件名称
cluster-config-file /var/lib/redis/nodes.conf
#数据存储目录
dir /var/lib/redis
#设置同时连接客户端的最大数量
maxclients 10000
#指定服务器冗余级别
loglevel notice
# 设置Redis能够使用的最大内存量。这有助于防止Redis因内存耗尽而崩溃
maxmemory 1000mb
#内存淘汰策略
maxmemory-policy volatile-lru
# 数据持久化
appendonly yes
#设置AOF的同步策略,如everysec(每秒同步一次)以平衡性能和数据安全性。
appendfsync everysec
#AOF文件名
appendfilename "appendonly.aof"
#自动重写附加文件条件配置
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# 允许在AOF重写期间进行增量fsync操作它可以帮助减少延迟并减轻fsync对应用程序性能的影响
aof-rewrite-incremental-fsync yes
# 优化内存使用
hash-max-ziplist-entries 512
hash-max-ziplist-value 64kb
# 频率
hz 10
redis集群svc
kubectl apply -f headless-service.yaml
cat headless-service.yaml
apiVersion: v1
kind: Service
metadata:
name: redis-service
namespace: redis-clu-9
labels:
app: redis
spec:
ports:
- name: redis-port
port: 6379
- name: redis-cluster-port
port: 16379
clusterIP: None
selector:
app: redis
appCluster: redis-cluster
暴漏访问集群入口:
kubectl apply -f redis-cluster-access-service.yaml
cat redis-cluster-access-service.yaml
apiVersion: v1
kind: Service
metadata:
name: redis-cluster-access-service
namespace: redis-clu-9
labels:
app: redis
spec:
ports:
- name: redis-cluster-port
protocol: TCP
port: 6379
targetPort: 6379
nodePort: 34476
selector:
app: redis
appCluster: redis-cluster
type: NodePort
redis集群statefulSet
kubectl apply -f redis.yaml
cat redis.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis-app
namespace: redis-clu-9
spec:
serviceName: "redis-service"
selector:
matchLabels:
app: redis
replicas: 6
template:
metadata:
labels:
app: redis
appCluster: redis-cluster
spec:
terminationGracePeriodSeconds: 20
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- redis
topologyKey: kubernetes.io/hostname
containers:
- name: redis
image: redis:6.2.12
command: ["/etc/redis/fix-pod-ip.sh", "redis-server", "/etc/redis/redis.conf"]
resources:
requests:
cpu: "100m" #此处根据业务需求修改
memory: "100Mi" #此处根据业务需求修改
env:
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
ports:
- name: redis
containerPort: 6379
protocol: "TCP"
- name: cluster
containerPort: 16379
protocol: "TCP"
volumeMounts:
- name: "redis-conf"
mountPath: "/etc/redis"
readOnly: false
- name: "redis-data"
mountPath: "/var/lib/redis"
readOnly: false
volumes:
- name: "redis-conf"
configMap:
name: "redis-conf"
defaultMode: 0755
volumeClaimTemplates:
- metadata:
name: redis-data
namespace: redis-clu-9
spec:
accessModes: [ "ReadWriteMany" ]
resources:
requests:
storage: 1000M
storageClassName: nfs
redis集群初始化
容器内创建
#### 手动获取集群node ip
kubectl get pod -n redis-clu-9 -o wide
#### 命令获取集群node ip
kubectl get pods -n redis-clu-9 -l app=redis -o jsonpath='{range.items[*]}{.status.podIP}:6379 ' | sed 's/ :6379//g'
#### 输出下面类似格式的IP:PORT列表, 复制这个输出下面命令会用到###
192.168.53.251:6379 192.168.232.57:6379 192.168.161.250:6379 192.168.104.168:6379 192.168.201.26:6379 192.168.77.202:6379
########
kuectl exec -it -n redis-clu-9 pod-name bash
### 以下为进入容器操作,node_IP 列表为上面命令输出
redis-cli --cluster create node1_IP:6379 node2_IP:6379 node3_IP:6379 node4_IP:6379 node5_IP:6379 node6_IP:6379 --cluster-replicas 1
容器外创建
### 也可以在容器外一键创建
kubectl -n redis-clu-9 exec -it pod-name -- redis-cli --cluster create $(kubectl get pods -n redis-clu-9 -l app=redis -o jsonpath='{range.items[*]}{.status.podIP}:6379 ' | sed 's/ :6379//g') --cluster-replicas 1
集群创建过程结果
redis集群参数优化
配置文件参数
Redis-configmap.yaml 的 redis.conf部分
apiVersion: v1
kind: ConfigMap
metadata:
name: redis-conf
namespace: redis-clu-9
data:
redis.conf: |
# 在这里粘贴你的 redis.conf 文件的内容
# 或者你可以使用 kubectl create configmap --dry-run -o yaml ... 来生成
#开启集群模式
cluster-enabled yes
port 6379
#保护模式
protected-mode no
#redis后台运行
#daemonize yes
# 设置客户端连接的超时时间,避免长时间占用连接资源
timeout 300
#设置集群节点之间通信的超时时间
cluster-node-timeout 5000
#指定集群配置文件名称
cluster-config-file /var/lib/redis/nodes.conf
#集群数据目录
dir /var/lib/redis
#设置同时连接客户端的最大数量
maxclients 10000
#指定服务器冗余级别
loglevel notice
##############
# 设置Redis能够使用的最大内存量。这有助于防止Redis因内存耗尽而崩溃
maxmemory 1000mb
#内存淘汰策略,针对设置 expried key
maxmemory-policy volatile-lru
# 数据持久化
appendonly yes
#设置AOF的同步策略,如everysec(每秒同步一次)以平衡性能和数据安全性。
appendfsync everysec
appendfilename "appendonly.aof"
#自动重写附加文件
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# 优化内存使用
hash-max-ziplist-entries 512
hash-max-ziplist-value 64kb
# 任务频率
# 值越大redis 响应时间越短性能较好但同时资源消(cpu)耗也会增大
# 最好结合监控数据如: cpu memery network 来测试验证设定
# 也可以结合业务性能要求来设置
# 需要重启生效
hz 10
# 允许在AOF重写期间进行增量fsync操作可以帮助减少延迟并减轻fsync对应用程序性能的影响
aof-rewrite-incremental-fsync yes
其他重要参数:
cluster-replica-validity-factor:10
cluster-slave-validity-factor 老版本
它会影响副本如何决定在发生故障时是否有资格对主节点进行故障转移。
如果设置为零,副本将始终认为自己可以进行故障转移,这意味着无论与主服务器断开连接多长时间,它都会尝试接管主服务器。
如果设置为正值,则通过将节点超时值乘以此因子来计算最大断开连接时间。如果副本与其主服务器断开连接的时间超过此计算时间,则副本不会尝试对主服务器进行故障转移。
例如,如果节点超时时间为 5 秒,有效性因子为 10,则如果副本断开连接超过 50 秒,则副本不会尝试故障转移其主节点。将因子设置为零是确保在网络分区解决后集群继续运行的唯一方法。
cluster-node-timeout : 15000 ms
一个至关重要的配置参数,用于指定 Redis Cluster 节点在被视为发生故障之前可以处于不可用状态的最长时间。如果主节点在此超时期限内无法与大多数其他主节点通信,它将进入错误状态并停止接受写入。此设置可确保集群能够可靠地确定节点何时不再按预期运行并采取必要的措施,例如故障转移到副本
cluster-migration-barrier: 1
用于指定主服务器必须具有的最小副本数,在此数量下,其他副本才被允许迁移到缺少副本的另一个主服务器。这有助于确保主服务器保持足够的副本数,然后才允许其副本之一在其他地方提供帮助。
如果主服务器最终没有任何副本,则具有超过规定的最低所需副本数的主服务器的副本将迁移到需要副本的主服务器。此机制通过确保所有主服务器都由副本备份来帮助维护 Redis 集群的弹性和稳定性,从而促进服务的连续性。
cluster-require-full-coverage: yes
默认情况下,它设置为“是”,这意味着如果由于故障或问题导致任何节点无法覆盖一定比例的键空间,则集群将停止接受写入。这可确保集群仅在能够覆盖整个键空间时才处理事务,从而保持数据完整性和一致性。
如果设置为“否”,即使只能处理部分键,集群也将继续处理读取和写入查询,从而允许操作在键空间的可用部分上进行。如果您想确保应用程序在出现某些节点故障或使用只有一两个分片的集群时仍能部分可用,这将非常有用。
cluster-allow-reads-when-dow: no
决定了当集群被标记为失败时,节点是否将继续处理读取请求。默认情况下,此设置为“否”,这意味着当集群关闭时,节点将停止处理所有流量,以确保不会读取任何可能不一致的数据。但是,将此选项设置为“是”可让节点在失败状态下继续处理读取请求。如果您的应用程序优先考虑读取可用性,这可能很有用,但需要注意的是,这可能会导致读取可能过时或不一致的数据。
读写key测试
Create-key.py
from rediscluster import RedisCluster
# Redis集群的启动节点列表,这里需要根据你的实际环境进行修改
startup_nodes = [
{"host": "192.168.123.240", "port": "34476"},
# 如果有多个节点,继续添加...
# {"host": "another_host", "port": "another_port"},
]
# 连接到Redis集群
rc = RedisCluster(startup_nodes=startup_nodes, decode_responses=True)
# 批量创建1万个键值对
for i in range(1, 10001):
key = f"key_{i}"
value = f"value_{i}"
rc.set(key, value)
print("键值对创建完成!")
创建的key分布测试
root@k8s-master1:[/root/zl_test/redis_cluster]redis-cli -h 192.168.123.240 -p 34476
192.168.123.240:34476> get key_1
(error) MOVED 11998 192.168.53.192:6379
192.168.123.240:34476> get key_100
(error) MOVED 12624 192.168.53.192:6379
192.168.123.240:34476> get key_800
(error) MOVED 12225 192.168.53.192:6379
192.168.123.240:34476> get key_1000
(error) MOVED 33 192.168.161.230:6379
192.168.123.240:34476> get key_5000
(error) MOVED 2768 192.168.161.230:6379
192.168.123.240:34476> get key_8000
(error) MOVED 13142 192.168.53.192:6379
192.168.123.240:34476> get key_9001
(error) MOVED 5571 192.168.104.186:6379
192.168.123.240:34476> get key_9006
(error) MOVED 9508 192.168.104.186:6379
192.168.123.240:34476> get key_10000
(error) MOVED 8087 192.168.104.186:6379
### 可以使用redis-cli -c 集群模式测试
redis集群验证
集群当前状态
高可用测试
从集群状态中可看出
192.168.53.192主和192.168.232.52从互为主备
删除节点模拟故障
通过修改主节点镜像的方式故障
从节点日志
可以看到主节点故障后从节点已成为主节点
查看集群信息及使用
可以看到原slave已切换成master且集群仍可正常使用
恢复集群
恢复pod
新成为master上日志,显示主从同步成功
查看集群状态新上线后已成为slave节点
集群状态正常