在K8s上部署Redis 集群

雨点打透心脏的1/2处 2022-01-21 22:43 361阅读 0赞

一、前言
架构原理:每个Master都可以拥有多个Slave。当Master下线后,Redis集群会从多个Slave中选举出一个新的Master作为替代,而旧Master重新上线后变成新Master的Slave。

二、准备操作
本次部署主要基于该项目:

  1. https://github.com/zuxqoj/kubernetes-redis-cluster

其包含了两种部署Redis集群的方式:

StatefulSet
Service&Deployment

两种方式各有优劣,对于像Redis、Mongodb、Zookeeper等有状态的服务,使用StatefulSet是首选方式。本文将主要介绍如何使用StatefulSet进行Redis集群的部署。

三、StatefulSet简介
RC、Deployment、DaemonSet都是面向无状态的服务,它们所管理的Pod的IP、名字,启停顺序等都是随机的,而StatefulSet是什么?顾名思义,有状态的集合,管理所有有状态的服务,比如MySQL、MongoDB集群等。
StatefulSet本质上是Deployment的一种变体,在v1.9版本中已成为GA版本,它为了解决有状态服务的问题,它所管理的Pod拥有固定的Pod名称,启停顺序,在StatefulSet中,Pod名字称为网络标识(hostname),还必须要用到共享存储。
在Deployment中,与之对应的服务是service,而在StatefulSet中与之对应的headless service,headless service,即无头服务,与service的区别就是它没有Cluster IP,解析它的名称时将返回该Headless Service对应的全部Pod的Endpoint列表。
除此之外,StatefulSet在Headless Service的基础上又为StatefulSet控制的每个Pod副本创建了一个DNS域名,这个域名的格式为:

  1. $(podname).(headless server name)
  2. FQDN $(podname).(headless server name).namespace.svc.cluster.local

也即是说,对于有状态服务,我们最好使用固定的网络标识(如域名信息)来标记节点,当然这也需要应用程序的支持(如Zookeeper就支持在配置文件中写入主机域名)。
StatefulSet基于Headless Service(即没有Cluster IP的Service)为Pod实现了稳定的网络标志(包括Pod的hostname和DNS Records),在Pod重新调度后也保持不变。同时,结合PV/PVC,StatefulSet可以实现稳定的持久化存储,就算Pod重新调度后,还是能访问到原先的持久化数据。
以下为使用StatefulSet部署Redis的架构,无论是Master还是Slave,都作为StatefulSet的一个副本,并且数据通过PV进行持久化,对外暴露为一个Service,接受客户端请求。

四、部署过程
本文参考项目的README中,简要介绍了基于StatefulSet的Redis创建步骤:

1.创建NFS存储
2.创建PV
3.创建PVC
4.创建Configmap
5.创建headless服务
6.创建Redis StatefulSet
7.初始化Redis集群

这里,我将参考如上步骤,实践操作并详细介绍Redis集群的部署过程。文中会涉及到很多K8S的概念,希望大家能提前了解学习

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

安装NFS

  1. yum -y install nfs-utils(主包提供文件系统)
  2. yum -y install rpcbind(提供rpc协议)

然后,新增/etc/exports文件,用于设置需要共享的路径:

  1. [root@ftp pv3]# cat /etc/exports
  2. /usr/local/k8s/redis/pv1 192.168.0.0/24(rw,sync,no_root_squash)
  3. /usr/local/k8s/redis/pv2 192.168.0.0/24(rw,sync,no_root_squash)
  4. /usr/local/k8s/redis/pv3 192.168.0.0/24(rw,sync,no_root_squash)
  5. /usr/local/k8s/redis/pv4 192.168.0.0/24(rw,sync,no_root_squash)
  6. /usr/local/k8s/redis/pv5 192.168.0.0/24(rw,sync,no_root_squash)
  7. /usr/local/k8s/redis/pv6 192.168.0.0/24(rw,sync,no_root_squash)

2019060413312366.png
创建相应目录

  1. [root@ftp quizii]# mkdir -p /usr/local/k8s/redis/pv{1..6}

