探索Redis集群 -(五)基于K8S搭建Redis集群、客户端访问
前情提要:
计划接下来用两周的时间,搞清楚Redis Cluster的相关内容。既然要深入了解集群,自然就需要解答以下几个基本问题:
- 集群中各节点的表现形式是什么?节点的增加和减少是怎样进行的?
- 数据在集群中是如何分布的?集群是如何处理读写操作的?在节点增减的时候,如何确保读写操作的正常进行?
- 集群是如何发现故障的?一旦发现故障,又是怎样进行处理的?
- 这种集群方式有没有什么局限?使用时有什么注意事项?
- 实际怎么搭建一个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
- 这个命令最终会输出每个pod的ip及端口,类似内容:
执行集群初始化命令之后会自动建议主从分配及槽分配,直接输入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, ...}}