Kubernetes 部署 Redis-Cluster

海叶秋
2023-12-01

第六章 Kubernetes 部署 Redis-Cluster

主要处理问题:
解决 Pod 重启之后,IP 发生变化。
使用 Redis-cluster-Proxy 解决 Redis-Cluster 外部不能访问问题。



一、K8s 部署 redis-cluster 所面临的问题及方案

  • 持久化,存储数据 k8s 采用 StatefulSet(有状态服务部署)
PVC的方式进行持久化处理。
  • Pod 重启后 IP 发生变化,导致集群不可用(重点)
redis 启动时指定 IP。
CNI:容器网络接口,主要利用calico组件的两个kubernetes注解:
cni.projectcalico.org/ipAddrs; 注释:单个 pod 固定 IP。
cni.projectcalico.org/ipv4pools。 注释:多个 pod 固定 IP 地址池 。
  • Redis-Cluster 对 k8s 集群内部提供访问通过发布 Service 服务,对外部如何提供访问(重点)。
因 Redis Cluster 请求时,会自动重定向:
通过 NodePort 对外发布 Redis 服务,但是在 K8S 外部也无法连接 Redis PodIP。因此需要通过 redis-cluster-proxy 来实现代理访问。 

二、部署 Redis-Cluster 方式

1.创建 NFS 文件系统

创建 NFS 文件存储主要是为了 Redis-Cluster 集群提供稳定的后端存储,当Redis的Pod重启或迁移后,依然能获得原先的数据。这里,我们先要创建NFS,然后通过使用PV为 Redis-Cluster 挂载一个远程的NFS路径。

### 下载 
yum install -y rpcbind nfs-utils
### 创建需要共享的文件夹
mkdir -p /nfs/k8s/redis/pv{1..9}
### 填写配置文件
[root@NFS-harbor ~]# cat /etc/exports
/nfs/k8s/redis/pv1 192.168.1.0/24(rw,sync,no_root_squash)
/nfs/k8s/redis/pv2 192.168.1.0/24(rw,sync,no_root_squash)
/nfs/k8s/redis/pv3 192.168.1.0/24(rw,sync,no_root_squash)
/nfs/k8s/redis/pv4 192.168.1.0/24(rw,sync,no_root_squash)
/nfs/k8s/redis/pv5 192.168.1.0/24(rw,sync,no_root_squash)
/nfs/k8s/redis/pv6 192.168.1.0/24(rw,sync,no_root_squash)
/nfs/k8s/redis/pv7 192.168.1.0/24(rw,sync,no_root_squash)
/nfs/k8s/redis/pv8 192.168.1.0/24(rw,sync,no_root_squash)
/nfs/k8s/redis/pv9 192.168.1.0/24(rw,sync,no_root_squash)
### 启动 NFS
systemctl start rpcbind
systemctl start nfs
### 本地测试
[root@NFS-harbor ~]# showmount -e 127.0.0.1
Export list for 127.0.0.1:
/nfs/k8s/redis/pv9 192.168.1.0/24
/nfs/k8s/redis/pv8 192.168.1.0/24
/nfs/k8s/redis/pv7 192.168.1.0/24
/nfs/k8s/redis/pv6 192.168.1.0/24
/nfs/k8s/redis/pv5 192.168.1.0/24
/nfs/k8s/redis/pv4 192.168.1.0/24
/nfs/k8s/redis/pv3 192.168.1.0/24
/nfs/k8s/redis/pv2 192.168.1.0/24
/nfs/k8s/redis/pv1 192.168.1.0/24

2.创建 PV 存储卷

我这里创建了 9 个 PV 卷,这里不做描述:

[wyh@k8s-master-01 redis]$ cat pv.yaml 
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv1
spec:
  capacity: 
    storage: 300M
  accessModes:
    - ReadWriteOnce
  nfs:
    server: 192.168.1.88
    path: "/nfs/k8s/redis/pv1"

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv2
spec:
  capacity:
    storage: 300M
  accessModes:
    - ReadWriteOnce
  nfs:
    server: 192.168.1.88
    path: "/nfs/k8s/redis/pv2"

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv3
spec:
  capacity:
    storage: 300M
  accessModes:
    - ReadWriteOnce
  nfs:
    server: 192.168.1.88
    path: "/nfs/k8s/redis/pv3"

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv4
spec:
  capacity:
    storage: 300M
  accessModes:
    - ReadWriteOnce
  nfs:
    server: 192.168.1.88
    path: "/nfs/k8s/redis/pv4"

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv5
spec:
  capacity:
    storage: 300M
  accessModes:
    - ReadWriteOnce
  nfs:
    server: 192.168.1.88
    path: "/nfs/k8s/redis/pv5"

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv6
spec:
  capacity:
    storage: 300M
  accessModes:
    - ReadWriteOnce
  nfs:
    server: 192.168.1.88
    path: "/nfs/k8s/redis/pv6"

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv7
spec:
  capacity:
    storage: 300M
  accessModes:
    - ReadWriteOnce
  nfs:
    server: 192.168.1.88
    path: "/nfs/k8s/redis/pv7"

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv8
spec:
  capacity:
    storage: 300M
  accessModes:
    - ReadWriteOnce
  nfs:
    server: 192.168.1.88
    path: "/nfs/k8s/redis/pv8"


---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv9
spec:
  capacity:
    storage: 300M
  accessModes:
    - ReadWriteOnce
  nfs:
    server: 192.168.1.88
    path: "/nfs/k8s/redis/pv9"

3.创建 Configmap 配置文件

请看一下我注释的部分,是网上的通过脚本来解决 POD IP 变更的问题,只解决了本身的 POD IP,而集群连接还是没有解决,我这里有自定义的名称空间,请注意!

[wyh@k8s-master-01 redis_6.2.4]$ cat ConfigMap.yaml 
apiVersion: v1
kind: ConfigMap
metadata:
  name: redis-cluster
  namespace: judicial
  labels:
    app: redis-cluster
data:
#  fix-ip.sh: |
#    #!/bin/sh
#    CLUSTER_CONFIG="/data/nodes.conf"
#    if [ -f ${CLUSTER_CONFIG} ]; then
#      if [ -z "${POD_IP}" ]; then 
#        echo "Unable to determine Pod IP address!"
#        exit 1
#      fi
#      echo "Updating my IP to ${POD_IP} in ${CLUSTER_CONFIG}"
#      sed -i.bak -e "/myself/ s/[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}/${POD_IP}/" ${CLUSTER_CONFIG}
#    fi
#    exec "$@"
  redis.conf: |+
    ### 绑定IP
    bind 0.0.0.0  
    ### 绑定端口
    port 6379    
    ### 限制内存大小
    maxmemory 1GB    
    ### 启用保护模式
    protected-mode yes
    ### 启用密码认证
    ### 主要是针对master对应的slave节点设置的,在slave节点数据同步的时候用到
    masterauth 123456  
    ### 启用密码认证
    ### 对登录权限做限制,redis每个节点的requirepass可以是独立、不同的
    requirepass 123456
    logfile /var/log/redis.log  
    appendonly yes
    daemonize yes
    # 启用Cluster
    cluster-enabled yes
    cluster-config-file /data/nodes.conf
    cluster-migration-barrier 1
    cluster-require-full-coverage no
    cluster-node-timeout 500000

4.创建 StatefulSet 服务 Redis-Cluster

着重看一下 POD 固定 IP 的配置文件,这里使主要利用calico组件的两个。
后边我会单独写一期 calico 设置 IP 地址池的文档去介绍如何实现。

cni.projectcalico.org/ipAddrs :指定 pod ip地址
cni.projectcalico.org/ipv4pools :多个 pod 时 固定IP池