接着,启动NFS和rpcbind服务:

  1. systemctl restart rpcbind
  2. systemctl restart nfs
  3. systemctl enable nfs
  4. [root@ftp pv3]# exportfs -v
  5. /usr/local/k8s/redis/pv1
  6. 192.168.0.0/24(sync,wdelay,hide,no_subtree_check,sec=sys,rw,secure,no_root_squash,no_all_squash)
  7. /usr/local/k8s/redis/pv2
  8. 192.168.0.0/24(sync,wdelay,hide,no_subtree_check,sec=sys,rw,secure,no_root_squash,no_all_squash)
  9. /usr/local/k8s/redis/pv3
  10. 192.168.0.0/24(sync,wdelay,hide,no_subtree_check,sec=sys,rw,secure,no_root_squash,no_all_squash)
  11. /usr/local/k8s/redis/pv4
  12. 192.168.0.0/24(sync,wdelay,hide,no_subtree_check,sec=sys,rw,secure,no_root_squash,no_all_squash)
  13. /usr/local/k8s/redis/pv5
  14. 192.168.0.0/24(sync,wdelay,hide,no_subtree_check,sec=sys,rw,secure,no_root_squash,no_all_squash)
  15. /usr/local/k8s/redis/pv6
  16. 192.168.0.0/24(sync,wdelay,hide,no_subtree_check,sec=sys,rw,secure,no_root_squash,no_all_squash)

客户端

  1. yum -y install nfs-utils

查看存储端共享

  1. [root@node2 ~]# showmount -e 192.168.0.222
  2. Export list for 192.168.0.222:
  3. /usr/local/k8s/redis/pv6 192.168.0.0/24
  4. /usr/local/k8s/redis/pv5 192.168.0.0/24
  5. /usr/local/k8s/redis/pv4 192.168.0.0/24
  6. /usr/local/k8s/redis/pv3 192.168.0.0/24
  7. /usr/local/k8s/redis/pv2 192.168.0.0/24
  8. /usr/local/k8s/redis/pv1 192.168.0.0/24

创建PV
每一个Redis Pod都需要一个独立的PV来存储自己的数据,因此可以创建一个pv.yaml文件,包含6个PV:

  1. [root@master redis]# cat pv.yaml
  2. apiVersion: v1
  3. kind: PersistentVolume
  4. metadata:
  5. name: nfs-pv1
  6. spec:
  7. capacity:
  8. storage: 200M
  9. accessModes:
  10. - ReadWriteMany
  11. nfs:
  12. server: 192.168.0.222
  13. path: "/usr/local/k8s/redis/pv1"
  14. ---
  15. apiVersion: v1
  16. kind: PersistentVolume
  17. metadata:
  18. name: nfs-vp2
  19. spec:
  20. capacity:
  21. storage: 200M
  22. accessModes:
  23. - ReadWriteMany
  24. nfs:
  25. server: 192.168.0.222
  26. path: "/usr/local/k8s/redis/pv2"
  27. ---
  28. apiVersion: v1
  29. kind: PersistentVolume
  30. metadata:
  31. name: nfs-pv3
  32. spec:
  33. capacity:
  34. storage: 200M
  35. accessModes:
  36. - ReadWriteMany
  37. nfs:
  38. server: 192.168.0.222
  39. path: "/usr/local/k8s/redis/pv3"
  40. ---
  41. apiVersion: v1
  42. kind: PersistentVolume
  43. metadata:
  44. name: nfs-pv4
  45. spec:
  46. capacity:
  47. storage: 200M
  48. accessModes:
  49. - ReadWriteMany
  50. nfs:
  51. server: 192.168.0.222
  52. path: "/usr/local/k8s/redis/pv4"
  53. ---
  54. apiVersion: v1
  55. kind: PersistentVolume
  56. metadata:
  57. name: nfs-pv5
  58. spec:
  59. capacity:
  60. storage: 200M
  61. accessModes:
  62. - ReadWriteMany
  63. nfs:
  64. server: 192.168.0.222
  65. path: "/usr/local/k8s/redis/pv5"
  66. ---
  67. apiVersion: v1
  68. kind: PersistentVolume
  69. metadata:
  70. name: nfs-pv6
  71. spec:
  72. capacity:
  73. storage: 200M
  74. accessModes:
  75. - ReadWriteMany
  76. nfs:
  77. server: 192.168.0.222
  78. path: "/usr/local/k8s/redis/pv6"

