美文网首页
k8s环境下单节点运行Redis集群

k8s环境下单节点运行Redis集群

作者: 行走的记忆 | 来源:发表于2019-08-10 16:39 被阅读0次

    单机下用Redis二进制程序包搭建Redis集群的案例很多,用docker在单节点上搭集群的也很多,但是在k8s下单节点搭集群的就很少了,有的只是挂载一个临时目录,数据无法持久化,pod销毁后,数据就没了。在k8s环境下测试机又不够的情况下使用Redis集群就不太方便了,本文就是笔者的根据自身需要实践出来的,期间也找了很多网上资料,最后自己综合琢磨出的解决方案,由于对docker和k8s不是很精通,可能有其他更简单的方案,欢迎大家交流。

    一、所需的背景知识

    1、对docker,k8s有一定使用经验:会编写yaml文件,知道如何排查pod不能running的问题
    2、在单机环境下搭建过Redis集群
    3、如果以上的知识都不知道也没关系,本文尽量保证你按照步骤执行不出错。但是k8s环境你必须得有,基本的linux命令知识得有

    二、软件版本

    k8s v1.10.2
    docker 18.03.1-ce, build 9ee9f40
    Redis5.0.5

    三、准备工作

     docker pull redis:5.0.5
     mkdir redis-cluster
     cd redis-cluster/
     mkdir data
    

    创建多个节点的数据存储目录,避免Redis实例启动时配置文件冲突导致无法启动,在单台物理机上搭建过Redis集群的应该知道。建好6个节点的目录,后面会用到,这里的目录要注意权限问题,k8s启动的pod需要读写这里的文件夹

    for port in `seq 7001 7006`; do \
      mkdir -p ./${port}/
    done
    

    创建完成后目录结构

    目录结构

    四、编辑yaml文件

    创建k8s的yaml文件
    回到redis-cluster目录下

    1、创建ConfigMap

    先创建redis.conf配置文件
    vi redis-cm.yaml
    将以下内容贴到文件中,保存退出

    apiVersion: v1
    kind: ConfigMap
    metadata:
     name: redis-conf
    data:
     redis.conf: |
             appendonly yes
             cluster-enabled yes
             cluster-config-file /var/lib/redis/nodes.conf
             cluster-node-timeout 5000
             dir /var/lib/redis
             port 6379
    

    2、创建statefulset

    vi redis-statefulset.yaml

    apiVersion: apps/v1beta1
    kind: StatefulSet
    metadata:
      name: redis-app
    spec:
      serviceName: "redis-service"
      replicas: 6
      template:
        metadata:
          labels:
            app: redis
            appCluster: redis-cluster
        spec:
          nodeSelector:
            node: mfc    # 这里需要根据自己的k8s节点情况修改,本案例需修改成node-222
          terminationGracePeriodSeconds: 20
          affinity:
            podAntiAffinity:
              preferredDuringSchedulingIgnoredDuringExecution:
              - weight: 100
                podAffinityTerm:
                  labelSelector:
                    matchExpressions:
                    - key: app
                      operator: In
                      values:
                      - redis
                  topologyKey: kubernetes.io/hostname
          containers:
          - name: redis
            image: "redis:latest"
            imagePullPolicy: IfNotPresent #默认情况是会根据配置文件中的镜像地址去拉取镜像,配置为本地有镜像不拉取远程,避免你的环境不能访问外网,拉取镜像失败
            command:
              - "redis-server"
            args:
              - "/etc/redis/redis.conf"
              - "--protected-mode"
              - "no"
            resources:
              requests:
                cpu: "100m"
                memory: "100Mi"
            ports:
                - name: redis
                  containerPort: 6379
                  protocol: "TCP"
                - name: cluster
                  containerPort: 16379
                  protocol: "TCP"
            volumeMounts:
              - name: "redis-conf"
                mountPath: "/etc/redis"
              - name: "redis-data"
                mountPath: "/var/lib/redis"
          volumes:
          - name: "redis-conf"
            configMap:
              name: "redis-conf"
              items:
                - key: "redis.conf"
                  path: "redis.conf"
      volumeClaimTemplates:   #可看作pvc的模板
        - metadata:
          name: redis-data
        spec:
          accessModes: [ "ReadWriteOnce" ]
          resources:
            requests:
              storage: 1Gi
    

    3、创建pv的yaml文件

    vi pv1.yaml

    kind: PersistentVolume
    apiVersion: v1
    metadata:
      name: redis-pv-volume1
      labels:
        type: local
    spec:
      capacity:
        storage: 2Gi
      accessModes:
        - ReadWriteOnce
      hostPath:
        path: /home/bboss/redis-cluster/data/7001/
    

    /home/bboss/redis-cluster/data/7001/此目录为前面我们创建的redis数据存储的6个目录
    将此文件复制5份,依次命名为pv2.yaml 到pv6.yaml
    在对应的文件中将

    name: redis-pv-volume1
    path: /home/bboss/redis-cluster/data/7001/
    

    这2处修改为对应文件编号的序号,例如pv2.yaml修改为

    name: redis-pv-volume2
    path: /home/bboss/redis-cluster/data/7002/
    

    五、开始搭建集群

    1、创建cm和pv如下图

    km create -f redis-cm.yaml
    km create -f pv1.yaml
    
    image.png

    2、创建statefulset

    确定当前宿主机在k8s集群中的label

    kubectl get nodes --show-labels
    
    k8s集群机器ip和对应的label

    如图,我当前部署的机器ip是222对应的node标签名为node-222
    将redis-statefulset.yaml文件中node: mfc 修改为 node: node-222,稍后启动redis的pod是都会选择在node-222这个节点的机器上启动,这正是我们需要的,修改正确后

    km create -f redis-statefulset.yaml
    

    [注:我这里km是做了配置,km相当于是kubectl -n mfc-namespace 这是指定k8s的namespace的,我的namespace名是mfc-namespace,kubectl命令不指定-n 默认是使用default为名的namespace]

    kubectl get pods  -n mfc-namespace | grep redis 
    

    查看6个redis节点是否都启动完成

    查看一下pod对应的ip,下一步配置集群需要知道各节点的ip,这ip是k8s分配的

    kubectl get pods -o wide -n mfc-namespace | grep redis
    

    验证一下pvc是否都bound了

    kubectl get pvc -o wide -n mfc-namespace
    
    redis节点的ip

    3、初始化Redis集群

    启动一个独立的redis

    docker run -it redis:latest bash
    redis-cli --cluster create 10.254.79.20:6379 10.254.79.21:6379 10.254.79.22:6379 10.254.79.23:6379 10.254.79.24:6379 10.254.79.25:6379 --cluster-replicas 1
    
    初始化Redis集群 接上图

    4、验证Redis集群

    日志显示集群已经初始化好,slots也分配完成,验证一下集群
    用redis-cli随便连接某个redis节点

    /usr/local/bin/redis-cli -c -h 10.254.79.20 -p 6379
    
    验证集群

    搭建完成了,下面来看看本地存储的数据
    随便进到某个目录下看看,如下图redis集群的数据已经落到物理机上了。

    3号节点的数据文件

    验证一下pod重启后集群数据是否还在

    重启Redis集群

    看ip还是原来的ip,这就是k8s定义statefulset的作用
    通过刚才启动的独立redis再次连接集群看数据还在不在,如下图数据都还在

    验证集群中已数据是否还在,如图数据都还在

    5、创建headlessService

    到这里我们的Redis集群已经是完全可用的了,但是我们的应用要使用该集群时配置ip不好记,这时就需要用到k8s中的Service
    vi redis-headlessService.yaml

    apiVersion: v1
    kind: Service
    metadata:
      name: redis-service
      labels:
        app: redis
    spec:
      ports:
      - name: redis-port
        port: 6379
      clusterIP: None
      selector:
        app: redis
        appCluster: redis-cluster
    

    执行 km create -f redis-headlessService.yaml 创建服务
    查看服务

    查看redis-service服务

    另外用k8s启一个有nslookup或者ping工具的pod,验证一下服务名是否可以访问

    验证服务名

    k8s是自带dns功能的,k8s会根据serviceName生成对应的域名
    如上图,可以看到节点1 (10.254.79.20)的完整域名是redis-app-0.redis-service.mfc-namespace.svc.cluster.local,在k8s中同一个namespace的服务只需要前面的子域名即可,后面的会默认补齐,本例中的serviceName是redis-app-0.redis-service,这是一个节点的服务名。对于整个集群的serviceName是redis-service

    用刚才的docker启动的redis验证一下域名是否可以连接,如下图用域名连接失败,因为我们这个是用docker启动的不在k8s中,用pod的ip是可以连接的,因为pod的ip访问是走docker的网络。因为服务名是k8s做的路由,所以必须在k8s集群中注册才可以使用服务名互相访问。serviceName其实是在操作系统中做了一个转发的动作,通过iptales做策略实现的。

    用域名连接失败

    用redis集群中的任意一个节点验证,域名是可以访问的

    用redis集群中的节点验证

    六、总结

    网上大部分文章都是类似的方案,使用pv,pvc做存储,有的pv是绑定的nfs,我也试过nfs碰到问题没解决只能想其他办法,但是我理解的是如果6个redis节点的配置文件nodes.conf和数据文件都在共享一个目录,那还是会有冲突,导致redis只能启动一个节点,而其他节点无法启动,除非能把文件命名定义成不同节点对应不同的文件名,因为没搭成功基于nfs的pvc存储,不知道是不是每个节点一个隔离的pvc,所以无法知道以后会再实践。所以本例主要是通过定义不同的pv把目录隔离开,但是定义StatefulSet时不好指定pvc,看到有文章用到volumeClaimTemplates(可看作pvc的模板)传输门于是问题就应然而解了,StatefulSet在创建pod时是前一个创建成功才继续下一个是有顺序的,同时创建pvc也是有顺序的,每个pvc绑定一个pv,这样6个节点的数据文件就自动绑定到各自的目录下了。
    另外redis集群初始化不需要Ruby了,避免了安装ruby的麻烦。

    七、参考资料

    [https://www.cnblogs.com/tylerzhou/p/11027559.html]
    [https://v1-12.docs.kubernetes.io/zh/docs/tasks/run-application/force-delete-stateful-set-pod/]
    [https://juejin.im/post/5d206b1e5188252f275fdc95]
    [https://www.cnblogs.com/breezey/p/6582082.html]
    [https://juejin.im/post/5c989ff2f265da60f206ffe4#heading-7]
    [https://www.jianshu.com/p/a5172b0eeae4]
    https://www.cnblogs.com/xiaochangwei/p/7993065.html

    相关文章

      网友评论

          本文标题:k8s环境下单节点运行Redis集群

          本文链接:https://www.haomeiwen.com/subject/gkvljctx.html