likes
comments
collection
share

Kubernetes中部署Redis Cluster本篇文章讲解如何在Kubernetes中搭建集群化的Redis以及R

作者站长头像
站长
· 阅读数 29

一、前言

原文连接

本文章是一篇在Kubernetes中搭建集群化的Redis的教程,下文中将称其为Redis Cluster

除了如何在Kuberbnetes中部署Redis Cluster外,还将在Kubernetes中部署Redis Cluster Proxy用作Redis Cluster的代理。

要想理解本文搭建Redis Cluster的原理,首先要先对以下的前置知识有所理解:

  • Kubernets 有所了解
  • 熟悉 KubernetsStatefulSetDeployment
  • 熟悉 KubernetsService
  • 熟悉 KubernetsConfigMap
  • 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节点),三主三从。

Kubernetes中部署Redis Cluster本篇文章讲解如何在Kubernetes中搭建集群化的Redis以及R

当然资源丰富的时候,最好各个pod部署在不同的Node上。这里只是在测试环境下搭建的集群,少占用几个机器。

Redis作为一个数据库自然是有状态的,所以采用StatefulSet的部署方式。

在编写StatefulSetyaml文件之前我们要先构建好其对应的ServiceConfigMap

这里主要将分为三个步骤:

  • 部署 Service
  • 部署 ConfigMap
  • 部署 StatuFulSet

2.1 部署 Service

因为Redis Cluster中的各个实例需要在集群内相互通信,所以我们就采用无头HeaderLessService方式。不需要有具体的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
  • 这里我们声明StatefulSetpods数量是6个
  • 绑定前面的Serviceconf

部署成功该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

再依次建立每个masterslave的关系:

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 infocluster 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:

Kubernetes中部署Redis Cluster本篇文章讲解如何在Kubernetes中搭建集群化的Redis以及R

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

Kubernetes中部署Redis Cluster本篇文章讲解如何在Kubernetes中搭建集群化的Redis以及R

有时就会重定向到另一个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集群了。如下图所示:

Kubernetes中部署Redis Cluster本篇文章讲解如何在Kubernetes中搭建集群化的Redis以及R

不过代理服务不支持cluster info这种命令。

此时就可以通过客户端工具进行连接了。

如果你使用的是 DataGrip 客户端工具,应该选择stand-alone的连接方式而不是cluster模式,并且需要关闭以下校验:

Kubernetes中部署Redis Cluster本篇文章讲解如何在Kubernetes中搭建集群化的Redis以及R

六、最后的集群整体架构

Kubernetes中部署Redis Cluster本篇文章讲解如何在Kubernetes中搭建集群化的Redis以及R

Proxy 应该与master 和 salve直接相连,但线条会显得凌乱,就直接连在node上了。

转载自:https://juejin.cn/post/7405473135536177186
评论
请登录