如上,可以看到所有PV除了名称和挂载的路径外都基本一致。执行创建即可:

  1. [root@master redis]#kubectl create -f pv.yaml
  2. persistentvolume "nfs-pv1" created
  3. persistentvolume "nfs-pv2" created
  4. persistentvolume "nfs-pv3" created
  5. persistentvolume "nfs-pv4" created
  6. persistentvolume "nfs-pv5" created
  7. persistentvolume "nfs-pv6" created

2.创建Configmap
这里,我们可以直接将Redis的配置文件转化为Configmap,这是一种更方便的配置读取方式。配置文件redis.conf如下

  1. [root@master redis]# cat redis.conf
  2. appendonly yes
  3. cluster-enabled yes
  4. cluster-config-file /var/lib/redis/nodes.conf
  5. cluster-node-timeout 5000
  6. dir /var/lib/redis
  7. port 6379

创建名为redis-conf的Configmap:

  1. kubectl create configmap redis-conf --from-file=redis.conf

查看创建的configmap:

  1. [root@master redis]# kubectl describe cm redis-conf
  2. Name: redis-conf
  3. Namespace: default
  4. Labels: <none>
  5. Annotations: <none>
  6. Data
  7. ====
  8. redis.conf:
  9. ----
  10. appendonly yes
  11. cluster-enabled yes
  12. cluster-config-file /var/lib/redis/nodes.conf
  13. cluster-node-timeout 5000
  14. dir /var/lib/redis
  15. port 6379
  16. Events: <none>

如上,redis.conf中的所有配置项都保存到redis-conf这个Configmap中。

3.创建Headless service
Headless service是StatefulSet实现稳定网络标识的基础,我们需要提前创建。准备文件headless-service.yml如下:

  1. [root@master redis]# cat headless-service.yaml
  2. apiVersion: v1
  3. kind: Service
  4. metadata:
  5. name: redis-service
  6. labels:
  7. app: redis
  8. spec:
  9. ports:
  10. - name: redis-port
  11. port: 6379
  12. clusterIP: None
  13. selector:
  14. app: redis
  15. appCluster: redis-cluster

创建:

  1. kubectl create -f headless-service.yml

查看:
在这里插入图片描述
可以看到,服务名称为redis-service,其CLUSTER-IP为None,表示这是一个“无头”服务。

4.创建Redis 集群节点
创建好Headless service后,就可以利用StatefulSet创建Redis 集群节点,这也是本文的核心内容。我们先创建redis.yml文件:

  1. [root@master redis]# cat redis.yaml
  2. apiVersion: apps/v1beta1
  3. kind: StatefulSet
  4. metadata:
  5. name: redis-app
  6. spec:
  7. serviceName: "redis-service"
  8. replicas: 6
  9. template:
  10. metadata:
  11. labels:
  12. app: redis
  13. appCluster: redis-cluster
  14. spec:
  15. terminationGracePeriodSeconds: 20
  16. affinity:
  17. podAntiAffinity:
  18. preferredDuringSchedulingIgnoredDuringExecution:
  19. - weight: 100
  20. podAffinityTerm:
  21. labelSelector:
  22. matchExpressions:
  23. - key: app
  24. operator: In
  25. values:
  26. - redis
  27. topologyKey: kubernetes.io/hostname
  28. containers:
  29. - name: redis
  30. image: redis
  31. command:
  32. - "redis-server"
  33. args:
  34. - "/etc/redis/redis.conf"
  35. - "--protected-mode"
  36. - "no"
  37. resources:
  38. requests:
  39. cpu: "100m"
  40. memory: "100Mi"
  41. ports:
  42. - name: redis
  43. containerPort: 6379
  44. protocol: "TCP"
  45. - name: cluster
  46. containerPort: 16379
  47. protocol: "TCP"
  48. volumeMounts:
  49. - name: "redis-conf"
  50. mountPath: "/etc/redis"
  51. - name: "redis-data"
  52. mountPath: "/var/lib/redis"
  53. volumes:
  54. - name: "redis-conf"
  55. configMap:
  56. name: "redis-conf"
  57. items:
  58. - key: "redis.conf"
  59. path: "redis.conf"
  60. volumeClaimTemplates:
  61. - metadata:
  62. name: redis-data
  63. spec:
  64. accessModes: [ "ReadWriteMany" ]
  65. resources:
  66. requests:
  67. storage: 200M