---
apiVersion: v1
kind: Service
metadata:
  name: redis-cluster
  namespace: judicial
  labels:
    app: redis-cluster
spec:
  ports:
  - port: 6379
    targetPort: 6379
    name: client
  - port: 16379
    targetPort: 16379
    name: gossip
  clusterIP: None
  selector:
    app: redis-cluster
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis-cluster
  namespace: judicial
  labels:
    app: redis-cluster
spec:
  serviceName: redis-cluster
  replicas: 6
  selector:
    matchLabels:
      app: redis-cluster
  template:
    metadata:
      labels:
        app: redis-cluster
      ### 指定 pod ip地址
      annotations:
        ### 指定单个 pod ip 地址
        #"cni.projectcalico.org/ipAddrs": "[\"10.96.1.100\"]"
        ### 设置多pod固定IP池,这里部署两个IP地址池,固定为六个可用IP,防止 Pod 重启之后,pod ip地址变化,导致 redis 集群连接失败
        "cni.projectcalico.org/ipv4pools": "[\"new-pool1\",\"new-pool2\"]"
    spec:
      containers:
      - name: redis
        image: 192.168.1.88/redis/redis:6.2.4
        ports:
        - containerPort: 6379
          name: client
        - containerPort: 16379
          name: gossip
        command: 
        #- "/conf/fix-ip.sh"
        - "redis-server"
        args:
        - "/conf/redis.conf"
        - "--cluster-announce-ip"
        - "$(POD_IP)"
        env:
        - name: POD_IP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        ### 就绪指针探测
        readinessProbe:
          exec:
            command:
            - sh
            - -c
            - "redis-cli -h $(hostname) ping"
          ### 用来表示初始化延迟的时间,也就是告诉监测从多久之后开始运行,单位是秒
          initialDelaySeconds: 15
          ### 用来表示监测的超时时间,如果超过这个时长后,则认为监测失败
          timeoutSeconds: 5
        livenessProbe:
          exec:
            command:
            - sh
            - -c
            - "redis-cli -h $(hostname) ping"
          initialDelaySeconds: 20
          periodSeconds: 3
        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
      labels:
        name: redis-cluster
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 300M

5.初始化 Redis-Cluster

使用 正则过滤出想要的 IP 地址及端口,这个我采用六个节点三主三从的方式进行创建集群。

### -a 后面跟密码
kubectl exec -it redis-cluster-0 -- redis-cli --cluster create --cluster-replicas 1 -a 123456 $((kubectl get pods -l app=redis-cluster -o jsonpath='{range.items[*]}{.status.podIP}:6379 ') | awk '{print $1" "$2" "$3" "$4" "$5" "$6" "}')

6.部署Redis-Cluster-Proxy提供代理

主要实现通过 Redis-Cluster-Proxy 中间件如何实现在集群外部对 Redis-Cluster 进行访问。
这里需要强调一下,由于 Redis-Cluster-Proxy 目前比较新,目前官网还没有一个正式版本可以使用,这里我是从 Docker Hub 上下载了一个进行使用,这里不在做演示,自己也可以自定义 Dockerfile 去制作镜像。配置文件这里我指定的是我的 POD IP。

1.Config 配置文件

[wyh@k8s-master-01 redis-cluster-proxy]$ cat configmap.yaml 
apiVersion: v1
kind: ConfigMap
metadata:
  name: redis-proxy
  namespace: judicial
data:
  proxy.conf: |
    #cluster   redis-cluster:6379
    cluster   10.10.10.0:6379
    cluster   10.10.10.1:6379
    cluster   10.10.10.2:6379
    cluster   10.10.10.3:6379
    cluster   10.10.10.4:6379
    cluster   10.10.10.5:6379
    # 配置为Redis Cluster Service
    bind 0.0.0.0
    # redis-cluster-proxy 对外暴露端口
    port 7777   
    # 线程数量
    threads 8   
    daemonize no
    enable-cross-slot yes    
    # 配置Redis Cluster 认证密码  
    auth GuoLian@2021
    auth-user default
    #log-level error

