调查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命令。

方式構成フェイルオーバー論理DBシャーディングReplicationマスター、レプリカ×〇×Sentinelマスター、レプリカ、Sentinel〇〇×Clusterマルチマスター、レプリカ〇×〇

似乎还有一些与外部工具结合的方法。

※请查阅

 

Redis、Sentinel的功能

在本文中,我将介绍关于Redis Sentinel的内容。
首先,我将谈及Redis和Sentinel的功能,然后在后面的内容中我会详细说明这些功能在操作者角色中的变化。

Redis Sentinel的故障转移流程。

以下是用图示记录Redis Sentinel故障转移的流程。
当Sentinel检测到主节点宕机时,它将把其中一个从节点晋升为新主节点。

image.png

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

image.png

在故障转移过程中,每个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

我将针对每个比较进行说明。

項目比較メモ必要なディスクサイズRDB<AOFAOFは圧縮せずコマンドとデータを保存するため。Redisダウン時のデータロスの大きさRDB>AOFAOFは追記分だけ保存する。そのため保存頻度を大きくしてロスを小さくしやすい。ファイル読み込み速度RDB>AOFAOFではデータに加えコマンドを読み込んでいるため。永続化失敗の可能性RDB<AOFRDBはアトミックに保存が可能。AOFはバグで復元が失敗したことがあるらしい。ファイル復元のしやすさRDB<AOFAOFでは破損を修正するツールがある。また直接ファイルを修正しやすいため。

根据使用场景的不同,两者都有优点和缺点,应根据需要选择其中一种或同时采用两种。

※仅需一种选项:请用中文将以下内容简洁地表达出来。

参考

 

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是主节点。下面是一个简单的图示例:

image.png

上图考虑了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的目标,那将是很方便的。我还有很多不了解的基础设施知识,我会继续学习。

bannerAds