如上,总共创建了6个Redis节点(Pod),其中3个将用于master,另外3个分别作为master的slave;Redis的配置通过volume将之前生成的redis-conf这个Configmap,挂载到了容器的/etc/redis/redis.conf;Redis的数据存储路径使用volumeClaimTemplates声明(也就是PVC),其会绑定到我们先前创建的PV上。
这里有一个关键概念——Affinity,请参考官方文档详细了解。其中,podAntiAffinity表示反亲和性,其决定了某个pod不可以和哪些Pod部署在同一拓扑域,可以用于将一个服务的POD分散在不同的主机或者拓扑域中,提高服务本身的稳定性。
而PreferredDuringSchedulingIgnoredDuringExecution 则表示,在调度期间尽量满足亲和性或者反亲和性规则,如果不能满足规则,POD也有可能被调度到对应的主机上。在之后的运行过程中,系统不会再检查这些规则是否满足。
在这里,matchExpressions规定了Redis Pod要尽量不要调度到包含app为redis的Node上,也即是说已经存在Redis的Node上尽量不要再分配Redis Pod了。但是,由于我们只有三个Node,而副本有6个,因此根据PreferredDuringSchedulingIgnoredDuringExecution,这些豌豆不得不得挤一挤,挤挤更健康~
另外,根据StatefulSet的规则,我们生成的Redis的6个Pod的hostname会被依次命名为 $(statefulset名称)-$(序号) 如下图所示:

  1. [root@master redis]# kubectl get pods -o wide
  2. NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE
  3. redis-app-0 1/1 Running 0 2h 172.17.24.3 192.168.0.144 <none>
  4. redis-app-1 1/1 Running 0 2h 172.17.63.8 192.168.0.148 <none>
  5. redis-app-2 1/1 Running 0 2h 172.17.24.8 192.168.0.144 <none>
  6. redis-app-3 1/1 Running 0 2h 172.17.63.9 192.168.0.148 <none>
  7. redis-app-4 1/1 Running 0 2h 172.17.24.9 192.168.0.144 <none>
  8. redis-app-5 1/1 Running 0 2h 172.17.63.10 192.168.0.148 <none>

如上,可以看到这些Pods在部署时是以{0…N-1}的顺序依次创建的。注意,直到redis-app-0状态启动后达到Running状态之后,redis-app-1 才开始启动。
同时,每个Pod都会得到集群内的一个DNS域名,格式为$(podname).$(service name).$(namespace).svc.cluster.local ,也即是:

  1. redis-app-0.redis-service.default.svc.cluster.local
  2. redis-app-1.redis-service.default.svc.cluster.local
  3. ...以此类推...

在K8S集群内部,这些Pod就可以利用该域名互相通信。我们可以使用busybox镜像的nslookup检验这些域名:

  1. [root@master redis]# kubectl exec -ti busybox -- nslookup redis-app-0.redis-service
  2. Server: 10.0.0.2
  3. Address 1: 10.0.0.2 kube-dns.kube-system.svc.cluster.local
  4. Name: redis-app-0.redis-service
  5. Address 1: 172.17.24.3

