Kubernetes中部署Redis Cluster本篇文章讲解如何在Kubernetes中搭建集群化的Redis以及R
一、前言
本文章是一篇在Kubernetes中搭建集群化的Redis的教程,下文中将称其为Redis Cluster。
除了如何在Kuberbnetes中部署Redis Cluster外,还将在Kubernetes中部署Redis Cluster Proxy用作Redis Cluster的代理。
要想理解本文搭建Redis Cluster的原理,首先要先对以下的前置知识有所理解:
- 对
Kubernets有所了解 - 熟悉
Kubernets的StatefulSet和Deployment - 熟悉
Kubernets的Service - 熟悉
Kubernets的ConfigMap - Redis 集群的基本了解
本教程假设读者已经会
kubectl等k8s工具的使用,所以当创建某个Kubernets的某个对象时,只会给出yaml文件,省略具体的执行命令。
除了知识以外还需要的前提环境和一些准备:
- 可用的 Kubernetst 环境及一些准备
- 需要的各种镜像
redis的镜像 我使用的6.2.5的版本redis-tribe的镜像redis-cluster-proxy的镜像
- 创建好一个
namespace(当然也可以用默认),本文的所有服务都会部署在同一个namespace下,本教程中以my-namespace为例,注意拷贝使用本文章中的yaml文件时记得替换成自己的namespace。 - 开放nodes上的相关的端口号,本教程使用的端口号:6379,36379,6378,36378
上面的三个镜像都是从网上找的一些镜像,然后上传到自己的镜像仓库中,因为我的仓库是私人private的所以在拉取镜像时需要加上secret。请大家根据自己的情况进行镜像的准备和配置文件的编写。后面将不再赘述。
redis-cluster-proxy 该中间件目前没有稳定版的镜像,github上有源代码,可以根据教程自己制作镜像,也可以去网上搜索一下别人制作好的镜像,我使用的是这个镜像:hub.docker.com/r/jeko/redi…
二、部署 Redis 集群中的节点
我们第一步就来部署Redis集群中的节点。
由于我的kubernetes环境中有三个nodes,故我想来设置六个Pods(redis节点),三主三从。

