调查Kubernetes中Redis的操作员以实现冗余
首先
最近我正在使用Kubernetes(k8s)接触Redis。当将Redis作为服务的一部分使用时,我非常关心在节点故障或中间件更新时是否会出现宕机问题。Redis本身具备冗余机制,但在使用k8s进行部署时,有操作员(Operator)的选择。本文将总结我对Redis Operator的调研结果。
Redis的冗余解决方案
首先是Redis自身的冗余方式。由于Redis Operator基于Redis的方式,我们在这里梳理一下。
Redis可以采用主从架构,并且可以复制数据(Replication)。但是,Replication没有故障转移功能,即使主节点宕机,从节点也不会晋升为主节点。
Redis Sentinel除了主从节点,还有一个监视者Sentinel。Sentinel会监视主节点的状态,并在主节点宕机时执行故障转移。
Redis Cluster也支持故障转移。此外,它采用多主节点结构,将数据分散到多个主节点上进行分片。但是,它无法使用多个数据库并且无法使用SELECT命令。
似乎还有一些与外部工具结合的方法。
※请查阅
Redis、Sentinel的功能
在本文中,我将介绍关于Redis Sentinel的内容。
首先,我将谈及Redis和Sentinel的功能,然后在后面的内容中我会详细说明这些功能在操作者角色中的变化。
Redis Sentinel的故障转移流程。
以下是用图示记录Redis Sentinel故障转移的流程。
当Sentinel检测到主节点宕机时,它将把其中一个从节点晋升为新主节点。

在每个 Sentinel 上进行故障检测。每隔2秒,Sentinel 向 Redis 发送 hello 消息以确认其状态。如果发现主节点故障,将视为主观下线状态(SDOWN)。
然后,确认其他 Sentinel 的判断,如果发现主节点处于客观下线状态(ODOWN)。是否处于 ODOWN 状态,取决于被判断为 SDOWN 的 Sentinel 数量是否大于等于配置中的 QUORUM 值。
一旦进入 ODOWN 状态,将执行故障转移。