可以看到, redis-app-0的IP为172.17.24.3。当然,若Redis Pod迁移或是重启(我们可以手动删除掉一个Redis Pod来测试),IP是会改变的,但是Pod的域名、SRV records、A record都不会改变。

另外可以发现,我们之前创建的pv都被成功绑定了:

  1. [root@master redis]# kubectl get pv
  2. NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
  3. nfs-pv1 200M RWX Retain Bound default/redis-data-redis-app-2 3h
  4. nfs-pv3 200M RWX Retain Bound default/redis-data-redis-app-4 3h
  5. nfs-pv4 200M RWX Retain Bound default/redis-data-redis-app-5 3h
  6. nfs-pv5 200M RWX Retain Bound default/redis-data-redis-app-1 3h
  7. nfs-pv6 200M RWX Retain Bound default/redis-data-redis-app-0 3h
  8. nfs-vp2 200M RWX Retain Bound default/redis-data-redis-app-3 3h

5.初始化Redis集群

创建好6个Redis Pod后,我们还需要利用常用的Redis-tribe工具进行集群的初始化

创建Ubuntu容器
由于Redis集群必须在所有节点启动后才能进行初始化,而如果将初始化逻辑写入Statefulset中,则是一件非常复杂而且低效的行为。这里,本人不得不称赞一下原项目作者的思路,值得学习。也就是说,我们可以在K8S上创建一个额外的容器,专门用于进行K8S集群内部某些服务的管理控制。
这里,我们专门启动一个Ubuntu的容器,可以在该容器中安装Redis-tribe,进而初始化Redis集群,执行:

  1. kubectl run -it ubuntu --image=ubuntu --restart=Never /bin/bash

我们使用阿里云的Ubuntu源,执行:

  1. root@ubuntu:/# cat > /etc/apt/sources.list << EOF
  2. deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse
  3. deb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse
  4. deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse
  5. deb-src http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse
  6. deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse
  7. deb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse
  8. deb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse
  9. deb-src http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse
  10. deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse
  11. deb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse
  12. > EOF

成功后,原项目要求执行如下命令安装基本的软件环境:

  1. apt-get update
  2. apt-get install -y vim wget python2.7 python-pip redis-tools dnsutils

初始化集群
首先,我们需要安装redis-trib

  1. pip install redis-trib==0.5.1

然后,创建只有Master节点的集群:

  1. redis-trib.py create \
  2. `dig +short redis-app-0.redis-service.default.svc.cluster.local`:6379 \
  3. `dig +short redis-app-1.redis-service.default.svc.cluster.local`:6379 \
  4. `dig +short redis-app-2.redis-service.default.svc.cluster.local`:6379

其次,为每个Master添加Slave

  1. redis-trib.py replicate \
  2. --master-addr `dig +short redis-app-0.redis-service.default.svc.cluster.local`:6379 \
  3. --slave-addr `dig +short redis-app-3.redis-service.default.svc.cluster.local`:6379
  4. redis-trib.py replicate \
  5. --master-addr `dig +short redis-app-1.redis-service.default.svc.cluster.local`:6379 \
  6. --slave-addr `dig +short redis-app-4.redis-service.default.svc.cluster.local`:6379
  7. redis-trib.py replicate \
  8. --master-addr `dig +short redis-app-2.redis-service.default.svc.cluster.local`:6379 \
  9. --slave-addr `dig +short redis-app-5.redis-service.default.svc.cluster.local`:6379

