在K8S上搭建Redis集群

今天让咱们试着在k8s里部署一个redis集群,了解更多k8s的细节和特性。node

环境:minikube v0.30 (kubernetes 1.10)python

注:redis-cluster相关的背景知识和细节在此不作赘述,能够参考以前的文章稍做回顾git

问题分析

本质上来讲,在k8s上部署一个redis集群和部署一个普通应用没有什么太大的区别,但须要注意下面几个问题:github

  1. REDIS是一个有状态应用redis

    这是部署redis集群时咱们最须要注意的问题,当咱们把redis以pod的形式部署在k8s中时,每一个pod里缓存的数据都是不同的,并且pod的IP是会随时变化,这时候若是使用普通的deployment和service来部署redis-cluster就会出现不少问题,所以须要改用StatefulSet + Headless Service来解决ubuntu

  2. 数据持久化api

    redis虽然是基于内存的缓存,但仍是须要依赖于磁盘进行数据的持久化,以便服务出现问题重启时能够恢复已经缓存的数据。在集群中,咱们须要使用共享文件系统 + PV(持久卷)的方式来让整个集群中的全部pod均可以共享同一份持久化储存缓存

概念介绍

在开始以前先来详细介绍一下几个概念和原理ruby

Headless Service

简单的说,Headless Service就是没有指定Cluster IP的Service,相应的,在k8s的dns映射里,Headless Service的解析结果不是一个Cluster IP,而是它所关联的全部Pod的IP列表bash

StatefulSet

StatefulSet是k8s中专门用于解决有状态应用部署的一种资源,总的来讲能够认为它是Deployment/RC的一个变种,它有如下几个特性:

  1. StatefulSet管理的每一个Pod都有惟一的文档/网络标识,而且按照数字规律生成,而不是像Deployment中那样名称和IP都是随机的(好比StatefulSet名字为redis,那么pod名就是redis-0, redis-1 ...)

  2. StatefulSet中ReplicaSet的启停顺序是严格受控的,操做第N个pod必定要等前N-1个执行完才能够

  3. StatefulSet中的Pod采用稳定的持久化储存,而且对应的PV不会随着Pod的删除而被销毁

另外须要说明的是,StatefulSet必需要配合Headless Service使用,它会在Headless Service提供的DNS映射上再加一层,最终造成精确到每一个pod的域名映射,格式以下:

$(podname).$(headless service name)
复制代码

有了这个映射,就能够在配置集群时使用域名替代IP,实现有状态应用集群的管理

方案

借助StatefulSet和Headless Service,集群的部署方案设计以下(图片来自参考文章):

配置步骤大概罗列以下:

  1. 配置共享文件系统NFS
  2. 建立PV和PVC
  3. 建立ConfigMap
  4. 建立Headless Service
  5. 建立StatefulSet
  6. 初始化redis集群

实际操做

因为使用的是minikube的单node环境,为了简化复杂度,此次先不配置PV和PVC,直接经过普通Volume的方式来挂载数据

建立ConfigMap

先建立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
复制代码

而后kubectl create configmap redis-conf --from-file=redis.conf来建立ConfigMap

建立HeadlessService

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
复制代码

建立StatefulSet

apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
  name: redis-app
spec:
  serviceName: "redis-service"
  replicas: 6
  template:
    metadata:
      labels:
        app: redis
        appCluster: redis-cluster
    spec:
      terminationGracePeriodSeconds: 20
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - redis
              topologyKey: kubernetes.io/hostname
      containers:
      - name: redis
        image: "registry.cn-qingdao.aliyuncs.com/gold-faas/gold-redis:1.0"
        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"
      - name: "redis-data"
        emptyDir: {} 
复制代码

初始化redis集群

StatefulSet建立完毕后,能够看到6个pod已经启动了,但这时候整个redis集群尚未初始化,须要使用官方提供的redis-trib工具。

咱们固然能够在任意一个redis节点上运行对应的工具来初始化整个集群,但这么作显然有些不太合适,咱们但愿每一个节点的职责尽量地单一,因此最好单独起一个pod来运行整个集群的管理工具。

在这里须要先介绍一下redis-trib,它是官方提供的redis-cluster管理工具,能够实现redis集群的建立、更新等功能,在早期的redis版本中,它是以源码包里redis-trib.rb这个ruby脚本的方式来运做的(pip上也能够拉到python版本,但我运行失败),如今(我使用的5.0.3)已经被官方集成进redis-cli中。

开始初始化集群,首先在k8s上建立一个ubuntu的pod,用来做为管理节点:

kubectl run -i --tty redis-cluster-manager --image=ubuntu --restart=Never /bin/bash
复制代码

进入pod内部先安装一些工具,包括wget,dnsutils,而后下载和安装redis:

wget http://download.redis.io/releases/redis-5.0.3.tar.gz
tar -xvzf redis-5.0.3.tar.gz
cd redis-5.0.3.tar.gz && make
复制代码

编译完毕后redis-cli会被放置在src目录下,把它放进/usr/local/bin中方便后续操做

接下来要获取已经建立好的6个节点的host ip,能够经过nslookup结合StatefulSet的域名规则来查找,举个例子,要查找redis-app-0这个pod的ip,运行以下命令:

root@redis-cluster-manager:/# nslookup redis-app-0.redis-service
Server:		10.96.0.10
Address:	10.96.0.10#53

Name:	redis-app-0.redis-service.gold.svc.cluster.local
Address: 172.17.0.10
复制代码

172.17.0.10就是对应的ip。此次部署咱们使用0,1,2做为Master节点;3,4,5做为Slave节点,先运行下面的命令来初始化集群的Master节点:

redis-cli --cluster create 172.17.0.10:6379 172.17.0.11:6379 172.17.0.12:6379
复制代码

而后给他们分别附加对应的Slave节点,这里的cluster-master-id在上一步建立的时候会给出:

redis-cli --cluster add-node 172.17.0.13:6379 172.17.0.10:6379 --cluster-slave --cluster-master-id adf443a4d33c4db2c0d4669d61915ae6faa96b46
复制代码
redis-cli --cluster add-node 172.17.0.14:6379 172.17.0.11:6379 --cluster-slave --cluster-master-id 6e5adcb56a871a3d78343a38fcdec67be7ae98f8
复制代码
redis-cli --cluster add-node 172.17.0.16:6379 172.17.0.12:6379 --cluster-slave --cluster-master-id c061e37c5052c22f056fff2a014a9f63c3f47ca0
复制代码

集群初始化后,随意进入一个节点检查一下集群信息:

至此,集群初始化完毕,咱们进入一个节点来试试,注意在集群模式下redis-cli必须加上-c参数才可以访问其余节点上的数据:

建立Service

如今进入redis集群中的任意一个节点均可以直接进行操做了,可是为了可以对集群其余的服务提供访问,还须要创建一个service来实现服务发现和负载均衡(注意这里的service和咱们以前建立的headless service不是一个东西)

yaml文件以下:

apiVersion: v1
kind: Service
metadata:
  name: gold-redis
  labels:
    app: redis
spec:
  ports:
  - name: redis-port
    protocol: "TCP"
    port: 6379
    targetPort: 6379
  selector:
    app: redis
    appCluster: redis-cluster
复制代码

部署完作个测试:

很nice,到这里全部的工做就完毕了~

参考文章:

相关文章
相关标签/搜索