当然资源丰富的时候,最好各个pod部署在不同的Node上。这里只是在测试环境下搭建的集群,少占用几个机器。
Redis作为一个数据库自然是有状态的,所以采用StatefulSet的部署方式。
在编写StatefulSet的yaml文件之前我们要先构建好其对应的Service和ConfigMap。
这里主要将分为三个步骤:
- 部署
Service - 部署
ConfigMap - 部署
StatuFulSet
2.1 部署 Service
因为Redis Cluster中的各个实例需要在集群内相互通信,所以我们就采用无头HeaderLess的Service方式。不需要有具体的ClusterIp。
kind: Service
apiVersion: v1
metadata:
name: redis-service
namespace: my-namespace
labels:
app: redis
spec:
ports:
- name: redis-port
port: 6379
selector:
app: redis-cluster
clusterIP: None
- 这里的selector中的标签
app: redis-cluster要和后面的Stateful中的设置的标签一致。
然后kubectl apply这个yaml文件进行Service的创建。
2.2 部署 ConfigMap
如果你曾在docker容器中或直接部署过redis实例,就肯定知道redis启动时有一个redis.conf配置文件。
所以我们也要为其写好一个配置文件,只不过是通过kubernetes configMap来做而已。
kind: ConfigMap
apiVersion: v1
metadata:
name: redis-conf
namespace: my-namespace
data:
redis.conf: |
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-require-full-coverage no
cluster-migration-barrier 1
appendonly yes
这里的|符号表示后面的内容作为一个完整的字符串,这样我们就不需要设置每一个配置用到的key了,在部署redis实例的时候直接使用该配置即可。
2.3 部署 StatefulSet
kind: StatefulSet
apiVersion: apps/v1
metadata:
name: redis-cluster
namespace: my-namespace
labels:
app: redis-cluster
spec:
replicas: 6
serviceName: redis-service
selector:
matchLabels:
app: redis-cluster
template:
metadata:
labels:
app: redis-cluster
spec:
volumes:
- name: redis-volume
configMap:
name: redis-conf
items:
- key: redis.conf
path: redis.conf
defaultMode: 420
containers:
- name: redis-container
image: 'registry.yourimages.com/redis:6.2.5'
command:
- redis-server
args:
- /etc/redis/redis.conf
ports:
- name: redis-port
containerPort: 6379
protocol: TCP
resources: {}
volumeMounts:
- name: redis-pvc
mountPath: /data
- name: volume-ji63m6
readOnly: true
mountPath: /etc/redis
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
imagePullPolicy: IfNotPresent
restartPolicy: Always
terminationGracePeriodSeconds: 30
dnsPolicy: ClusterFirst
serviceAccountName: default
securityContext: {}
imagePullSecrets:
- name: docker-secret
schedulerName: default-scheduler
volumeClaimTemplates:
- kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: redis-pvc
namespace: my-namespace
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: local
volumeMode: Filesystem
podManagementPolicy: OrderedReady
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 0
revisionHistoryLimit: 10
- 这里我们声明
StatefulSet的pods数量是6个 - 绑定前面的
Service和conf
部署成功该StatefulSet后,后面就可以对这六个节点就行编排确定谁是master谁是slave了。
对应的自动生成的六个Pod的名称是:
- redis-cluster-0
- redis-cluster-1
- ……
- redis-cluster-5
k8s 会为每个pod分配一个ipv4的内部地址。
2.4 测试是否互通
除了前面说的ipv4的地址外,由于我们创建了无头的服务,所以各个Pods间的通信是基于DNS的,那么每个Pod都有其DNS格式的ip地址:
Pod 名称 .Service名称.namespace 名称.svc.cluster.lcoal
以redis-cluster-0为例,其地址为:
redis-cluster-0.redis-service.mynamespace.svc.cluster.local
因为Pod可能重启,ipv4的地址就会变更,如果集群内使用这个来通信就麻烦了,所以这个DNS的地址非常重要,因为它只跟Pod名称有关系。
接下来我们就通过这个地址来测试是否互通。
- 随便进入一个pod,通过
kubetcl进入容器内部:kubectl exec -it redis-cluster-0 -n my-namespce - 然后通过
redis-cli通过DNS地址来登入到另外一个redis节点:redis-cli -h redis-cluster-4.redis-service.my-namespace.svc.cluster.local
只要成功登入说明是成功的。
此时可以通过cluster info命令查看集群情况:此时返回的信息中会显示集群的状态是failed的,因为现在只是网络上互通,还没有做master-slave的配置。接下来我们就来做这个配置。
还可以通过 cluster nodes 的命令查看各个节点的情况
三、集群配置
这里主要有两种方式来配置master-slave,一种是使用ipv4的方式来创建集群,另一种使用redis-tribe的方式。第一种方式的缺点是显而易见的,因为前文说过ipv4的地址是会发生变化的。但在此仍介绍它如何操作。
3.1 IP 地址配置集群
通过kubectl随便进入到一个容器环境中,然后通过每个pod的ipv4地址进行如下配置即可:
redis-cli --cluster create 10.233.70.145:6379 10.233.90.12:6379 10.233.70.165:6379 10.233.96.124:6379 10.233.90.182:6379 10.233.96.100:6379 --cluster-replicas 1
记得将ip地址替换成自己的pod的ip。
执行过程中会询问是否应用以上配置,输入yes后继续即可。
3.2 使用 redis-tribe 来配置
可以直接在机器上部署该工具,不过我们这里都有了kubernetes环境了,就直接以deployment的方式部署该服务。
kind: Deployment
apiVersion: apps/v1
metadata:
name: redis-cluster-tools
namespace: my-namespace
labels:
app: redis-cluster-tools
spec:
replicas: 1
selector:
matchLabels:
app: redis-cluster-tools
template:
metadata:
name: pos-redis
labels:
app: redis-cluster-tools
spec:
containers:
- name: pos-redis
image: 'registry.yourristry.com/redis-tools:tag'
args:
- /bin/bash
- '-c'
- sleep 3600
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
imagePullPolicy: IfNotPresent
restartPolicy: Always
terminationGracePeriodSeconds: 30
dnsPolicy: ClusterFirst
securityContext: {}
schedulerName: default-scheduler
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 25%
maxSurge: 25%
revisionHistoryLimit: 10
progressDeadlineSeconds: 600
部署成功后,进入Pod容器终端:
kubectl exec -it redis-cluster-tools-5f7f947d4f-ktcbl -n my-namespace -- bash
然后使用DNS的地址,执行下面的命令:
# 创建只有master的集群
redis-trib.py create `dig +short redis-cluster-0.redis-service.my-namespace.svc.cluster.local`:6379 `dig +short redis-cluster-1.redis-service.my-namespace.svc.cluster.local`:6379 `dig +short redis-cluster-2.redis-service.my-namespace.svc.cluster.local`:6379
再依次建立每个master和slave的关系:
redis-trib.py replicate --master-addr `dig +short redis-cluster-0.redis-service.my-namespace.svc.cluster.local`:6379 --slave-addr `dig +short redis-cluster-3.redis-service.my-namespace.svc.cluster.local`:6379
redis-trib.py replicate --master-addr `dig +short redis-cluster-1.redis-service.my-namespace.svc.cluster.local`:6379 --slave-addr `dig +short redis-cluster-4.redis-service.my-namespace.svc.cluster.local`:6379
redis-trib.py replicate --master-addr `dig +short redis-cluster-2.redis-service.my-namespace.svc.cluster.local`:6379 --slave-addr `dig +short redis-cluster-5.redis-service.my-namespace.svc.cluster.local`:6379
注意这里最好好好观察一下每个pod所处的node位置,最好不要让处于同一个node的两个pod互为主从关系,避免一台机器挂了,主从都挂了。
当然如果你有六个nodes,自然不需要考虑这个问题。
此时再使用redis-cli -c -h xxx -p 6379登入某个节点后,查看cluster info或cluster nodes就能看到集群的状态了。
四、失败的NodePort Service
我们前面只创建了集群的网络让各个redis的节点能够相互访问,现在我想在集群外进行访问,我该怎么做呢?
自然就想到了Kubernetes Service中的NodePort类型。
那接下来我们就尝试开放NodePort网络来链接我们的Redis Cluster。
kind: Service
apiVersion: v1
metadata:
name: redis-service-node
namespace: my-namespace
labels:
app: redis
spec:
ports:
- name: redis-port
protocol: TCP
port: 6379
targetPort: 6379
nodePort: 36379
selector:
app: redis-cluster
type: NodePort
创建完成后,我们尝试在集群外来访问。
我在我的笔记本上安装了redis-cli客户端,任选一个node的ip加36379端口号进行连接:
redis-cli -h 192.168.4.222 -p 36379 -c
然后执行cluster info:

这看起来好像没问题,但当执行一些get、set命令时:

有时就会重定向到另一个pod中,此时用的ip是集群内的ip,就会导致连接失败。所以使用nodeport是不行的。
当使用一些redis的客户端:Datagrip,Redis Desktop Manager以集群方式连接时也会报错。
接下来我们就尝试再加一层代理的方式来处理这个问题。
五、Redis Cluster Proxy
Redis Cluster Proxy是一款开源的用于代理redis cluster的中间件。同样我们在kubernetes中部署它。
5.1 准备 redis-cluster-conf
apiVersion: v1
kind: ConfigMap
metadata:
name: redis-proxy-conf
namespace: my-namespace
data:
proxy.conf: |
#cluster redis-cluster:6379
cluster 10.233.xxx.xxx:6379
cluster 10.233.xxx.xxx:6379
cluster 10.233.xxx.xxx:6379
cluster 10.233.xxx.xxx:6379
cluster 10.233.xxx.xxx:6379
cluster 10.233.xxx.xxx:6379
# 配置为Redis Cluster Service
bind 0.0.0.0
# redis-cluster-proxy 对外暴露端口
port 6378
# 线程数量
threads 8
daemonize no
enable-cross-slot yes
# 配置Redis Cluster 认证密码
# auth xxx
# auth-user xxx
#log-level error
上面的ip地址换成自己的pods的ip地址。
同样我们也可以使用DNS地址(推荐用这个)。
apiVersion: v1
kind: ConfigMap
metadata:
name: redis-proxy-conf
namespace: my-namespace
data:
proxy.conf: |
#cluster redis-cluster:6379
cluster redis-cluster-0.redis-service.my-namespace.svc.cluster.local:6379
cluster redis-cluster-1.redis-service.my-namespace.svc.cluster.local:6379
cluster redis-cluster-2.redis-service.my-namespace.svc.cluster.local:6379
cluster redis-cluster-3.redis-service.my-namespace.svc.cluster.local:6379
cluster redis-cluster-4.redis-service.my-namespace.svc.cluster.local:6379
cluster redis-cluster-5.redis-service.my-namespace.svc.cluster.local:6379
# 配置为Redis Cluster Service
bind 0.0.0.0
# redis-cluster-proxy 对外暴露端口
port 6378
# 线程数量
threads 8
daemonize no
enable-cross-slot yes
# 配置Redis Cluster 认证密码
# auth xxx
# auth-user xxx
#log-level error
5.2 部署 redis-cluster-proxy 的Service
因为最后我们需要把代理的服务暴露出去,所以这里会部署一个NodePort类型的Service。
apiVersion: v1
kind: Service
metadata:
name: redis-proxy-service
namespace: my-namespace
spec:
type: NodePort
ports:
- name: redis-proxy-port
protocol: TCP
port: 6378
targetPort: 6378
nodePort: 36378
selector:
app: redis-proxy
redis-proxy是我们后面要部署的deployment的名称。
5.3 部署 redis-cluster-proxy 服务
我们部署两个实例来保持高可用。
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-proxy
namespace: my-namespace
spec:
replicas: 2
selector:
matchLabels:
app: redis-proxy
template:
metadata:
labels:
app: redis-proxy
spec:
imagePullSecrets:
- name: docker-secret
containers:
- name: redis-proxy
image: registry.cn-hangzhou.aliyuncs.com/4port-hz/redis-proxy:1.0
imagePullPolicy: Always
command: ["redis-cluster-proxy"]
args:
- -c
- /data/proxy.conf # 指定启动配置文件
ports:
- name: port
containerPort: 6378
protocol: TCP
volumeMounts:
- name: redis-proxy-conf
mountPath: /data/
volumes: # 挂载proxy配置文件
- name: redis-proxy-conf
configMap:
name: redis-proxy-conf
部署成功后就可以基于代理服务的NodePort来访问redis集群了。如下图所示:

不过代理服务不支持cluster info这种命令。
此时就可以通过客户端工具进行连接了。
如果你使用的是 DataGrip 客户端工具,应该选择stand-alone的连接方式而不是cluster模式,并且需要关闭以下校验:
六、最后的集群整体架构

Proxy 应该与master 和 salve直接相连,但线条会显得凌乱,就直接连在node上了。
转载自:https://juejin.cn/post/7405473135536177186