至此,我们的Redis集群就真正创建完毕了,连到任意一个Redis Pod中检验一下:

  1. [root@master redis]# kubectl exec -it redis-app-2 /bin/bash
  2. root@redis-app-2:/data# /usr/local/bin/redis-cli -c
  3. 127.0.0.1:6379> cluster nodes
  4. 5d3e77f6131c6f272576530b23d1cd7592942eec 172.17.24.3:6379@16379 master - 0 1559628533000 1 connected 0-5461
  5. a4b529c40a920da314c6c93d17dc603625d6412c 172.17.63.10:6379@16379 master - 0 1559628531670 6 connected 10923-16383
  6. 368971dc8916611a86577a8726e4f1f3a69c5eb7 172.17.24.9:6379@16379 slave 0025e6140f85cb243c60c214467b7e77bf819ae3 0 1559628533672 4 connected
  7. 0025e6140f85cb243c60c214467b7e77bf819ae3 172.17.63.8:6379@16379 master - 0 1559628533000 2 connected 5462-10922
  8. 6d5ee94b78b279e7d3c77a55437695662e8c039e 172.17.24.8:6379@16379 myself,slave a4b529c40a920da314c6c93d17dc603625d6412c 0 1559628532000 5 connected
  9. 2eb3e06ce914e0e285d6284c4df32573e318bc01 172.17.63.9:6379@16379 slave 5d3e77f6131c6f272576530b23d1cd7592942eec 0 1559628533000 3 connected
  10. 127.0.0.1:6379> cluster info
  11. cluster_state:ok
  12. cluster_slots_assigned:16384
  13. cluster_slots_ok:16384
  14. cluster_slots_pfail:0
  15. cluster_slots_fail:0
  16. cluster_known_nodes:6
  17. cluster_size:3
  18. cluster_current_epoch:6
  19. cluster_my_epoch:6
  20. cluster_stats_messages_ping_sent:14910
  21. cluster_stats_messages_pong_sent:15139
  22. cluster_stats_messages_sent:30049
  23. cluster_stats_messages_ping_received:15139
  24. cluster_stats_messages_pong_received:14910
  25. cluster_stats_messages_received:30049
  26. 127.0.0.1:6379>

另外,还可以在NFS上查看Redis挂载的数据:

  1. [root@ftp pv3]# ll /usr/local/k8s/redis/pv3
  2. total 12
  3. -rw-r--r-- 1 root root 92 Jun 4 11:36 appendonly.aof
  4. -rw-r--r-- 1 root root 175 Jun 4 11:36 dump.rdb
  5. -rw-r--r-- 1 root root 794 Jun 4 11:49 nodes.conf

6.创建用于访问Service
前面我们创建了用于实现StatefulSet的Headless Service,但该Service没有Cluster Ip,因此不能用于外界访问。所以,我们还需要创建一个Service,专用于为Redis集群提供访问和负载均衡:

  1. [root@master redis]# cat redis-access-service.yaml
  2. apiVersion: v1
  3. kind: Service
  4. metadata:
  5. name: redis-access-service
  6. labels:
  7. app: redis
  8. spec:
  9. ports:
  10. - name: redis-port
  11. protocol: "TCP"
  12. port: 6379
  13. targetPort: 6379
  14. selector:
  15. app: redis
  16. appCluster: redis-cluster

如上,该Service名称为 redis-access-service,在K8S集群中暴露6379端口,并且会对labels nameapp: redisappCluster: redis-cluster的pod进行负载均衡。

创建后查看:

  1. [root@master redis]# kubectl get svc redis-access-service -o wide
  2. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
  3. redis-access-service ClusterIP 10.0.0.64 <none> 6379/TCP 2h app=redis,appCluster=redis-cluster

如上,在K8S集群中,所有应用都可以通过10.0.0.64 :6379来访问Redis集群。当然,为了方便测试,我们也可以为Service添加一个NodePort映射到物理机上,这里不再详细介绍。

五、测试主从切换
在K8S上搭建完好Redis集群后,我们最关心的就是其原有的高可用机制是否正常。这里,我们可以任意挑选一个Master的Pod来测试集群的主从切换机制,如redis-app-0

  1. [root@master redis]# kubectl get pods redis-app-0 -o wide
  2. NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE
  3. redis-app-1 1/1 Running 0 3h 172.17.24.3 192.168.0.144 <none>

进入redis-app-0查看:

  1. [root@master redis]# kubectl exec -it redis-app-0 /bin/bash
  2. root@redis-app-0:/data# /usr/local/bin/redis-cli -c
  3. 127.0.0.1:6379> role
  4. 1) "master"
  5. 2) (integer) 13370
  6. 3) 1) 1) "172.17.63.9"
  7. 2) "6379"
  8. 3) "13370"
  9. 127.0.0.1:6379>