2.创建 Deployment 服务和 SVC 服务对外提供访问

[wyh@k8s-master-01 redis-cluster-proxy]$ cat Deployment.yaml 

---
# Redis-Proxy NodePort

apiVersion: v1
kind: Service
metadata:
  name: redis-proxy
  namespace: judicial
spec:
  type: NodePort # 对K8S外部提供服务
  ports:
  - name: redis-proxy
    nodePort: 30001   # 对外提供的端口
    port: 7777
    protocol: TCP
    targetPort: 7777
  selector:
    app: redis-proxy
---
# Redis-Proxy Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-proxy
  namespace: judicial
spec:
  replicas: 2
  selector:
    matchLabels:
      app: redis-proxy
  template:
    metadata:
      labels:
        app: redis-proxy
    spec:
      imagePullSecrets:
        - name: harbor
      containers:
        - name: redis-proxy
          image: 192.168.1.88/redis/redis-cluster-proxy:v2
          imagePullPolicy: Always
          command: ["redis-cluster-proxy"]
          args:
            - -c
            - /data/proxy.conf   # 指定启动配置文件
          ports:
            - name: redis-7777
              containerPort: 7777
              protocol: TCP
          volumeMounts:
            - name: redis-proxy-conf
              mountPath: /data/
      volumes:   # 挂载proxy配置文件
        - name: redis-proxy-conf
          configMap:
            name: redis-proxy

3.测试访问

这里测试连接登录,以及写入数据查看数据。

[root@glzx02 ~]# redis-cli -h 192.168.1.80 -p 30001 -c
192.168.1.80:30001> set aaa 123
OK
192.168.1.80:30001> get aaa
"123"


总结

主要对 POD IP 重启改变和 Redis-Cluster-Proxy 代理进行说明:

  • POD IP 虽然使用了地址池的方式进行了处理,但是在遇到机房断电,或者 K8S 集群重启时,还是会偶尔出现某些 POD连接不上集群的情况,我这里只是对 地址池进行了限制,限制可使用 IP 为 6 个,但是当 Redis-Cluster集群重启的时候,它是根据配置文件 node.conf 去连接集群的,虽然 IP 进行了限制,但是 POD 重启后 IP 还是会在这 6个 IP 中去变化,就会导致在连接集群时出现问题,但是不会影响使用,这时只需要将有问题的 POD 重启即可,到时候需要观察集群状态。下边做一下演示,根据出现问题进行处理,此问题虽为不经常出现,但也需要注意。
### 删除集群重新启动
[wyh@k8s-master-01 redis_6.2.4]$ kubectl delete -f redis-cluster.yaml 
service "redis-cluster" deleted
statefulset.apps "redis-cluster" deleted
[wyh@k8s-master-01 redis_6.2.4]$ kubectl apply -f redis-cluster.yaml
service/redis-cluster created
statefulset.apps/redis-cluster created
### 查看集群状态是否正常
[wyh@k8s-master-01 redis_6.2.4]$ kubectl exec -it redis-cluster-0 bash
root@redis-cluster-0:/data# redis-cli -c
127.0.0.1:6379> CLUSTER nodes
4b129009613ded445c84db21a89836f58d6caddc 10.10.10.2:6379@16379 slave 774e53350b385b1c1a453792d7dc74334748ab06 0 1629380407000 3 connected
2d392e5e21af88f28af044cd6f1ca748a67c02c3 10.10.10.1:6379@16379 master - 0 1629380407550 2 connected 5461-10922
c83440702852c535be4da7a76f06c299f5d84c88 :0@0 slave,noaddr 2d392e5e21af88f28af044cd6f1ca748a67c02c3 1629380242083 1629380242083 2 disconnected
8366a9f40a8a46f830ae418b83bbbbfbba2afbce 10.10.10.3:6379@16379 slave 81176db06cf2dd7227a492443b3ed2e49062ef40 0 1629380406546 1 connected
774e53350b385b1c1a453792d7dc74334748ab06 10.10.10.0:6379@16379 master - 0 1629380406000 3 connected 10923-16383
81176db06cf2dd7227a492443b3ed2e49062ef40 10.10.10.5:6379@16379 myself,master - 0 1629380404000 1 connected 0-5460
### 此时查看 有一个 slave 节点处于连接中断状态,此时集群仍可正常使用,将有问题的节点删除集群,并重启此 Pod
127.0.0.1:6379> CLUSTER FORGET c83440702852c535be4da7a76f06c299f5d84c88
OK

