关于如何搭建 K8s 集群,教程很多,我是参考的此文完成搭建:
Centos7.6部署k8s v1.16.4高可用集群(主备模式)_Kubernetes中文社区
https://www.kubernetes.org.cn/6632.html
最近我负责完成了公司 Pass 平台(内核部分)单机 Redis 升级改造为 Redis Cluster,并将相关代码和功能进行适配,业务平滑过渡。继而领导交代了后续任务,提供基于:
- 物理机或者虚拟机直接构建 Redis Cluster 部署文档
- 一台或多台物理机或虚拟机基于 Docker 构建 Redis Cluster 部署文档
- K8s 集群上构建 Redis Cluster 部署文档
并实现高可用和故障转移,根据ZAP理论,Redis 强调数据一致性,否则拒绝对外服务 (敲黑板,这是重点,稳定压倒一切)
目前文档完成了交付,在开发环境通过了测试。准备投入预生产环境。
一共是三份,前面两份文档没啥难度,重点记录第三份操作。
选择 K8s 来部署 Redis Cluster 集群,要点是理解 K8s 的功能,运用这些特性,再结合 Redis Cluster 本身的广播协议 Gossip,选择合适方案。
K8s部分
K8s的工作负载模式:
- Deployment(StateFulLess) 无状态
- StateFulSet 有状态
- 定时
- ...........
这里我选择是 StateFulSet 模式,适用于有状态应用,如 Redis, Zookeeper
这里需要注意的是,在 K8s 的 V1.18 版本后,StateFulSet 才从实验性功能,成为稳定功能,当然像 V1.10版本,都是可以使用此模式的。
数据挂载
计划使用分布式数据\对象存储,将 Redis Cluster 集群持久化的文件,进行保存。这里直接使用一台 NFS 做 PV,PVC,最好是用 MinIo 这种
关键需求:
- 集群内外均可访问
我查找了很多资料,CSDN,思否,51CTO,stackoverflow、后来直接找 K8s 开发者的博客(提供了思路)。没有找到实测可以集群外访问的 - 支持 node.conf 文件的内容重建
node.conf 文件,是 Redis Cluster 集群搭建后,自动生成的配置文件,如果机器非抗力的断电重启,虽然由 StateFulSet 进行维护状态,但可能节点ID、IP 发生了变化,从而丢失部分 Slot。 因此需要重建
Redis Cluster 部分
Gossip 协议里面,ping-pong 这种机制,直接绑定了 pod 的IP,所以也反推出了,K8s 应该采用的网络模式。是 Ingress-Nginx 还是 普通的 Service,是绑定Pod IP, 还是域名。这里我直接暴露 K8s 的 Node 机器节点 IP + redis 服务端口号,
上菜!
有了以上梳理,则已经完成了调研,直接再查具体文档,完成实现。
机器 和 节点规划
这里使用10台服务器(K8s集群内,1台 client, 3台 master, 6台 worker node),是的,我就是这么豪横。当然了,建议的最小数量机器是:(1台 master, 3台 work),再少就不合适了。
Redi Cluster 节点为 6个,则 每个 worker 节点,运行一个Redis 实例。
操作环境说明:基于 VMware Esxi 并通过 vSphere Client 管理 十台 Linux 服务器,组成 K8s 集群
主机列表:
VIP:192.168.50.222 ( VIP 通过Etcd 实现故障转移,默认在 Master1 上,虚拟节点)
Client:192.168.50.7(client主节点)
Master1:192.168.50.122(control plane)
Master2:192.168.50.167(control plane)
Master3:192.168.50.176(control plane)
Work1: 192.168.50.161(worker node)
Work2: 192.168.50.180(worker node)
Work3: 192.168.50.105(worker node)
Work4: 192.168.50.109(worker node)
Work5: 192.168.50.224(worker node)
Work6: 192.168.50.109(worker node)
- Centos 版本和配置:CentOS Linux release 7.8.2003 (Core):4颗CPU + 4GB (每台)
- Redis 版本:
V5.0.9 (本文档兼容任意 V5 版本,对于V4 及其以下,或V6等更高版本,不确定可行性,V4集群需要使用 Ruby 语法;V6 版本则需要对 Centos 7 系统设置进行更多的调整)
机器概览
节点信息
部署 NFS 存储
创建NFS是给Redis提供稳定后端存储,当Redis的Pod重启或迁移后,依然能获得原先的数据。这里使用最简单的 NFS 进行示例
# 在Master2 的磁盘做共享存储服务器,当前操作机器为 Master2
$ yum -y install nfs-utils rpcbind
$ mkdir -p /data/redis-cluster-example/cluster{1..6}
# 追加配置
$ cat >> /etc/exports <<EOF
/data/redis-cluster-example/cluster1 192.168.50.167/24(rw,sync,no_root_squash)
/data/redis-cluster-example/cluster2 192.168.50.167/24(rw,sync,no_root_squash)
/data/redis-cluster-example/cluster3 192.168.50.167/24(rw,sync,no_root_squash)
/data/redis-cluster-example/cluster4 192.168.50.167/24(rw,sync,no_root_squash)
/data/redis-cluster-example/cluster5 192.168.50.167/24(rw,sync,no_root_squash)
/data/redis-cluster-example/cluster6 192.168.50.167/24(rw,sync,no_root_squash)
EOF
$ chmod -R 755 /data/redis-cluster- example/
$ exportfs -arv
$ systemctl enable rpcbind && systemctl start rpcbind
$ systemctl enable nfs && systemctl start nfs
创建 PersistentVolumes
# 我们在 client 节点上进行相关操作。如果没有client节点,则在任意 master 节点进行操作
# 当前操作目录:/usr/local/soft/k8s/redis-cluster-example
$ mkdir -p /usr/local/soft/k8s/redis-cluster-example && cd /usr/local/soft/k8s/redis-cluster-example
$ cat <<EOF> /usr/local/soft/k8s/redis-cluster-example/pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: redis-pv1
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
storageClassName: "redis-cluster"
nfs:
path: /data/redis-cluster-example/cluster1
server: 192.168.50.167
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: redis-pv2
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
storageClassName: "redis-cluster"
nfs:
path: /data/redis-cluster-example/cluster2
server: 192.168.50.167
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: redis-pv3
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
storageClassName: "redis-cluster"
nfs:
path: /data/redis-cluster-example/cluster3
server: 192.168.50.167
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: redis-pv4
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
storageClassName: "redis-cluster"
nfs:
path: /data/redis-cluster-example/cluster4
server: 192.168.50.167
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: redis-pv5
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
storageClassName: "redis-cluster"
nfs:
path: /data/redis-cluster-example/cluster5
server: 192.168.50.167
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: redis-pv6
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
storageClassName: "redis-cluster"
nfs:
path: /data/redis-cluster-example/cluster6
server: 192.168.50.167
EOF
# 执行命令,创建 pv
$ kubectl apply -f pv.yaml
查看 PV 状态
kubectl get pv
创建 NameSpace
这里引入nameSpace 命名空间的好处是,便于集群的管理,类似于标签标记和分组
$ cat <<EOF> /usr/local/soft/k8s/redis-cluster/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: redis-cluster-example
EOF
# 创建执行 命名空间
$ kubectl apply -f namespace.yaml
创建 Headless Service
Headless service是StatefulSet实现稳定网络标识的基础,我们需要提前创建
$ cat <<EOF> /usr/local/soft/k8s/redis-cluster/headless-service.yaml
apiVersion: v1
kind: Service
metadata:
name: redis-cluster
namespace: redis-cluster-example
labels:
app: redis-cluster
spec:
selector:
app: redis-cluster
appCluster: redis-cluster
ports:
- name: redis-cluster
port: 6379
clusterIP: None
EOF
# 执行创建命令 这里指定并且引用了上方定义的 nameSpace
$ kubectl create -f headless-service.yaml
# 查看刚创建的 svc
kubectl get svc -n redis-cluster-example -o wide
创建 Redis 集群节点
$ cat <<EOF> /usr/local/soft/k8s/redis-redis-example/redis-sts.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: redis-cluster
data:
update-node.sh: |
#!/bin/sh
REDIS_NODES="/data/nodes.conf"
sed -i -e "/myself/ s/[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}/${POD_IP}/" ${REDIS_NODES}
exec "[email protected]"
redis.conf: |+
cluster-enabled yes
cluster-require-full-coverage no
cluster-node-timeout 15000
cluster-config-file /data/nodes.conf
cluster-migration-barrier 1
appendonly yes
protected-mode no
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis-cluster
namespace: redis-cluster-example
spec:
serviceName: redis-cluster
replicas: 6
selector:
matchLabels:
app: redis-cluster
namespace: redis-cluster-example
template:
metadata:
labels:
app: redis-cluster
namespace: redis-cluster-example
spec:
hostNetwork: true
containers:
- name: redis
image: redis:5.0.9
ports:
- containerPort: 6379
hostPort: 6379
name: client
- containerPort: 16379
name: gossip
command: ["/conf/update-node.sh", "redis-server", "/conf/redis.conf"]
env:
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
volumeMounts:
- name: conf
mountPath: /conf
readOnly: false
- name: data
mountPath: /data
readOnly: false
volumes:
- name: conf
configMap:
name: redis-cluster
defaultMode: 0755
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 5Gi
storageClassName: redis-cluster
EOF
# 执行创建命令
$ kubectl apply -f redis-sts.yaml
# 查看 redis pod 情况
$ kubectl get pod -n redis-cluster-example -o wide
# 查看 pvc 绑定使用情况
$ kubectl get pvc -n redis-cluster- example -o wide
查看使用情况
初始化 Redis Cluster
因为我们选择的 Redis 版本是大于4.0的,redis-cli 已经集成了部署集群的功能
# 全部复制,一次执行
$ kubectl exec -it redis-cluster-0 -n redis-cluster-example \
-- redis-cli --cluster create \
--cluster-replicas 1 \
$(kubectl get pods -l app=redis-cluster -n redis-cluster-example -o jsonpath='{range.items[*]}{.status.podIP}:6379 ')
执行初始化
查看集群信息
# 连接到 redis-0 容器
$ kubectl exec -it redis-cluster-0 -n redis-cluster-example -- /bin/bash
# 使用 redis-cli 工具连接到任意节点
$ redis-cli -c -p 6379
# 查看集群节点
$ cluster nodes 和 cluster info
查看数据挂载情况
在master2 NFS 上可以看到,持久化文件,使用 tree 树形查看
重启集群,校验数据是否丢失
# 单独删除某一个 pod,集群会自动重建 pod,并且数据可以正常访问
# 如,我们set foo bar ,集群重定向到 192.168.50.224:6379,删除 Ip 对应的Pod,重新 get foo
仔细看下方的图,他解释了所有问题,224号节点删除后,K8s 自动建立的,实现了高可用
$ kubectl delete pod redis-2 -n redis-cluster-example
# 这个命令是强制删除命令,会导致集群数据丢失,切勿随意执行。 但本部署方案,理论上已经规避该操作风险,可以正常执行
$ kubectl replace --force -f redis-sts.yaml
# 在重启物理机后,依然做到自动数据恢复,测试了4次宕机,均可恢复。如确实丢失身份节点,则需要手动添加节点,并分配 slot
还有其他的集群管理命令,就看请我其他的博客文章吧
展望
- 参数调优
- 负载均衡处理
我翻阅了很多资料,Redis Cluster 模式下,很容易出现的问题,是某个节点Cpu 负载过高;以及后续需要通过对请求进行一致性哈希运算,直接让不同的节点,处理固定的请求(类似于 CDN 策略)。这需要对 StateFulSet 模式更深入的了解 - 继续改造,并通过 Helm 进行包管理,和 Chart 化,方便部署
- Rancher化(已完成)
GitHub:基于K8s集群,搭建 redis-cluster 集群