如上可以看到,app-0为master,slave为172.17.63.9redis-app-3

接着,我们手动删除redis-app-0

  1. [root@master redis]# kubectl delete pod redis-app-0
  2. pod "redis-app-0" deleted
  3. [root@master redis]# kubectl get pod redis-app-0 -o wide
  4. NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE
  5. redis-app-0 1/1 Running 0 4m 172.17.24.3 192.168.0.144 <none>

我们再进入redis-app-0内部查看:

  1. [root@master redis]# kubectl exec -it redis-app-0 /bin/bash
  2. root@redis-app-0:/data# /usr/local/bin/redis-cli -c
  3. 127.0.0.1:6379> role
  4. 1) "slave"
  5. 2) "172.17.63.9"
  6. 3) (integer) 6379
  7. 4) "connected"
  8. 5) (integer) 13958

如上,redis-app-0变成了slave,从属于它之前的从节点172.17.63.9redis-app-3

六、疑问
至此,大家可能会疑惑,那为什么没有使用稳定的标志,Redis Pod也能正常进行故障转移呢?这涉及了Redis本身的机制。因为,Redis集群中每个节点都有自己的NodeId(保存在自动生成的nodes.conf中),并且该NodeId不会随着IP的变化和变化,这其实也是一种固定的网络标志。也就是说,就算某个Redis Pod重启了,该Pod依然会加载保存的NodeId来维持自己的身份。我们可以在NFS上查看redis-app-1的nodes.conf文件:

[root@k8s-node2 ~]# cat /usr/local/k8s/redis/pv1/nodes.conf 96689f2018089173e528d3a71c4ef10af68ee462 192.168.169.209:6379@16379 slave d884c4971de9748f99b10d14678d864187a9e5d3 0 1526460952651 4 connected237d46046d9b75a6822f02523ab894928e2300e6 192.168.169.200:6379@16379 slave c15f378a604ee5b200f06cc23e9371cbc04f4559 0 1526460952651 1 connected
c15f378a604ee5b200f06cc23e9371cbc04f4559 192.168.169.197:6379@16379 master - 0 1526460952651 1 connected 10923-16383d884c4971de9748f99b10d14678d864187a9e5d3 192.168.169.205:6379@16379 master - 0 1526460952651 4 connected 5462-10922c3b4ae23c80ffe31b7b34ef29dd6f8d73beaf85f 192.168.169.198:6379@16379 myself,slave c8a8f70b4c29333de6039c47b2f3453ed11fb5c2 0 1526460952565 3 connected
c8a8f70b4c29333de6039c47b2f3453ed11fb5c2 192.168.169.201:6379@16379 master - 0 1526460952651 6 connected 0-5461vars currentEpoch 6 lastVoteEpoch 4
如上,第一列为NodeId,稳定不变;第二列为IP和端口信息,可能会改变。

这里,我们介绍NodeId的两种使用场景:

当某个Slave Pod断线重连后IP改变,但是Master发现其NodeId依旧, 就认为该Slave还是之前的Slave。

当某个Master Pod下线后,集群在其Slave中选举重新的Master。待旧Master上线后,集群发现其NodeId依旧,会让旧Master变成新Master的slave。

对于这两种场景,大家有兴趣的话还可以自行测试,注意要观察Redis的日志。

发表评论

表情:
评论列表 (有 0 条评论,361人围观)

还没有评论,来说两句吧...

相关阅读

    相关 k8s部署redis

    写在前面 一般来说,REDIS部署有三种模式。 1. 单实例模式,一般用于测试环境。 2. 哨兵模式 3. 集群模式 后两者用于生产部署 `哨兵模式` >

    相关 K8S部署

    一、利用ansible部署kubernetes准备: 集群介绍 本系列文档致力于提供快速部署高可用k8s集群的工具,并且也努力成为k8s实践、使用的参考书;基于二进