探索Redis集群:(五)基于K8S搭建Redis集群、客户端访问

探索Redis集群 -(五)基于K8S搭建Redis集群、客户端访问

前情提要:

计划接下来用两周的时间,搞清楚Redis Cluster的相关内容。既然要深入了解集群,自然就需要解答以下几个基本问题:

  1. 集群中各节点的表现形式是什么?节点的增加和减少是怎样进行的?
  2. 数据在集群中是如何分布的?集群是如何处理读写操作的?在节点增减的时候,如何确保读写操作的正常进行?
  3. 集群是如何发现故障的?一旦发现故障,又是怎样进行处理的?
  4. 这种集群方式有没有什么局限?使用时有什么注意事项?
  5. 实际怎么搭建一个Redis集群?怎么使用?

之前完成了Redis集群的第四部分:探索Redis集群:(四)局限与注意事项

今天继续讨论:Redis集群 -(五)基于K8S搭建Redis集群、客户端访问

1. 集群搭建

这部分会使用K8S搭建一个3主3从的redis集群。

使用K8S部署Redis集群是很方便的,K8S可以通过Rancher Desktop很方便的进行安装。

k8s安装好之后信息如下:

bash 复制代码
➜ kubectl get nodes
NAME                   STATUS   ROLES                  AGE   VERSION
lima-rancher-desktop   Ready    control-plane,master   87d   v1.29.3+k3s1
➜ kubectl cluster-info
Kubernetes control plane is running at https://127.0.0.1:6443
CoreDNS is running at https://127.0.0.1:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
Metrics-server is running at https://127.0.0.1:6443/api/v1/namespaces/kube-system/services/https:metrics-server:https/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

接下来开始编写redis.yaml

1.1 配置文件 - configmap

yaml 复制代码
apiVersion: v1
kind: ConfigMap
metadata:
  name: redis-config
  labels:
    app: redis-cluster
data:
  redis.conf: |
    save ""
    appendonly no
    cluster-enabled yes
    cluster-config-file /var/nodes.conf
    cluster-node-timeout 5000
    port 6379

1.2 创建service - 无头服务和ClusterIP服务

这里创建两个service,可以有不同用途。

a. 无头服务(redis-service):

  • clusterIP: None 表示这是一个无头服务(Headless Service)。
  • 无头服务没有固定的 IP 地址,当 DNS 查询这个服务的时候,会返回后端 Pods 的 IP 地址列表,而不是单个服务 IP。
  • 这种类型的服务通常用于服务发现。它允许其他 Pods 直接连接到服务后端的 Pods,而不是通过负载均衡。
  • 无头服务常用于状态保持的应用程序,每个节点可能需要被单独寻址。

b. ClusterIP服务(redis-access-service):

  • type: ClusterIP 表示这是一个默认类型的服务,提供一个稳定的内部集群 IP 地址来代理访问后端 Pods。
  • 当客户端连接到这个服务时,Kubernetes 会提供简单的负载均衡,将连接分发到匹配选择器的 Pods 上。
  • 这种类型的服务适用于客户端只需要与任意后端实例通信的场景,不需要知道具体是哪个 Pod。

以下是具体yaml配置:

yaml 复制代码
---

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

---

apiVersion: v1
kind: Service
metadata:
  name: redis-access-service
  labels:
    app: redis-cluster
spec:
  type: ClusterIP
  ports:
    - name: redis-port
      protocol: TCP
      port: 6379
      targetPort: 6379
  selector:
    app: redis-cluster

1.3 有状态的应用 - StatefulSet

使用 StatefulSet,每个 Pod 都有一个固定的名称,格式为 -,其中 是从 0 开始的索引。这些 Pods 会按顺序创建和删除,确保有序性和稳定性。每个 Pod 的存储也会保持与 Pod 的绑定,即使 Pod 被重新调度到其他节点上。

yaml 复制代码
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis-cluster
  labels:
    app: redis-cluster
spec:
  serviceName: redis-service
  replicas: 6
  selector:
    matchLabels:
      app: redis-cluster
  template:
    metadata:
      labels:
        app: redis-cluster
    spec:
      terminationGracePeriodSeconds: 20
      containers:
        - name: redis
          image: redis:6.2
          command:
            - "redis-server"
          args:
            - "/etc/redis/redis.conf"
            - "--protected-mode"
            - "no"
          resources:
            requests:
              cpu: 100m
              memory: 500Mi
          ports:
            - name: redis
              containerPort: 6379
              protocol: TCP
            - name: cluster
              containerPort: 16379
              protocol: TCP
          volumeMounts:
            - name: redis-config
              mountPath: /etc/redis
      volumes:
        - name: redis-config
          configMap:
            name: redis-config

