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