在故障转移过程中,每个Sentinel都会为下一个Redis主节点候选者投票。投票完成后,一个Sentinel将成为领导者,汇总投票结果,并选出获得QUORUM或以上投票数量的Redis作为下一个主节点。然后,成为领导者的Sentinel会更新Redis Sentinel配置,并将副本提升为主节点。如果无法找到获得QUORUM投票数量的Redis,故障转移将失败,并在一定时间后重新尝试。
※ 只需一种选择,请将以下内容用中文进行释义:
将Redis数据持久化
将 Redis 数据保存在磁盘上,并在重启后恢复数据的方法说明。
保存数据的方法有 RDB 和 AOF(追加模式文件)两种。
RDB 方法将数据压缩并保存在文件中。您可以设置保存频率以满足指定时间内更改的数据量。
AOF 方法将数据和命令保存在文件中,而不进行压缩。下面是一个简单的示例。
root@redis-pod:/data# redis-cli set test 1
root@redis-pod:/data# cat appendonlydir/appendonly.aof.1.incr.aof
*2
$6
SELECT
$1
0
*3
$3
set
$4
test
$1
1
我将针对每个比较进行说明。
根据使用场景的不同,两者都有优点和缺点,应根据需要选择其中一种或同时采用两种。
※仅需一种选项:请用中文将以下内容简洁地表达出来。
参考
Redis的安全
在Redis中,您可以设置访问限制。通过bind设置,您可以限制可以访问的客户端的IP地址。
此外,您还可以创建用户并设置身份验证和权限。以下是创建具有set和get命令权限的用户”myaccount”的示例。
root@redis-pod:/data# redis-cli
127.0.0.1:6379> acl list
1) "user default on nopass ~* &* +@all"
127.0.0.1:6379> acl setuser myaccount on ~* &* >mypassword -@all +get +set
OK
127.0.0.1:6379> acl list
1) "user default on nopass ~* &* +@all"
2) "user myaccount on #89e01536ac207279409d4de1e5253e01f4a1769e696db0d6062ca9b8f56767c8 ~* &* -@all +set +get"
127.0.0.1:6379> exit
root@redis-pod:/data# redis-cli --user myaccount --pass mypassword
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:6379> set test aaa
OK
127.0.0.1:6379> get test
"aaa"
127.0.0.1:6379> config get dir
(error) NOPERM this user has no permissions to run the 'config|get' command
※仅需要一种选项:请将以下内容用中文进行本地化改写:
Redis运算符的功能。
本次将介绍使用Sentinel的以下GitHub操作器。
(Note: The translation is provided in Simplified Chinese)
现在可以使用k8s的功能,变得更加方便了。
监控Redis Sentinel的配置和设置。
运营商会确认以下内容,并在有问题时自动进行修正。
・Redis启动的数量与设置一致。
・Sentinel启动的数量与设置一致。
・只有一台主节点启动。
・副本连接到相同的主节点。
・Sentinel监视相同的主节点。
・Sentinel没有监视已经宕机的Redis和Sentinel。
我觉得没有使用Operator与使用Operator相比的一个主要区别是,Operator可以帮助我们确认Redis的数量。如果没有使用Operator,当主节点出现故障并进行故障转移后,配置中将不存在任何副本。而使用Operator,则可以启动一个新的Redis POD作为副本,保持主从复制结构的完整性。
此外,Operator还监控Redis和Sentinel的连接目标,因此当连接目标由于命令错误等原因发生变化时,会对其进行修正。
※ 仅提供一个选项:参考
可以作为自定义资源的一部分进行设置
在操作员中,可以将PV(持久卷)定义为自定义资源的一部分,以实现数据持久化。
通过使用PV,可以将用于Redis外部的数据持久化文件保存起来。如果没有操作员,PV的定义必须与自定义资源分开,因此操作员可以简化资源管理。
尝试使用Redis操作器
我会亲自创建一个具有Operator的k8s环境并进行尝试。
创建k8s环境
首先我们创建一个k8s环境。基本上和以前一样,但是有一次遇到了没有指定软件包版本而导致困扰的经验,所以我会进行指定。
#!/bin/bash -ex
echo 'install docker'
sudo yum install -y docker-20.10.13-2.amzn2.x86_64
sudo systemctl start docker
sudo systemctl enable docker
echo 'install k8s'
echo "`ip -4 a show eth0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}'` `hostname`" | sudo tee -a /etc/hosts
echo "
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=0
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
" | sudo tee -a /etc/yum.repos.d/kubernetes.repo > /dev/null
sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config
sudo yum install -y kubelet-1.24.0-0.x86_64 kubeadm-1.24.0-0.x86_64 kubectl-1.24.0-0.x86_64 --disableexcludes=kubernetes
sudo swapoff -a
sudo systemctl start kubelet
sudo systemctl enable kubelet
echo '
{
"exec-opts": ["native.cgroupdriver=systemd"]
}
' | sudo tee /etc/docker/daemon.json > /dev/null
sudo systemctl daemon-reload
sudo systemctl restart docker
sudo systemctl restart kubelet
sudo kubeadm init
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
echo 'setup control-plane node'
K8S_VERSION=$(kubectl version | base64 | tr -d '\n')
kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$K8S_VERSION"
CONTROL_PLANE=`kubectl get node | grep 'control-plane' | cut -d' ' -f1`
kubectl taint nodes $CONTROL_PLANE node-role.kubernetes.io/master:NoSchedule-
kubectl taint nodes $CONTROL_PLANE node-role.kubernetes.io/control-plane:NoSchedule-
但是,师傅,主从的称呼正在发生变化的趋势,而且命令也稍有变化。
安装Redis Operator
使用Helm安装Redis Operator。
#!/bin/bash -ex
SCRIPT_DIR=$(cd $(dirname $0); pwd)
echo 'install helm'
HELM_VERSION='v3.8.1'
cd $HOME
curl "https://get.helm.sh/helm-${HELM_VERSION}-linux-amd64.tar.gz" -o helm.tar.gz
tar -zxvf helm.tar.gz
mv linux-amd64 helm
echo 'export PATH=$PATH:$HOME/helm' >> $HOME/.bashrc
source .bashrc
helm version
echo 'install redis-operator'
REDIS_OPERATOR_VERSION='v1.1.1'
REDIS_NAMESPACE='redis'
cd $SCRIPT_DIR
helm repo add redis-operator https://spotahome.github.io/redis-operator
helm repo update
helm install redis-operator redis-operator/redis-operator \
--set image.tag=$REDIS_OPERATOR_VERSION \
-n $REDIS_NAMESPACE \
--create-namespace \
-f config/redis-operator-values.yaml
image:
repository: quay.io/spotahome/redis-operator
pullPolicy: Always
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 100m
memory: 128Mi
在安装过程中,您可以指定操作员的内存、CPU等。
创建Redis Sentinel
使用样例参考创建一个yaml文件,并创建Redis Sentinel。
此外,您可以在自定义资源中像Pod一样放入各种配置。
apiVersion: databases.spotahome.com/v1
kind: RedisFailover
metadata:
name: redisfailover
namespace: redis
spec:
sentinel:
replicas: 3
resources:
requests:
cpu: 100m
limits:
memory: 100Mi
redis:
replicas: 2
resources:
requests:
cpu: 100m
memory: 100Mi
limits:
cpu: 400m
memory: 500Mi
storage:
persistentVolumeClaim:
metadata:
name: rf-data
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-storage
resources:
requests:
storage: 1Gi
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
topologyKey: kubernetes.io/hostname
省略自定义资源以外的资源定义。
这次我们将各种设置放在了自定义资源中。
此外,Redis操作员的配置本身只需应用yaml文件即可完成。非常简单方便。
kubectl apply -f rf-basic.yaml
我們來介紹在自訂資源中的設定。
使用PV进行数据持久化。
将PV挂载到/data目录下,并将文件写入其中。
[ec2-user@ip-10-0-0-199 ~]$ kubectl exec -n redis rfr-redisfailover-0 -it -- sh
/data $ ls
dump.rdb
/data $ pwd
/data
出于时间考虑,本次使用了本地存储来编写文章,但也可以使用AWS的EBS或EFS进行存储。
只有一个节点的情况下,使用亲和性可以在节点级别上进行POD的分散配置,但在这种情况下没有特别的意义。
此外,GitHub页面中提到可以利用Secret进行身份验证的设置,但由于发生错误而无法执行。
[ec2-user@ip-10-0-0-53 ~]$ kubectl get pod -n redis
NAME READY STATUS RESTARTS AGE
redis-operator-777cdb655b-f2qxq 1/1 Running 0 143m
rfr-redisfailover-0 0/1 Running 0 35m
rfr-redisfailover-1 0/1 Running 0 35m
rfs-redisfailover-55b5fd485b-g9kz9 1/1 Running 0 35m
rfs-redisfailover-55b5fd485b-ldlvn 1/1 Running 0 35m
rfs-redisfailover-55b5fd485b-qhsct 1/1 Running 0 35m
[ec2-user@ip-10-0-0-53 ~]$ kubectl logs -n redis redis-operator-777cdb655b-f2qxq | tail -n 3
time="2022-05-21T14:09:35Z" level=info msg="podDisruptionBudget updated" namespace=redis podDisruptionBudget=rfs-redisfailover service=k8s.podDisruptionBudget src="poddisruptionbudget.go:79"
time="2022-05-21T14:09:35Z" level=info msg="deployment updated" deployment=rfs-redisfailover namespace=redis service=k8s.deployment src="deployment.go:102"
time="2022-05-21T14:09:35Z" level=error msg="error on object processing: WRONGPASS invalid username-password pair or user is disabled." controller-id=redisfailover object-key=redis/redisfailover operator=redisfailover service=kooper.controller src="controller.go:279"
访问Redis运营商
我将尝试从客户端访问已创建的Redis Sentinel。我将创建一个可以使用redis-cli命令的POD来进行尝试。
[ec2-user@ip-10-0-0-53 ~]$ kubectl apply -f redis.yaml
pod/redis-pod created
[ec2-user@ip-10-0-0-53 ~]$ kubectl get svc -n redis
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
redis-operator ClusterIP 10.105.178.232 <none> 9710/TCP 164m
rfs-redisfailover ClusterIP 10.100.210.109 <none> 26379/TCP 13m
[ec2-user@ip-10-0-0-53 ~]$ kubectl exec -n redis redis-pod -it -- bash
root@redis-pod:/data# redis-cli -h rfs-redisfailover -p 26379 sentinel get-master-addr-by-name mymaster
1) "10.32.0.8"
2) "6379"
root@redis-pod:/data# redis-cli -h 10.32.0.8 set testdata 100
OK
root@redis-pod:/data# redis-cli -h rfs-redisfailover -p 26379 sentinel replicas mymaster
1) 1) "name"
2) "10.32.0.9:6379"
3) "ip"
4) "10.32.0.9"
5) "port"
6) "6379"
7) "runid"
8) "5dd7d0cb8c3aabea01c5ec95cab15ec89a992db8"
9) "flags"
10) "slave"
11) "link-pending-commands"
12) "0"
13) "link-refcount"
14) "1"
15) "last-ping-sent"
16) "0"
17) "last-ok-ping-reply"
18) "109"
19) "last-ping-reply"
20) "109"
21) "down-after-milliseconds"
22) "5000"
23) "info-refresh"
24) "3605"
25) "role-reported"
26) "slave"
27) "role-reported-time"
28) "826627"
29) "master-link-down-time"
30) "0"
31) "master-link-status"
32) "ok"
33) "master-host"
34) "10.32.0.8"
35) "master-port"
36) "6379"
37) "slave-priority"
38) "100"
39) "slave-repl-offset"
40) "381554"
41) "replica-announced"
42) "1"
root@redis-pod:/data# redis-cli -h 10.32.0.9 get testdata
"100"
只要从 Sentinel 的 Service 中确认主地址,就能在 Redis 中进行数据的读写。
确认故障转移时的行为。
然后我们将观察Redis主节点宕机时的故障转移行为。作为宕机的情况,我们将删除Redis主节点的POD。
[ec2-user@ip-10-0-0-53 ~]$ kubectl get pod -n redis -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
redis-operator-777cdb655b-f2qxq 1/1 Running 0 170m 10.32.0.4 ip-10-0-0-53.ap-northeast-1.compute.internal <none> <none>
redis-pod 1/1 Running 0 12m 10.32.0.10 ip-10-0-0-53.ap-northeast-1.compute.internal <none> <none>
rfr-redisfailover-0 1/1 Running 0 19m 10.32.0.8 ip-10-0-0-53.ap-northeast-1.compute.internal <none> <none>
rfr-redisfailover-1 1/1 Running 0 19m 10.32.0.9 ip-10-0-0-53.ap-northeast-1.compute.internal <none> <none>
rfs-redisfailover-55b5fd485b-2967z 1/1 Running 0 19m 10.32.0.5 ip-10-0-0-53.ap-northeast-1.compute.internal <none> <none>
rfs-redisfailover-55b5fd485b-6pw27 1/1 Running 0 19m 10.32.0.6 ip-10-0-0-53.ap-northeast-1.compute.internal <none> <none>
rfs-redisfailover-55b5fd485b-fds5w 1/1 Running 0 19m 10.32.0.7 ip-10-0-0-53.ap-northeast-1.compute.internal <none> <none>
[ec2-user@ip-10-0-0-53 ~]$ kubectl exec -n redis rfs-redisfailover-55b5fd485b-2967z -- redis-cli -p 26379 info | grep mymaster
Defaulted container "sentinel" out of: sentinel, sentinel-config-copy (init)
master0:name=mymaster,status=ok,address=10.32.0.8:6379,slaves=1,sentinels=3
[ec2-user@ip-10-0-0-53 ~]$ kubectl delete pod -n redis rfr-redisfailover-0
pod "rfr-redisfailover-0" deleted
当主节点失效时,将执行故障切换。故障切换会确认主节点已经更改。
[ec2-user@ip-10-0-0-53 ~]$ kubectl get pod -n redis -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
redis-operator-777cdb655b-f2qxq 1/1 Running 0 173m 10.32.0.4 ip-10-0-0-53.ap-northeast-1.compute.internal <none> <none>
redis-pod 1/1 Running 0 15m 10.32.0.10 ip-10-0-0-53.ap-northeast-1.compute.internal <none> <none>
rfr-redisfailover-0 1/1 Running 0 93s 10.32.0.8 ip-10-0-0-53.ap-northeast-1.compute.internal <none> <none>
rfr-redisfailover-1 1/1 Running 0 23m 10.32.0.9 ip-10-0-0-53.ap-northeast-1.compute.internal <none> <none>
rfs-redisfailover-55b5fd485b-2967z 1/1 Running 0 23m 10.32.0.5 ip-10-0-0-53.ap-northeast-1.compute.internal <none> <none>
rfs-redisfailover-55b5fd485b-6pw27 1/1 Running 0 23m 10.32.0.6 ip-10-0-0-53.ap-northeast-1.compute.internal <none> <none>
rfs-redisfailover-55b5fd485b-fds5w 1/1 Running 0 23m 10.32.0.7 ip-10-0-0-53.ap-northeast-1.compute.internal <none> <none>
[ec2-user@ip-10-0-0-53 ~]$ kubectl exec -n redis rfs-redisfailover-55b5fd485b-2967z -- redis-cli -p 26379 info | grep mymaster
Defaulted container "sentinel" out of: sentinel, sentinel-config-copy (init)
master0:name=mymaster,status=ok,address=10.32.0.9:6379,slaves=1,sentinels=3
[ec2-user@ip-10-0-0-53 ~]$ kubectl exec -n redis rfs-redisfailover-55b5fd485b-2967z -- redis-cli -p 26379 sentinel replicas mymaster
Defaulted container "sentinel" out of: sentinel, sentinel-config-copy (init)
name
10.32.0.8:6379
ip
10.32.0.8
port
6379
runid
4ae072844f52afdffff3f166a0f4dfbdf603c40c
flags
slave
link-pending-commands
0
link-refcount
1
last-ping-sent
0
last-ok-ping-reply
568
last-ping-reply
568
down-after-milliseconds
5000
info-refresh
2217
role-reported
slave
role-reported-time
192467
master-link-down-time
0
master-link-status
ok
master-host
10.32.0.9
master-port
6379
slave-priority
100
slave-repl-offset
485233
replica-announced
1
根据结果确认,Redis主服务器已从rfr-redisfailover-0(10.32.0.8)更改为rfr-redisfailover-1(10.32.0.9)。rfr-redisfailover-0已成为从服务器。
在Redis Operator中,即使POD被删除,IP地址也不会改变。Redis和Sentinel的IP地址都不会改变。这样一来,对于Redis来说,IP地址的变化不再造成任何不便,一切问题都得到了解决。
确认检查当设置错误时的行为
由于操作员监控Redis和Sentinel的配置方面,所以我将进行确认。
这次我将尝试删除Sentinel的主节点监视对象。
[ec2-user@ip-10-0-0-53 ~]$ kubectl exec -n redis rfs-redisfailover-55b5fd485b-2967z -- redis-cli -p 26379 sentinel remove mymaster
Defaulted container "sentinel" out of: sentinel, sentinel-config-copy (init)
OK
[ec2-user@ip-10-0-0-53 ~]$ kubectl exec -n redis rfs-redisfailover-55b5fd485b-2967z -- redis-cli -p 26379 info | grep mymaster
Defaulted container "sentinel" out of: sentinel, sentinel-config-copy (init)
master0:name=mymaster,status=ok,address=10.32.0.9:6379,slaves=1,sentinels=3
[ec2-user@ip-10-0-0-53 ~]$ kubectl logs -n redis rfs-redisfailover-55b5fd485b-2967z
・・・
1:X 21 May 2022 14:45:59.027 # -monitor master mymaster 10.32.0.9 6379
1:X 21 May 2022 14:46:06.318 # +monitor master mymaster 10.32.0.9 6379 quorum 2
・・・
結果顯示,在設定變更之後,大約經過7秒,重新設定已經生效。
這是Redis Sentinel所不具備的功能,我非常感激。
确认在Split Brain状态下的行为
以下是关于发生拆离大脑时的行为描述。
在Redis Sentinel中的拆离大脑情况下,每个Sentinel都认为它们各自连接的Redis是主节点。下面是一个简单的图示例:

上图考虑了Sentinel之间以及Sentinel与Redis之间同时发生连接故障的情况。
这时,对于Sentinel1来说,Sentinel2、Sentinel3和Redis2(副本)似乎都已经宕机。
对于Sentinel2和Sentinel3来说,Sentinel1和Redis1(主服务器)似乎都已经宕机。
由于Sentinel2和Sentinel3认为主服务器已经宕机,它们执行故障转移将Redis2升级为主服务器。然而,Sentinel1并不知情,仍然认为Redis1是主服务器。结果就是,Sentinel1与Sentinel2、Sentinel3连接的是不同的Redis主服务器。如果此时有客户端发起连接,可能会出现数据丢失的情况。
在Redis Operator中,我们将验证当出现脑裂时会发生什么。
作为准备,我们需要安装chaos-mesh。
#!/bin/bash -ex
SCRIPT_DIR=$(cd $(dirname $0); pwd)
echo 'install chaos-mesh'
CHAOS_MESH_VERSION='2.1.3'
INSTALL_NAMESPACE='chaos-testing'
cd $SCRIPT_DIR
helm repo add chaos-mesh https://charts.chaos-mesh.org
helm install chaos-mesh chaos-mesh/chaos-mesh \
--version $CHAOS_MESH_VERSION \
-n $INSTALL_NAMESPACE \
--create-namespace \
-f config/chaos-mesh-values.yaml
echo 'annotate redis node'
CONTROL_PLANE=`kubectl get node | grep 'control-plane' | cut -d' ' -f1`
kubectl annotate node $CONTROL_PLANE chaos-mesh.org/inject=enabled
controllerManager:
enableFilterNamespace: 'true'
chaosDaemon:
runtime: 'containerd'
socketPath: '/var/run/containerd/containerd.sock'
通过Redis Operator引起连接故障。
创建以下yaml文件,用于断开指定POD的连接。
apiVersion: chaos-mesh.org/v1alpha1
kind: Workflow
metadata:
name: rf-chaos
namespace: chaos-testing
spec:
entry: rf-networkchaos
templates:
- name: rf-networkchaos
templateType: Parallel
children:
- redis-master
- redis-slave
- sentinel
- name: redis-master
templateType: NetworkChaos
networkChaos:
action: partition
mode: all
selector:
pods:
redis:
- rfr-redisfailover-0
direction: to
target:
mode: all
selector:
pods:
redis:
- rfs-redisfailover-55b5fd485b-jks7t
- rfs-redisfailover-55b5fd485b-pp6qg
- name: redis-slave
templateType: NetworkChaos
networkChaos:
action: partition
mode: all
selector:
pods:
redis:
- rfr-redisfailover-1
direction: to
target:
mode: all
selector:
pods:
redis:
- rfs-redisfailover-55b5fd485b-4cltl
- name: sentinel
templateType: NetworkChaos
networkChaos:
action: partition
mode: all
selector:
pods:
redis:
- rfs-redisfailover-55b5fd485b-4cltl
direction: to
target:
mode: all
selector:
pods:
redis:
- rfs-redisfailover-55b5fd485b-jks7t
- rfs-redisfailover-55b5fd485b-pp6qg
应用上述的yaml文件。
[ec2-user@ip-10-0-0-199 ~]$ kubectl apply -f rf-netchaos.yaml
workflow.chaos-mesh.org/rf-chaos created
我要查看Redis Operator的状态。
[ec2-user@ip-10-0-0-199 ~]$ kubectl exec -n redis rfs-redisfailover-55b5fd485b-4cltl -- redis-cli -p 26379 info | grep mymaster
Defaulted container "sentinel" out of: sentinel, sentinel-config-copy (init)
master0:name=mymaster,status=ok,address=10.32.0.13:6379,slaves=1,sentinels=3
[ec2-user@ip-10-0-0-199 ~]$ kubectl exec -n redis rfs-redisfailover-55b5fd485b-jks7t -- redis-cli -p 26379 info | grep mymaster
Defaulted container "sentinel" out of: sentinel, sentinel-config-copy (init)
master0:name=mymaster,status=ok,address=10.32.0.14:6379,slaves=1,sentinels=3
[ec2-user@ip-10-0-0-199 ~]$ kubectl exec -n redis rfs-redisfailover-55b5fd485b-pp6qg -- redis-cli -p 26379 info | grep mymaster
Defaulted container "sentinel" out of: sentinel, sentinel-config-copy (init)
master0:name=mymaster,status=ok,address=10.32.0.14:6379,slaves=1,sentinels=3
当从 Sentinel 查询 Redis 主服务器的 IP 地址时,我们发现只有一台 IP 地址与其他不同,这表明发生了脑裂问题。
另外,我们也可以观察 POD 的状态。
[ec2-user@ip-10-0-0-199 ~]$ kubectl get pod -n redis -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
redis-operator-777cdb655b-2w546 1/1 Running 0 24m 10.32.0.4 ip-10-0-0-199.ap-northeast-1.compute.internal <none> <none>
rfr-redisfailover-0 1/1 Running 0 4m24s 10.32.0.13 ip-10-0-0-199.ap-northeast-1.compute.internal <none> <none>
rfr-redisfailover-1 1/1 Running 0 4m24s 10.32.0.14 ip-10-0-0-199.ap-northeast-1.compute.internal <none> <none>
rfs-redisfailover-55b5fd485b-4cltl 1/1 Running 0 4m23s 10.32.0.11 ip-10-0-0-199.ap-northeast-1.compute.internal <none> <none>
rfs-redisfailover-55b5fd485b-jks7t 1/1 Running 0 4m23s 10.32.0.10 ip-10-0-0-199.ap-northeast-1.compute.internal <none> <none>
rfs-redisfailover-55b5fd485b-pp6qg 1/1 Running 0 4m23s 10.32.0.12 ip-10-0-0-199.ap-northeast-1.compute.internal <none> <none>
检查到READY栏位为1/1,说明客户端可以访问。如果数据丢失成为问题,我认为需要采取一些措施。查看操作员日志记录。
[ec2-user@ip-10-0-0-199 ~]$ kubectl logs -n redis redis-operator-777cdb655b-2w546 | tail
time="2022-05-22T02:26:21Z" level=info msg="configMap updated" configMap=rfr-redisfailover namespace=redis service=k8s.configMap src="configmap.go:78"
W0522 02:26:21.493347 1 warnings.go:70] policy/v1beta1 PodDisruptionBudget is deprecated in v1.21+, unavailable in v1.25+; use policy/v1 PodDisruptionBudget
W0522 02:26:21.497182 1 warnings.go:70] policy/v1beta1 PodDisruptionBudget is deprecated in v1.21+, unavailable in v1.25+; use policy/v1 PodDisruptionBudget
time="2022-05-22T02:26:21Z" level=info msg="podDisruptionBudget updated" namespace=redis podDisruptionBudget=rfr-redisfailover service=k8s.podDisruptionBudget src="poddisruptionbudget.go:79"
time="2022-05-22T02:26:21Z" level=info msg="statefulSet updated" namespace=redis service=k8s.statefulSet src="statefulset.go:102" statefulSet=rfr-redisfailover
W0522 02:26:21.507657 1 warnings.go:70] policy/v1beta1 PodDisruptionBudget is deprecated in v1.21+, unavailable in v1.25+; use policy/v1 PodDisruptionBudget
W0522 02:26:21.511551 1 warnings.go:70] policy/v1beta1 PodDisruptionBudget is deprecated in v1.21+, unavailable in v1.25+; use policy/v1 PodDisruptionBudget
time="2022-05-22T02:26:21Z" level=info msg="podDisruptionBudget updated" namespace=redis podDisruptionBudget=rfs-redisfailover service=k8s.podDisruptionBudget src="poddisruptionbudget.go:79"
time="2022-05-22T02:26:21Z" level=info msg="deployment updated" deployment=rfs-redisfailover namespace=redis service=k8s.deployment src="deployment.go:102"
time="2022-05-22T02:26:21Z" level=error msg="error on object processing: More than one master, fix manually" controller-id=redisfailover object-key=redis/redisfailover operator=redisfailover service=kooper.controller src="controller.go:279"
在日志的最后一句话中,出现了需要手动修正的错误消息,原因是由于存在多个主服务器。最后尝试解决连接问题。
[ec2-user@ip-10-0-0-199 ~]$ kubectl delete -f rf-netchaos.yaml
workflow.chaos-mesh.org "rf-chaos" deleted
[ec2-user@ip-10-0-0-199 ~]$ kubectl logs -n redis redis-operator-777cdb655b-2w546 | tail -n 20
・・・
time="2022-05-22T02:28:51Z" level=error msg="error on object processing: More than one master, fix manually" controller-id=redisfailover object-key=redis/redisfailover operator=redisfailover service=kooper.controller src="controller.go:279"
time="2022-05-22T02:29:21Z" level=info msg="configMap updated" configMap=rfs-redisfailover namespace=redis service=k8s.configMap src="configmap.go:78"
time="2022-05-22T02:29:21Z" level=info msg="configMap updated" configMap=rfr-s-redisfailover namespace=redis service=k8s.configMap src="configmap.go:78"
time="2022-05-22T02:29:21Z" level=info msg="configMap updated" configMap=rfr-readiness-redisfailover namespace=redis service=k8s.configMap src="configmap.go:78"
time="2022-05-22T02:29:21Z" level=info msg="configMap updated" configMap=rfr-redisfailover namespace=redis service=k8s.configMap src="configmap.go:78"
W0522 02:29:21.474784 1 warnings.go:70] policy/v1beta1 PodDisruptionBudget is deprecated in v1.21+, unavailable in v1.25+; use policy/v1 PodDisruptionBudget
W0522 02:29:21.478397 1 warnings.go:70] policy/v1beta1 PodDisruptionBudget is deprecated in v1.21+, unavailable in v1.25+; use policy/v1 PodDisruptionBudget
time="2022-05-22T02:29:21Z" level=info msg="podDisruptionBudget updated" namespace=redis podDisruptionBudget=rfr-redisfailover service=k8s.podDisruptionBudget src="poddisruptionbudget.go:79"
time="2022-05-22T02:29:21Z" level=info msg="statefulSet updated" namespace=redis service=k8s.statefulSet src="statefulset.go:102" statefulSet=rfr-redisfailover
W0522 02:29:21.487929 1 warnings.go:70] policy/v1beta1 PodDisruptionBudget is deprecated in v1.21+, unavailable in v1.25+; use policy/v1 PodDisruptionBudget
W0522 02:29:21.491568 1 warnings.go:70] policy/v1beta1 PodDisruptionBudget is deprecated in v1.21+, unavailable in v1.25+; use policy/v1 PodDisruptionBudget
time="2022-05-22T02:29:21Z" level=info msg="podDisruptionBudget updated" namespace=redis podDisruptionBudget=rfs-redisfailover service=k8s.podDisruptionBudget src="poddisruptionbudget.go:79"
time="2022-05-22T02:29:21Z" level=info msg="deployment updated" deployment=rfs-redisfailover namespace=redis service=k8s.deployment src="deployment.go:102"
[ec2-user@ip-10-0-0-199 ~]$ kubectl exec -n redis rfs-redisfailover-55b5fd485b-4cltl -- redis-cli -p 26379 info | grep mymaster
Defaulted container "sentinel" out of: sentinel, sentinel-config-copy (init)
master0:name=mymaster,status=ok,address=10.32.0.14:6379,slaves=1,sentinels=3
[ec2-user@ip-10-0-0-199 ~]$ kubectl exec -n redis rfs-redisfailover-55b5fd485b-jks7t -- redis-cli -p 26379 info | grep mymaster
Defaulted container "sentinel" out of: sentinel, sentinel-config-copy (init)
master0:name=mymaster,status=ok,address=10.32.0.14:6379,slaves=1,sentinels=3
[ec2-user@ip-10-0-0-199 ~]$ kubectl exec -n redis rfs-redisfailover-55b5fd485b-pp6qg -- redis-cli -p 26379 info | grep mymaster
Defaulted container "sentinel" out of: sentinel, sentinel-config-copy (init)
master0:name=mymaster,status=ok,address=10.32.0.14:6379,slaves=1,sentinels=3
过了一段时间,配置已更新。所有Sentinel连接到的Redis主节点已经更改为故障转移后的节点。一旦连接故障解除,系统会自动尝试恢复连接。
以上就确认了脑分离的问题。
总结
我尝试使用Redis Operator,实际是否采用Operator取决于具体情况。然而,如果可以通过Operator实现使用Redis的目标,那将是很方便的。我还有很多不了解的基础设施知识,我会继续学习。