1.4 应用到K8S

把以上的配置内容拼接到redis.yaml中后,执行kubectl apply -f redis.yaml进行集群部署:

bash 复制代码
➜ kubectl apply -f redis.yaml
configmap/redis-config created
service/redis-service created
service/redis-access-service created
statefulset.apps/redis-cluster created

通过kubectl get all | grep redis查看创建情况,最终会成功创建一个statefulset,两个service,六个pod,如下:

bash 复制代码
➜ kubectl get all | grep redis
pod/redis-cluster-0                     1/1     Running   0          17s
pod/redis-cluster-1                     1/1     Running   0          16s
pod/redis-cluster-2                     1/1     Running   0          15s
pod/redis-cluster-3                     1/1     Running   0          14s
pod/redis-cluster-4                     1/1     Running   0          13s
pod/redis-cluster-5                     1/1     Running   0          12s
service/redis-service          ClusterIP   None            <none>        6379/TCP   17s
service/redis-access-service   ClusterIP   10.xx.xx.xx   <none>        6379/TCP   17s
statefulset.apps/redis-cluster   6/6     17s

1.5 集群初始化

通过执行以下命令进行集群初始化:

bash 复制代码
kubectl exec -it redis-cluster-0 -- redis-cli -p 6379 --cluster create --cluster-replicas 1 $(kubectl get pods -l app=redis-cluster -o jsonpath='{range .items[*]}{.status.podIP}:6379 {end}')

解释下这个命令:

  • redis-cluster-0:由于是statefulset启动的pod,因此pod会按数字顺序来命名,redis-cluster-0就是启动的6个pod的第一个,也就是第一个redis实例。
  • kubectl exec -it redis-cluster-0 -- redis-cli -p 6379: 这部分使用kubectl exec命令在名为redis-cluster-0的Pod中执行redis-cli命令行工具,-p 6379指定了Redis服务器监听的端口。
  • --cluster create --cluster-replicas 1: 这部分是传递给redis-cli的参数,告诉redis-cli要创建一个Redis集群,并且每个主节点都有一个副本(从节点)。
  • $(kubectl get pods -l app=redis-cluster -o jsonpath='{range .items[*]}{.status.podIP}:6379 {end}'): 这部分是一个命令替换,使用kubectl get pods命令获取默认命名空间中所有带有标签app=redis-cluster的Pods的IP地址,并且将这些地址格式化为redis-cli需要的格式(即:)。-o jsonpath='{...}'指定了如何从返回的JSON对象中提取数据。
    • 这个命令最终会输出每个pod的ip及端口,类似内容:10.xx.0.101:6379 10.xx.0.102:6379 10.xx.0.103:6379 10.xx.0.104:6379 10.xx.0.105:6379 10.xx.0.106:6379

执行集群初始化命令之后会自动建议主从分配及槽分配,直接输入yes就会自动配置:

bash 复制代码
➜ kubectl exec -it redis-cluster-0 -- redis-cli -p 6379 --cluster create --cluster-replicas 1 $(kubectl get pods -l app=redis-cluster -o jsonpath='{range .items[*]}{.status.podIP}:6379 {end}')
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 10.xx.0.102:6379 to 10.xx.0.106:6379
Adding replica 10.xx.0.103:6379 to 10.xx.0.105:6379
Adding replica 10.xx.0.101:6379 to 10.xx.0.104:6379
M: m1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 10.xx.0.106:6379
   slots:[0-5460] (5461 slots) master
M: m2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 10.xx.0.105:6379
   slots:[5461-10922] (5462 slots) master
M: m3xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 10.xx.0.104:6379
   slots:[10923-16383] (5461 slots) master
S: s3xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 10.xx.0.101:6379
   replicates m3xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
S: s1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 10.xx.0.102:6379
   replicates m1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
S: s2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 10.xx.0.103:6379
   replicates m2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
.
>>> Performing Cluster Check (using node 10.xx.0.106:6379)
M: m1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 10.xx.0.106:6379
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
S: s2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 10.xx.0.103:6379
   slots: (0 slots) slave
   replicates m2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
S: s3xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 10.xx.0.101:6379
   slots: (0 slots) slave
   replicates m3xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
S: s1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 10.xx.0.102:6379
   slots: (0 slots) slave
   replicates m1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