### 接下来查找出对应有问题的 pod ,进行删除重建
[wyh@k8s-master-01 redis_6.2.4]$ kubectl get pods -o wide
NAME                           READY   STATUS    RESTARTS   AGE     IP               NODE          NOMINATED NODE   READINESS GATES
redis-cluster-0                1/1     Running   0          6m24s   10.10.10.5       k8s-work-05   <none>           <none>
redis-cluster-1                1/1     Running   0          6m1s    10.10.10.1       k8s-work-03   <none>           <none>
redis-cluster-2                1/1     Running   0          5m37s   10.10.10.0       k8s-work-01   <none>           <none>
redis-cluster-3                1/1     Running   0          5m18s   10.10.10.2       k8s-work-04   <none>           <none>
redis-cluster-4                1/1     Running   0          4m57s   10.10.10.3       k8s-work-02   <none>           <none>
redis-cluster-5                1/1     Running   0          4m34s   10.10.10.4       k8s-work-06   <none>           <none>
### 由上图可知,为 redis-cluster-5,对其进行删除,k8s 会对其重新创建
[wyh@k8s-master-01 redis_6.2.4]$ kubectl delete pod redis-cluster-5
pod "redis-cluster-5" deleted
### 创建完成之后,节点会根据配置文件加入集群
### 此时查看集群状态正常
127.0.0.1:6379> CLUSTER nodes
4b129009613ded445c84db21a89836f58d6caddc 10.10.10.2:6379@16379 slave 774e53350b385b1c1a453792d7dc74334748ab06 0 1629381013792 3 connected
c83440702852c535be4da7a76f06c299f5d84c88 10.10.10.4:6379@16379 slave 2d392e5e21af88f28af044cd6f1ca748a67c02c3 0 1629381014796 2 connected
2d392e5e21af88f28af044cd6f1ca748a67c02c3 10.10.10.1:6379@16379 master - 0 1629381015801 2 connected 5461-10922
8366a9f40a8a46f830ae418b83bbbbfbba2afbce 10.10.10.3:6379@16379 slave 81176db06cf2dd7227a492443b3ed2e49062ef40 0 1629381014000 1 connected
774e53350b385b1c1a453792d7dc74334748ab06 10.10.10.0:6379@16379 master - 0 1629381013000 3 connected 10923-16383
81176db06cf2dd7227a492443b3ed2e49062ef40 10.10.10.5:6379@16379 myself,master - 0 1629381014000 1 connected 0-5460
  • 最后就是 Redis-Cluster-Proxy 代理的问题,根据网上查找的部署方案总结,在使用 IP 的时候是可以实现代理的,但是使用 K8S 中的域名进行代理时就会失败,目前还没有找达到原因,索性就把 Redis-Cluster 集群中所有的节点 都写入配置文件中,因为我这里固定的 IP 地址池,始终都会是这 6 个 IP,所有也不影响使用。
	cluster   10.10.10.0:6379
    cluster   10.10.10.1:6379
    cluster   10.10.10.2:6379
    cluster   10.10.10.3:6379
    cluster   10.10.10.4:6379
    cluster   10.10.10.5:6379

以上就是我这期的内容,如果大家有更好的解决方案,欢迎大家一起来探讨解答。谢谢!

 类似资料: