如何在K8s上部署Redis集群

分类:编程技术 时间:2024-02-20 15:23 浏览:0 评论:0
0
本文将详细讲解如何在K8s上部署Redis集群。小编觉得很实用,所以分享给大家,作为参考。希望您读完本文后有所收获。

1.前言
架构原理:每个Master可以有多个Slave。当Master离线时,Redis集群会从多个Slave中选出一个新的Master作为替代,老Master上线后会成为新Master的Slave。

2.准备工作
本次部署主要基于这个项目:

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

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

StatefulSet
Service&Deployment

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

3. StatefulSet简介
RC、Deployment、DaemonSet都是无状态服务。它们管理的Pod的IP、名称、启动和停止顺序都是随机的,什么是StatefulSet?顾名思义,有状态集合管理所有有状态服务,例如 MySQL、MongoDB 集群等。
StatefulSet 本质上是 Deployment 的变体,在 v1.9 中已经成为 GA 版本。为了解决有状态服务的问题,它管理的Pod有固定的Pod名称、启动和停止序列,在StatefulSet中,Pod名称称为网络标识符(主机名),必须使用共享存储。
在Deployment中对应的服务是service,在StatefulSet中对应的是headless service,无头服务,即无头服务。与服务的区别在于它没有集群IP。解析该 Headl 对应的所有 Pod 的端点列表
另外,StatefulSet 基于 Headless Service 为 StatefulSet 控制的每个 Pod 副本创建一个 DNS 域名。该域名的格式为:

$(podname).(无头服务器名称) FQDN: $(podname).(无头服务器名称).namespace .svc.cluster.local

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

4.部署流程
文章参考项目的README简单介绍了基于StatefulSet创建Redis的步骤:

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

这里我就参考上面的步骤,练习一下操作,详细介绍一下Redis集群的部署过程。本文会涉及到很多K8S的概念。希望大家提前了解和学习

1.创建NFS存储
创建NFS存储主要是为了给Redis提供稳定的后端。结束存储,当重新说明: OD重启或迁移后,仍能获取原始数据。这里,我们首先创建NFS,然后使用PV挂载Redis的远程NFS路径。

安装NFS

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

然后添加/etc/exports文件,设置要共享的路径:


创建对应目录

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

接下来,启动NFS和rpcbind服务:

systemctl restart rpcbindsystemctl restart nfssystemctl启用nfs
[root@ftp pv3]# exportfs -v/usr/local/k8s/redis/pv1192.168.0.0/24( 同步,wdelay,隐藏,no_subtree_check,sec=sys ,rw,安全,no_root_squash,no_all_squash)/ usr / local / k8s / redis / pv2192.168.0.0/24(同步,wdelay,隐藏,no_subtree_check,sec = sys,rw,安全,no_root_squash,no_all_squash)/ usr / local /k8s/redis/pv3192.168.0.0/24(同步,wdelay,隐藏,no_subtree_check,sec=sys,rw,secure,no_root_squash,no_all_squash)/usr/local/k8s /redis/pv4192.168.0.0/24(同步,wdelay,隐藏,no_subtree_check,sec = sys,rw,安全,no_root_squash,no_all_squash)/ usr / local / k8s / redis / pv5192.168.0.0/24(同步,wdelay,隐藏,no_subtree_check,sec = sys,rw ,secure,no_root_squash,no_all_squash)/usr/local/k8s/redis/pv6192.168.0.0/24(sync,wdelay,hide,no_subtree_check,sec=sys,rw,secure,no_root_squash,no_all_squash)

客户

yum -y install nfs-utils

查看存储共享

[root @node2 ~]# showmount -e 192.168.0.222导出 192.168.0.222 的列表:/usr/local/k8s/redis/pv6 192.168.0.0/24/usr/local/k8s/ redis/pv5 192.168.0.0/24/usr /local/k8s/redis/pv4 192.168.0.0/24/usr/local/k8s/redis/pv3 192.168.0.0/24/usr/local/k8s/redis/pv2 192.168。 0.0/24/usr/local/k8s/redis/pv1 192.168.0.0/24

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

[root@master redis]# cat pv.yaml apiVersion: v1kind: PersistentVolumemetadata: name: nfs- PV1Spec: CapAcity :存储:200M 访问模式:-Radwritemany NFS:服务器:192.168.0.222 路径:“/USR/LOCAL/K8S/PV1” --- APIVERSION:V1Kind:P Ersistitvolumemetadata:名称:nfs-vp2spec:容量:存储:200M 访问模式: - ReadWriteMany nfs:服务器:192.168.0.222 路径:“/usr/local/k8s/redis/pv2"---apiVersion: v1kind: PersistentVolumemetadata: 名称: nfs-pv3spec: 容量: 存储: 200M 访问模式: - ReadWriteMany nfs: 服务器: 192.168.0.222 路径: "/usr/local/k8s/redis/pv3"-- -apiVersion:v1kind:PersistentVolumemetadata:名称:nfs-pv4spec:容量:存储:200M accessModes:-ReadWriteMany nfs:服务器:192.168.0.222 路径:“/usr/local/k8s/redis/pv4”---apiVersion:v1kind: PersistentVolumemetadata: 名称: nfs-pv5spec: 容量: 存储: 200M accessModes: - ReadWriteMany nfs: 服务器: 192.168.0.222 路径: "/usr/local/k8s/redis/ pv5"---apiVersion : v1kind: PersistentVolumemetadata: 名称: nfs -pv6spec:capacity:storage:200MaccessModes:-ReadWriteManynfs:server:192.168.0.222path:“/usr/local/k8s/redis/pv6”

如上,可以看到所有PV都是除了名称和挂载路径之外基本相同,执行创建即可:

[root@master redis]#kubectl create -f pv.yaml permanentvolume " nfs-pv1" 创建持久entvolume“nfs-pv2”创建了持久卷“nfs-pv3”创建了持久卷“nfs-pv4”创建了持久卷“nfs-pv5”创建了持久卷“nfs-pv6”创建了

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

[root@master redis]# cat redis.confappendonly yescluster-enabled yescluster-config-file /var/ lib/redis /nodes.confcluster-node-timeout 5000dir /var/lib/redisport 6379

创建一个名为 redis-conf 的 Configmap:

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

查看创建的configmap:

[root@ master redis ]# kubectl 描述 cm redis-confName: redis-confNamespace: defaultLabels:   注释: Data====redis.conf:----appendonly yescluster-enabledyescluster-config-file /var/lib/redis/nodes.confcluster-node-timeout 5000dir /var/ lib/redisport 6379Events: 

同上,保存redis.conf中的所有配置项到 Configmap redis-conf。

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

[root@master redis]# cat headless-service.yaml apiVersion: v1kind: Servicemetadata: name: redis- 服务标签: app: redisspec: ports: - name: redis-port 端口: 6379 clusterIP: None 选择器: app: redis appCluster: redis-cluster

创建:

kubectl create -f headless-service.yml

查看:

可以看到服务名称为redis-service,其CLUSTER-IP 为 None,这意味着这是一个“无头”服务。

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

[root@master redis]# cat redis.yaml apiVersion: apps/v1beta1kind: StatefulSetmetadata: name: redis- appspec:serviceName:“redis-service”副本:6模板:元数据:标签:app:redis appCluster:redis-cluster规范:terminationGracePeriodSeconds:20亲和力:podAntiAffinity:PreferredDuringSchedulingIgnoredDuringExecution:-权重:100 podAffinityTerm:labelSelector:matchExpressions:-key:应用程序运算符:在值中: - redis 拓扑密钥:kubernetes.io/hostname 容器: - 名称:redis 映像:redis 命令: - “redis-server” args: - “/etc/redis/re dis.conf” - “--保护模式” - “无” 资源:“ 请求:” cpu:“100m” 内存:“100Mi” 端口: - 名称:redis 容器端口:6379 协议:“TCP” - 名称:集群容器端口:16379 协议:“TCP“volumeMounts:-名称:“redis-conf”挂载路径:“/etc/redis”-名称:“redis-data”挂载路径:“/var/lib/redis”卷:-名称:“redis-conf”configMap:名称:“redis-conf”项目:项目-键:“redis.conf”路径:“redis.conf”volumeClaimTemplates:-元数据:名称:redis-data规范:accessModes:[“ReadWriteMany”]资源:请求:存储:200M 

如上,一共创建了6个Redis节点(Pod),其中3个作为master,另外3个作为master的slave;Redis配置挂载了之前生成的redis-conf Configmap通过volume到容器的/etc/redis/redis.conf;使用volumeClaimTemplates(即PVC)声明Redis数据存储路径,它将绑定到我们之前创建的文件PV上
这里有一个关键的概念——亲和力,具体请参考官方文档,其中podAntiAffinity代表反亲和力,它决定了某个pod不能被哪些pod访问部署在同一拓扑域中。可以用来将服务的pod分散在不同的主机或者拓扑域中,以提高服务本身的稳定性。 。
PreferredDuringSchedulingIgnoredDuringExecution 表示在调度期间应尽可能满足亲和性或反亲和性规则。如果不能满足规则,POD也可能被调度到相应的主机上。在后续运行过程中,系统将不再检查是否满足这些规则。
这里,matchExpressions规定Redis Pod应该被调度到尽可能不分配给包含app redis的Node,也就是说Redis Pod应该尽可能少的分配给已经有Redis的Node。不过,由于我们只有3个Node和6个副本,根据PreferredDuringSchedulingIgnoredDuringExecution,这些豌豆必须被挤掉,这样更健康~
另外,根据StatefulSet的规则,6个Red我们生成的 Pod 的主机名将命名为 $(statefulset name)-$(serial number) 如下所示:

 [root@Master Redis]#Kubectl get pods -O Wide name Ready Status Restarts Age IP NODE NODEREDIS-APP 0 1/1 Running0                                                                                                                                                                                                                                                                             172.17.24.3        192.168.0.144   2小时 172.17.63.8 192.168.0.14 8 <无>redis-app- 2 1/1 运行 0 2h 172.17.24.8 192.168.0.144 <无>redis-app-3 1/1 运行 0                                                                                                                                                          172.17. 63.9      192.168.0.148                                                                                                                   bsp; 0 2小时172.17.24.9 192.168.0.144 redis-app-5 1/1 Running 0 2h 172.17.63.10 192.168.0.148 

如上,可以看到部署时部署了这些Pod,它们按照 {0...N-1} 的顺序依次创建。需要注意的是,redis-app-1启动后直到redis-app-0状态达到Running状态时才会启动。
同时,每个Pod都会获得集群中的一个DNS域名,格式为$(podname).$(service name).$(namespace).svc.cluster.local< /code>,也就是:

redis-app-0.redis-service.default.svc.cluster.localredis-app-1.redis- service.default.svc.cluster.local...等等...

在K8S集群内,这些Pod之间可以使用这个域名进行通信。我们可以使用busybox镜像的nslookup来查看这些域名:

[root@master redis]# kubectl exec -ti busybox -- nslookup redis-应用程序-0。 redis-service服务器:10.0.0.2地址1:10.0.0.2 kube-dns.kube-system.svc.cluster.localName: redis-app-0.redis-serviceAddress 1: 172.17.24.3

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

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

[root@master redis ]# kubectl get pvNAME 容量访问模式回收策略状态声明存储类原因 AGenfs-pv1 200M RWX 保留绑定默认/redis-data-redis-app-2 3hnfs-pv3 200M RWX 保留绑定默认/redis-data-redis-app- 4 3hnfs-pv4 200M RWX 保留绑定默认/redis-data-redis-app-5 3hnf s-pv5 200M RWX 保留绑定默认/redis-data-redis-app-1 3hnfs-pv6 200M RWX 保留绑定默认/redis-data -redis-app-0 3hnfs-vp2 200M RWX 保留绑定默认/红色is-data-redis-app-3 3小时

5。初始化Redis集群

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

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

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

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

root@ubuntu:/# cat > /etc/apt/sources.list << EOFdeb http://mirrors.aliyun.com/ubuntu/ bionic 主受限宇宙 multiversedeb-src http://mirrors.aliyun .com /ubuntu/ bionic 主受限宇宙 multiversedeb http://mirrors.aliyun.com/ubuntu/ bionic-security 主受限宇宙 multiversedeb-src http://mirrors.aliyun.com/ubuntu/ bionic-security 主受限宇宙 multiversedeb http://mirrors.aliyun.com/ubuntu/ bionic-updates 主要受限宇宙 multiversedeb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates 主要受限宇宙 multiversedeb http://mirrors.aliyun.com/ ubuntu/ bionic-propose 主要受限宇宙多元宇宙 deb-src http://mirrors.aliyun.com/ubuntu/ bionic-propose 主要受限宇宙多元宇宙 deb http://mirrors.aliyun.com/ubuntu/ bionic-backports 主要受限宇宙multiversedeb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports 主受限宇宙多元宇宙>EOF

成功后,原project需要执行以下命令安装基础软件环境:

apt-get updateapt-get install -y vim wget python2.7 python-pip redis- tools dnsutils

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

pip install redis-trib==0.5.1

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

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

其次,为每个Master添加Slave

redis-trib.py 复制 \ --master-addr `dig +short redis-app-0 .redis-service.default.svc.cluster.local`:6379 \ --slave- addr `dig +short redis-app-3.redis-service.default.svc.cluster.local`:6379redis-trib.py 重新plicate \ --master-addr `dig +short redis-app-1.redis-service.default.svc.cluster.local`:6379 \ --slave-addr `dig +shortredis-app-4.redis-service. default.svc.cluster.local`:6379redis-trib.py 复制 \ --master-addr `dig +short redis-app-2.redis-service.default.svc.cluster .local`:6379 \ --slave- addr `dig +short redis-app-5.redis-service.default.svc.cluster.local`:6379

至此,我们的Redis集群才真正创建完成,连接到任意一个Redis Pod即可检查:

[root@master redis]# kubectl exec -it redis-app-2 /bin /bashroot@redis-app-2:/data# /usr/local/bin/redis-cli -c127.0.0.1:6379> 集群节点5d3e77f6131c6f272576530b23d1cd7592942eec 172.17.24.3:6379@16379 master - 0 1559628533000 1 个已连接 0-5461a4b 529c40a920da314c6c93d17dc603625d6412c 172.17 .63.10:6379@16379 主站 - 0 1559628531670 6 已连接10923-16383368971dc8916611a86577a8726e4f1f3a69c5eb7 172.17.24.9:6379@16379从站0025e6140f85cb243c60c214467b7e77bf819ae3 0 15596 28533672 4 已连接0025e6140f85cb243c60c214467b7e77bf819ae3 172.17.63.8:6379@16379 主站 - 0 1559628533000 2 已连接 5462-109226d5ee94b78b279e7d3c77a55 437695662e8c039e 172.17.24.8:6379@16379我自己,奴隶a4b529c40a920da314c6c93 d17dc603625d6412c 0 1559628532000 5已连接2eb3e06ce914e0e285d6284 c4df32573e318bc01 172.17.63.9:6379@16379从属5d3e77f6131c6f27257 6530b23d1cd7592942eec 0 1559628533000 3已连接127。 0.0.1:6379> 集群 infocluster_state:okcluster_slots_signed:16384cluster_slots_ok:16384cluster_slots_pfail:0cluster_slots_fail:0cluster_known_nodes:6cluster_size:3cluster_current_epoch:6cluster_my_epoch:6cluster_stats_messages_ping_s ent:1 4910cluster_stats_messages_pong_sent:15139cluster_stats_messages_sent:30049cluster_stats_messages_ping_received:15139cluster_stats_messages_pong_received:14910cluster_stats_messages_received:30049127.0.0.1:6379> 

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

[root@ftp pv3]#ll/usr/local/k8s/redis/pv3total 12-rw-r--r-- 1 root root 92 Jun 4 11:36appendonly.aof-rw-r--r-- 1 root root 175 Jun 4 11:36 dump.rdb-rw-r--r-- 1根根794年6月4日11:49nodes.conf

6。创建用于访问的Service
前面我们创建了一个Headless Service用于实现StatefulSet,但是这个Service没有Cluster IP,所以无法用于外部访问。因此,我们还需要创建一个Service,专门为Redis集群提供访问和负载均衡:

[root@master redis]# cat redis-access - service.yaml apiVersion: v1kind: Servicemetadata: name: redis-access-service labels: app: redisspec: ports: - name: redis-port 协议: "TCP" 端口: 6379 targetPort: 6379 选择器: app: redis appCluster: redis -cluster

如上,服务名称为redis-access-service。 K8S集群暴露6379端口,labels nameapp:redis的Pod or appCluster:redis-cluster 将进行负载平衡。

创建后查看:

[root@master redis]# kubectl get svc redis-access-service -o WideNAME TYPE CLUSTER- IP EXTERNAL-IP PORT(S) AGE SELECTORredis-access-service ClusterIP 10.0.0.64    6379/TCP 2h app=redis,appCluster=redis-cluster

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

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

[root@master redis]# kubectl get pods redis-app-0 -o WideNAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODEredis-app-1 1/1 Running0 3h 172.17.24.3 192.168。 0.144 

输入redis-app-0查看:

[root@master redis ]# kubectl exec -it redis-app-0 /bin/bashroot@redis-app-0:/data# /usr/local/bin/redis-cli -c127.0.0.1:6379> 角色1) "master"2 ) (整数)133703) 1) 1) "172.17.63.9" 2) "6379" 3) "13370"127.0.0.1:6379>

如上所示,app-0 是master,slave是172.17.63.9,也就是redis-app-3

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

[root@master redis]# kubectl delete pod redis-app-0pod“redis-app-0”已删除[root@master redis]# kubectl get pod redis-app-0 -o WideNAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODEredis-app-0 1/1 Running 0 4m 172.17.24.3 192.168.0.144 <无>

我们进入redis-app-0里面查看:

[root@master redis]# kubectl exec -it redis-app -0 /bin/bashroot@redis-app-0:/data# /usr/local/bin/redis-cli -c127.0.0.1:6379> 角色1) “从站”2) “172.17. 63.9"3) (integer) 63794) "connected"5) (integer) 13958

如上,redis-app-0变了,成为了slave,属于它的上一个从节点172.17.63.9,即redis-app-3

6.问题
此时,您可能会想,为什么在不使用 stable 标志的情况下,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 96689f2023089173e528d3a71c4ef10af68ee462 192.168 .169.209:6379 @16379 从站 d884c4971de9748f99b10d14678d864187a9e5d3 0 1526460952651 4 连接237d46046d9b75a6822f02523ab894928e2300e6 19 2.168.169.200 :6379@16379 从机 c15f378a604ee5b200f06cc23e9371cbc04f4559 0 1526460952651 1 已连接
c15f378a604ee5b200f06cc23e9371cbc04f455 9 192.168.169.1 97:6379@16379 主站 - 0 1526460952651 1 连接 10923-16383d884c4971de9748f99b1192.168 .169.198:6379 @16379我自己,从机c8a8f70b4c29333de6039c47b2f3453ed11fb5c2 0 1526460952565 3已连接
c8a8f70b4c29333de6039c47b2f3453ed11fb5c2 192.168.169。 201:6379@16379 master - 0 1526460952651 6 linked 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日志。

这篇《如何在K8s上部署Redis集群》的文章就分享到这里。希望以上内容能够对大家有所帮助,让大家能够学到更多的知识。 ,如果您觉得文章不错,请转发,让更多人看到。

1. 本站所有资源来源于用户上传或网络,仅作为参考研究使用,如有侵权请邮件联系站长!
2. 本站积分货币获取途径以及用途的解读,想在本站混的好,请务必认真阅读!
3. 本站强烈打击盗版/破解等有损他人权益和违法作为,请各位会员支持正版!
4. 编程技术 > 如何在K8s上部署Redis集群

用户评论