• Install Redis Cluster(1master-2slave) on Kubernetes


    目录

    Node & Software & Docker Images Lists

    Prerequisites

    Architecture

    Setting up your Redis cluster

    Creating Namespace 

    Creating StorageClass

    Creating Persistent volumes

     Creating ConfigMap 

    Creating StatefulSet

    Creating Headless Service

    Testing Redis Cluster

    Deploying Application 

    Review

    Flaw

    Reference documents


    Node & Software & Docker Images Lists

    HOSTNAME

    IPNODE TYPECONFIG
    master1192.168.1.100master4vCPU8G
    Software NameVersion
    kubeadmv1.26.0
    kubectlv1.26.0
    kubeletv1.26.0
    cri-containerd-cni1.6.14
    centos7.9
    Image TypeVersion
    k8sregistry.aliyuncs.com/google_containers/coredns:v1.9.3
    registry.aliyuncs.com/google_containers/etcd:3.5.6-0
    registry.aliyuncs.com/google_containers/kube-apiserver:v1.26.0
    registry.aliyuncs.com/google_containers/kube-controller-manager:v1.26.0
    registry.aliyuncs.com/google_containers/kube-proxy:v1.26.0
    registry.aliyuncs.com/google_containers/kube-scheduler:v1.26.0
    registry.aliyuncs.com/google_containers/pause:3.9
    calicodocker.io/calico/apiserver:v3.24.5
    docker.io/calico/cni:v3.24.5
    docker.io/calico/kube-controllers:v3.24.5
    docker.io/calico/node:v3.24.5
    docker.io/calico/pod2daemon-flexvol:v3.24.5
    docker.io/calico/typha:v3.24.5
    quay.io/tigera/operator:v1.28.5
    redisdocker.io/library/redis:6.2.3-alpine
    dashborddocker.io/kubernetesui/dashboard:v2.7.1
    pythondocker.io/library/python:3.11.3

    Prerequisites

    Before we begin, there are a few things that you will need to make sure you have installed:

    1. Kubernetes Cluster
    2. Containerd

    If you haven't installed,you could refer to this guide: Install Kubernetes 1.26 on Centos 7.9(Contianerd as CRI )_JJH的创世纪的博客-CSDN博客

    Architecture

    This article explores how to set up a Redis cluster (1 Master- 2 Slave) on Kubernetes and the architecture shown in the diagram below. We set StatefulSet as manager and run 3 redis-pods instances, create 1 Storageclass and 3 PVs, and a headless service exposed to other intranet pods that here is application-pod.

    Setting up your Redis cluster

    ​Before you go much further, you should have a Kubernetes cluster up and running. This article makes use of Redis version 6. Follow the step-by-step instructions here, and your Redis cluster will be ready without any trouble. ​

    You can download all files here.kubernetes-redis/redis-cluster-1master-2slave at main · ck784101777/kubernetes-redis · GitHub

    Creating Namespace 

    Create a namespace for a Redis server setup:

    kubectl create namespace redis
    

     Then you can check it.

    kubectl get pods -n redis

    Creating StorageClass

    ​A storage class links to a provisioner, which is a plugin that can reserve disk space or purchase volumes to your cloud provider on your behalf. ​In this guide we uses persistent volume for the purposes of the demo with local storage (a file system folder). 

    Create a storage class, which points to the local storage, using the following manifest code:

    kubectl apply -f storageclass.yaml
    1. apiVersion: storage.k8s.io/v1
    2. kind: StorageClass
    3. metadata:
    4. name: local-storage
    5. provisioner: kubernetes.io/no-provisioner
    6. volumeBindingMode: WaitForFirstConsumer
    7. allowVolumeExpansion: true
    8. reclaimPolicy: Delete

    Then you can check it.(By the way,Storageclass as a kind of resources has no concept of namespace)

    kubectl get storageclass

    Creating Persistent volumes

    Persistent volumes  (PVs) are used to create a storage size. In this demo, you will create a Redis cluster with three pods (one master and two slaves). Therefore, create three PVs.

    The following code creates three PVs using the local storage provisioner:

    kubectl apply -f pv.yaml
    1. apiVersion: v1
    2. kind: PersistentVolume
    3. metadata:
    4. name: redis-pv1
    5. spec:
    6. storageClassName: redis-storage
    7. capacity:
    8. storage: 1Gi
    9. accessModes:
    10. - ReadWriteOnce
    11. hostPath:
    12. path: "/storage/data1"
    13. ---
    14. apiVersion: v1
    15. kind: PersistentVolume
    16. metadata:
    17. name: redis-pv2
    18. spec:
    19. storageClassName: redis-storage
    20. capacity:
    21. storage: 1Gi
    22. accessModes:
    23. - ReadWriteOnce
    24. hostPath:
    25. path: "/storage/data2"
    26. ---
    27. apiVersion: v1
    28. kind: PersistentVolume
    29. metadata:
    30. name: redis-pv3
    31. spec:
    32. storageClassName: redis-storage
    33. capacity:
    34. storage: 1Gi
    35. accessModes:
    36. - ReadWriteOnce
    37. hostPath:
    38. path: "/storage/data3"
    1. kubectl get pv
    2. NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
    3. local-pv1 1Gi RWO Retain Bound redis/data-redis-0 local-storage 16h
    4. local-pv2 1Gi RWO Retain Bound redis/data-redis-1 local-storage 16h
    5. local-pv3 2Gi RWO Retain Bound redis/data-redis-2 local-storage 16h

     Creating ConfigMap 

    The ConfigMap in the Kubernetes cluster is a key-value store. You can use the config information of Redis in the Kubernetes cluster as a ConfigMap. Get the full code of the ConfigMap manifest here redis-config.yaml. Download this file and save to your loacl system. 

    One thing important is to change the password of master and slave with your desired password, which is needed for authentication. 

    kubectl apply -n redis -f config.yaml

    Creating StatefulSet

    StatefulSet is the workload API object used to manage stateful applications such as Mysql、Oracle、Redis.

    The StatefulSet offers ordered pod names starting from zero and recreates the pod with the same name whenever the pod dies or crashes. A pod can fail at any time. The persistent pod identifier uses this feature (recreating the pod with the same name) to match existing persistent volume (storage volume attached to the failed pod) to the newly created pod.

    Create redis-statefulset.yaml which define a initContenter and run a series of shell commands.These commands aim at make a election strategy when master-pod down and following below diagram.

    Node The command  of `redis slaveof` can turn the current server into a slave server of the specified server. The format is `slaveof hostip port` 

    1. apiVersion: apps/v1
    2. kind: StatefulSet
    3. metadata:
    4. name: redis
    5. spec:
    6. serviceName: redis
    7. replicas: 3
    8. selector:
    9. matchLabels:
    10. app: redis
    11. template:
    12. metadata:
    13. labels:
    14. app: redis
    15. spec:
    16. initContainers:
    17. - name: config
    18. image: redis:6.2.3-alpine
    19. command: [ "sh", "-c" ]
    20. args:
    21. - |
    22. cp /tmp/redis/redis.conf /etc/redis/redis.conf
    23. echo "finding master..."
    24. MASTER_FDQN=`hostname -f | sed -e 's/redis-[0-9]\./redis-0./'`
    25. if [ "$(redis-cli -h sentinel -p 5000 ping)" != "PONG" ]; then
    26. echo "master not found, defaulting to redis-0"
    27. if [ "$(hostname)" == "redis-0" ]; then
    28. echo "this is redis-0, not updating config..."
    29. else
    30. echo "updating redis.conf..."
    31. echo "slaveof $MASTER_FDQN 6379" >> /etc/redis/redis.conf
    32. fi
    33. else
    34. echo "sentinel found, finding master"
    35. MASTER="$(redis-cli -h sentinel -p 5000 sentinel get-master-addr-by-name mymaster | grep -E '(^redis-\d{1,})|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})')"
    36. echo "master found : $MASTER, updating redis.conf"
    37. echo "slaveof $MASTER 6379" >> /etc/redis/redis.conf
    38. fi
    39. volumeMounts:
    40. - name: redis-config
    41. mountPath: /etc/redis/
    42. - name: config
    43. mountPath: /tmp/redis/
    44. containers:
    45. - name: redis
    46. image: redis:6.2.3-alpine
    47. command: ["redis-server"]
    48. args: ["/etc/redis/redis.conf"]
    49. ports:
    50. - containerPort: 6379
    51. name: redis
    52. volumeMounts:
    53. - name: data
    54. mountPath: /data
    55. - name: redis-config
    56. mountPath: /etc/redis/
    57. volumes:
    58. - name: redis-config
    59. emptyDir: {}
    60. - name: config
    61. configMap:
    62. name: redis-config
    63. volumeClaimTemplates:
    64. - metadata:
    65. name: data
    66. spec:
    67. accessModes: [ "ReadWriteOnce" ]
    68. storageClassName: "redis-storage"
    69. resources:
    70. requests:
    71. storage: 500Mi
    kubectl apply -n redis -f redis-statefulset.yaml
    1. kubectl get pods -n redis
    2. NAME READY STATUS RESTARTS AGE
    3. redis-0 1/1 Running 1 (12m ago) 15h
    4. redis-1 1/1 Running 1 (12m ago) 15h
    5. redis-2 1/1 Running 0 23s

    Then you can use `kubectl logs` to find how the election strategy run.As you can see,the "redis-0" was selected to Master,and "redis-1","redis-2" were selected to Slave.

    1. [root@master1 ~]# kubectl logs -f -nredis redis-0 -c config
    2. finding master...
    3. Could not connect to Redis at sentinel:5000: Try again
    4. master not found, defaulting to redis-0
    5. this is redis-0, not updating config...
    6. [root@master1 ~]# kubectl logs -f -nredis redis-1 -c config
    7. finding master...
    8. Could not connect to Redis at sentinel:5000: Try again
    9. master not found, defaulting to redis-0
    10. updating redis.conf...
    11. [root@master1 ~]# kubectl logs -f -nredis redis-2 -c config
    12. finding master...
    13. Could not connect to Redis at sentinel:5000: Try again
    14. master not found, defaulting to redis-0
    15. updating redis.conf...

    Next we try to delete "redis-2" and the StatefulSet recreate a new pod named "redis-2",too.And of cause , is still be seleted to Slave.By the way,we see log of "Could not connect to Redis at sentinel:5000:".This is because we dont set a redis-sentinel,the Sentinel node is implemented through k8s’ built-in failure recovery mechanism + the script in the StatefulSet configuration content.

    1. [root@master1 ~]# kubectl delete pod -nredis redis-2
    2. pod "redis-2" deleted
    3. [root@master1 ~]# kubectl get pod -nredis
    4. NAME READY STATUS RESTARTS AGE
    5. curl 1/1 Running 2 (12m ago) 15h
    6. redis-0 1/1 Running 1 (12m ago) 15h
    7. redis-1 1/1 Running 1 (12m ago) 15h
    8. redis-2 0/1 Init:0/1 0 2s
    9. [root@master1 ~]# kubectl logs -f -nredis redis-2 -c config
    10. finding master...
    11. Could not connect to Redis at sentinel:5000: Name does not resolve
    12. master not found, defaulting to redis-0
    13. updating redis.conf...

    Creating Headless Service

    Headless services are usually used in conjunction with StatefulSet. Using headless services can expose breakpoints to other applications for their access .The  particularity is only internal pods can communicate with each other. Not exposed to applications outside the Kubernetes cluster

    1. apiVersion: v1
    2. kind: Service
    3. metadata:
    4. name: redis
    5. spec:
    6. clusterIP: None
    7. ports:
    8. - port: 6379
    9. targetPort: 6379
    10. name: redis
    11. selector:
    12. app: redis
    kubectl apply -n redis -f redis-service.yaml
    1. kubectl get service -n redis
    2. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
    3. redis ClusterIP None <none> 6379/TCP 16h

     If other pod run to access Headless Service,the can via domain name which is "redis.redis"(the full name is redis.redis.svc.cluster.local).And the single domain name of each redis-pod such as "redis-0" is "redis-0.redis.redis.svc.cluster.local".So the syntax is ...svc.cluster.local

    1. [root@master1 nginx]# kubectl run curl --image=radial/busyboxplus:curl -it
    2. If you don't see a command prompt, try pressing enter.
    3. [ root@curl:/ ]$ nslookup redis.redis
    4. Server: 10.96.0.10
    5. Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
    6. Name: redis.redis
    7. Address 1: 10.244.137.99 redis-0.redis.redis.svc.cluster.local
    8. Address 2: 10.244.137.116 redis-1.redis.redis.svc.cluster.local
    9. Address 3: 10.244.137.114 redis-2.redis.redis.svc.cluster.loca
    10. [ root@curl:/ ]$ nslookup redis-0.redis.redis.svc.cluster.local
    11. Server: 10.96.0.10
    12. Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
    13. Name: redis-0.redis.redis.svc.cluster.local
    14. Address 1: 10.244.137.97 redis-0.redis.redis.svc.cluster.local
    15. [ root@curl:/ ]$ nslookup redis-1.redis.redis.svc.cluster.local
    16. Server: 10.96.0.10
    17. Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
    18. Name: redis-1.redis.redis.svc.cluster.local
    19. Address 1: 10.244.137.96 redis-1.redis.redis.svc.cluster.local
    20. [ root@curl:/ ]$ nslookup redis-2.redis.redis.svc.cluster.local
    21. Server: 10.96.0.10
    22. Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
    23. Name: redis-2.redis.redis.svc.cluster.local
    24. Address 1: 10.244.137.102 redis-2.redis.redis.svc.cluster.local

    Testing Redis Cluster

    Now we have created a Redis cluster with one master node and two slave nodes. Pod redis-0 will act as master node and pod redis-1 and redis-2 will act as slave nodes. You can check the log and description of redis-0.Or go inside the container to see the cluster information:

    1. kubectl -n redis exec -it redis-0 -- sh
    2. ----
    3. redis-cli 
    4. auth a-very-complex-password-here
    5. info replication

    The figure above shows how many slaves the master is connected to, as well as the slave's IP address and other information.Similarly, you can check the log of the slave pod to see that the master and slave are connected successfully:

    1. $kubectl -n redis exec -it redis-1 -- sh
    2. ---
    3. redis-cli 
    4. auth a-very-complex-password-here
    5. info replication

    Note that you can only write data on the master pod; slave pods are only for reading. So, log into the master pod and create some key-value data. Then check to see if the same data is replicated in the slave pod.

    1. $kubectl -n redis exec -it redis-0 -- sh
    2. --
    3. redis-cli 
    4. auth a-very-complex-password-here
    5. SET name1 zhangsan
    6. SET name1 lisi
    7. SET name1 wangwu
    1. $kubectl -n redis exec -it redis-1 -- sh
    2. --
    3. redis-cli 
    4. auth a-very-complex-password-here
    5. keys *

    Deploying Application 

    Now we going to Deploy a application which use python3 and import redis module.Code a simple sample to connect to redis and try read from redis cluster via "redis.redis:6379" and write to redis cluster via "0.redis.redis.svc.cluster.local:6379".(Don't forget replace your password)

    1. import redis
    2. wredis_url = "redis-0.redis.redis.svc.cluster.local"
    3. rredis_url = "redis.redis"
    4. rr = redis.Redis(host=rredis_url, port=6379, decode_responses=True,password="Your password")
    5. wr = redis.Redis(host=wredis_url, port=6379, decode_responses=True,password="Your password")
    6. wr.set('age', '18')
    7. print(rr['age'])

     You could run a python image and following below test:

    1. kubectl run application --image=docker.io/library/python:3.11.3 -it /bin/bash
    2. --
    3. pip3 install redis
    4. cat < ~/test.py
    5. import redis
    6. wredis_url = "redis-0.redis.redis.svc.cluster.local"
    7. rredis_url = "redis.redis"
    8. rr = redis.Redis(host=rredis_url, port=6379, decode_responses=True,password="Your password")
    9. wr = redis.Redis(host=wredis_url, port=6379, decode_responses=True,password="Your password")
    10. wr.set('age', '18')
    11. print(rr['age'])
    12. EOF
    13. python3 ~/test.py
    14. [OUTPUT]
    15. 18

    Review

    This article configures a redis cluster with one master and two slaves. The focus is on the configuration with StatefulSet. If you have experience, you may ask, where are the sentinel nodes deployed? This is a good question. In fact, in this configuration case, the Sentinel node is implemented through k8s’ built-in failure recovery mechanism + the script in the StatefulSet configuration content. Let's review the deployment process:

    • 1. Create a namespace. We don't want to expose the redis service to other applications in k8s. At the same time, creating a separate namespace helps manage resources, such as resource quotas (CPU, memory, IO)
    • 2-3. Section 2-3 describes how to create a persistent volume for redis. First, define a Storageclass. Use StorageClass to dynamically create PV according to PVC. Then we created a specific PV, and the creation of a PVC is not shown here, that is, it does not declare how the PV is used. The pv-pvc matching mechanism here is implemented by volumeClaimTemplates. By defining volumeClaimTemplates in Statefulset and specifying the Storageclass of volumeClaimTemplates as the created Storageclass, the pv-pvc relationship is dynamically established
    • 4. Created configMap to save redis configuration information
    • 5. Create a StatefulSet stateful service, the StatefulSet controller will create pods in order (starting from -0), and create the same pod name when the pod dies or restarts, the first pod will be assumed to be the master, and the others The pods will become slaves. Therefore, for a stateful application cluster, we need an ordered pod name, otherwise the pods will not be able to identify each other. And a master-slave election mechanism is added to StatefulSet, that is, redis-0 will be the master and others will be slaves. Since the pod is stateful, even if it dies
    • 6.Create a application with python3 and embed test code to check read and write to redis cluster.

    In the example configuration in this article, the main pod (master) is only allowed to write data. This is because data synchronization cannot be handled if multiple pods are allowed to write data, the master pod will push the data changes to the slave pods and always stay in sync.

    This approach ensures that data is replicated smoothly across all pods. The slave pods are used for data retrieval purposes only. , the first pod will be assumed to be the master, and the other pods will be slaves. Therefore, for a stateful application cluster, you need an ordered pod name.

    However, Pods are assigned a random name in a Kubernetes deployment. Also, in order to match an existing persistent volume with a newly created pod, the same pod name is required whenever the pod is restarted.

    A StatefulSet controller in a Kubernetes cluster will help you easily deploy stateful applications by giving each pod an ordered number starting from zero and creating the same pod name when the pod dies or restarts. The functionality provided by the StatefulSet controller is very much needed to set up a cluster environment for stateful applications.

    Deploying stateful applications on a Kubernetes cluster has always been complicated. This article simplifies the complexities involved in deploying Redis Cluster.

    Flaw

    Finally, I want to talk about some of the flaws of this guide.

    1. I didn't prepare a Kubernetes cluster by 3 nodes due to insufficient resources on my computer.Because of that,I did't set node affinity for redis pod.Ideally,the master redis-pod and the slave redis-pod should be assigned to different nodes then we can avoid single point of failure.
    2. Regarding persistent storage, in this guide we are using local volumes as persistent storage, which is definitely a bad approach. Using local volumes as persistent storage means that pods are tied to persistent storage, and we cannot avoid data loss when a node goes down.So the better way is depart away them,using nfs,ceph or other reliable storage methoh as persistent storage and via mount to use it.So better way is to separate them, using nfs, ceph or other reliable storage method as persistent storage and use it by mounting.
    3. Using the method of this tutorial will creates a time vacuum.This question is about how Kubernetes check the health status of pod and how many time cost that the pod be recreate.It will spend some time to recreate a new pod when master pod died and write operations cannot be performed during this time.Let think about the traditional approach ,which we will set a sentinel check the health status of redis cluster and select a new master,and the transfer that slave to master is very fast.Unfortunately, using the architecture of this tutorial cannot solve the problem of single point of failure of the master node.

    Reference documents

    Service | Kubernetes

    K8S 快速入门(十六)实战篇:StorageClass(存储类)_犬豪的博客-CSDN博客

    StatefulSets | Kubernetes

    (十七)高并发redis学习笔记:3节点哨兵搭建_redis三节点的哨兵怎么配置_秦怀的博客-CSDN博客

    redis之sentinel模式_redis sentinel_九千⑦的博客-CSDN博客

    Redis哨兵服务器,检测主服务器宕机_JJH的创世纪的博客-CSDN博客

    Redis Enterprise for Kubernetes architecture | Redis Documentation Center

    Redis-哨兵选出Master较慢_redis宕机后,哨兵不会选举新的主机_iuAlex的博客-CSDN博客

    K8S中pod健康状态的检查_k8s pod 健康检查 进程状态_EgretLu的博客-CSDN博客

    https://www.cnblogs.com/xinzhiyu/p/16909146.html

  • 相关阅读:
    View 自定义 - 属性 xml
    计算机毕业设计Java校园课室资源预约系统设计与实现(系统+源码+mysql数据库+lw文档)
    聊聊流式数据湖Paimon(五)
    LeetCode 104. 二叉树的最大深度
    基于springboot实现疫苗接种管理系统项目【项目源码】
    WordPress主题网站首页添加好看的四格小工具教程
    【c语言基础题】— —第五版,可当作日常练习和期末复习,有奇效哟!
    读取文件中的数据排序后再次写到本地
    具有自适应边界与最优引导的莱维飞行蚁狮优化算法-附代码
    intellij idea的快速配置详细使用
  • 原文地址:https://blog.csdn.net/ck784101777/article/details/130394590