M: m2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 10.xx.0.105:6379
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
M: m3xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 10.xx.0.104:6379
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

1.6 访问集群

访问任一个pod打开redis-cli -p 6379 -c查看集群。

可以看到集群当前已知的节点总数为6个,包括3个主节点和3个从节点:

bash 复制代码
➜  kubectl exec -it redis-cluster-0 -- redis-cli -p 6379 -c
127.0.0.1:6379> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6

设置一个值会重定向到另一个节点:

bash 复制代码
127.0.0.1:6379> set a 1
-> Redirected to slot [15495] located at 10.xx.0.105:6379
OK

集群节点信息:

bash 复制代码
127.0.0.1:6379> cluster nodes
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 10.xx.0.xxx:6379@16379 slave xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 0 xxxxx 2 connected
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 10.xx.0.xxx:6379@16379 slave xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 0 xxxxx 3 connected
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 10.xx.0.xxx:6379@16379 slave xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 0 xxxxx 1 connected
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 10.xx.0.xxx:6379@16379 myself,master - 0 xxxxx 1 connected 0-5460
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 10.xx.0.xxx:6379@16379 master - 0 xxxxx 2 connected 5461-10922
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 10.xx.0.xxx:6379@16379 master - 0 xxxxx 3 connected 10923-16383

2. 客户端访问

2.1 命令行访问

进入任一pod:

bash 复制代码
kubectl exec -it <redis-pod-name> -- redis-cli -c

2.2 SpringBoot访问

SpringBoot应用和redis集群部署在同一个namespace下(当前是default),就可以通过redis-access-service:6379直接访问Redis集群:

yml 复制代码
spring:
  redis:
    cluster:
      max-redirects: 3
      nodes:
        - redis-access-service:6379
    lettuce:
      pool:
        max-active: 2
        max-wait: -1

当然,也可以配置多个node节点,类似下面:

yml 复制代码
spring:
  redis:
    cluster:
      nodes:
        - redis-node-1:6379
        - redis-node-2:6379
        - redis-node-3:6379

2.3 Python访问

启动一个Python的pod:

bash 复制代码
➜ kubectl run python-pod --image=python:3 -- sleep 3600 

pod/python-pod created

进入Python pod:

bash 复制代码
kubectl exec -it python-pod -- /bin/bash

在Python pod安装Redis库:

bash 复制代码
pip install redis-py-cluster 

在Python pod中打开python命令行,测试访问集群、执行set操作、输出集群信息:

  • 访问集群使用集群service: startup_nodes = [{"host":"redis-access-service","port": "6379"}]
bash 复制代码
root@python-pod:/# python3
Python 3.12 [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from rediscluster import RedisCluster
>>> startup_nodes = [{"host":"redis-access-service","port": "6379"}]
>>> redis_cluster = RedisCluster(startup_nodes=startup_nodes, decode_responses=True)
>>> redis_cluster.set("key", "value")
True
>>> redis_cluster.get("key")
'value'
>>> redis_cluster.cluster_info()
{'10.42.0.108:6379': {'cluster_state': 'ok', 'cluster_slots_assigned': 16384, 'cluster_slots_ok': 16384, 'cluster_slots_pfail': 0, 'cluster_slots_fail': 0, 'cluster_known_nodes': 6, 'cluster_size': 3, ...}}
相关推荐
CoderIsArt44 分钟前
Redis的三种模式:主从模式,哨兵与集群模式
数据库·redis·缓存
P.H. Infinity4 小时前
【RabbitMQ】03-交换机
分布式·rabbitmq
ketil275 小时前
Redis - String 字符串
数据库·redis·缓存
龙哥·三年风水6 小时前
群控系统服务端开发模式-应用开发-个人资料
分布式·php·群控系统
王佑辉7 小时前
【redis】延迟双删策略
redis
生命几十年3万天7 小时前
redis时间优化
数据库·redis·缓存
funnyZpC8 小时前
quartz集群增强版🎉
java·分布式·开源·集群·定时任务
明达技术9 小时前
工业4.0时代下的分布式IO模块
分布式
Shenqi Lotus9 小时前
Redis-“自动分片、一定程度的高可用性”(sharding水平拆分、failover故障转移)特性(Sentinel、Cluster)
redis·sentinel·cluster·failover·sharding·自动分片·水平拆分
天冬忘忧10 小时前
Spark 程序开发与提交:本地与集群模式全解析
大数据·分布式·spark