在Kubernetes上构建Redis集群时需要考虑的事项,包括多个区域及区域中的Redis类
以下是在Kubernetes集群上使用多可用区域(MZR)部署Redis Cluster时需要考虑的要点备忘录。
Redis 不仅可作为内存缓存替代 memcached,还能将数据保存在持久卷中。在应用 Kubernetes 进行微服务化和现代化的过程中,轻量高速的 Redis 被视为重要的配置部分之一。另外,在引用的 “The Twelve-Factor Apps” 的第VI条中,建议将会话状态数据保存在像 Redis 这样的数据存储中。因此,在 Kubernetes 集群中,我们需要掌握如何在高可用性和性能方面有效利用 Redis 的技巧。
The Twelve-Factor Apps https://12factor.net/ja/processes
VI. プロセス
アプリケーションを1つもしくは複数のステートレスなプロセスとして実行する
抜粋
Webシステムの中には、“スティッキーセッション”に頼るものがある – これはユーザーのセッションデータを
アプリケーションプロセスのメモリにキャッシュし、同じ訪問者からの将来のリクエストが同じプロセスに送ら
れることを期待するものである。スティッキーセッションはTwelve-Factorに違反しており、決して使ったり
頼ったりしてはならない。セッション状態のデータは、有効期限を持つデータストア(例:Memcached や
Redis)に格納すべきである。
在可靠性和性能方面,使用”Redis集群”或”Redis和Sentinel”是常见的。在云计算中,通常会通过将多个数据中心放置在一个地区(区域)内,形成可用区来保证高可用性。这种方法不仅可以对数据中心级别的故障进行应对,还可以在广域灾害中产生一定的效果。即使一个数据中心因为严重故障等原因长时间无法使用,我们仍然可以期望在剩余的数据中心中继续进行业务。
本文主要验证在将Redis集群部署在多个数据中心的多区域(Multi-zone)情况下,所面临的问题和实现方法。
关于分区和Redis Cluster的配置想法。
Redis是一个键值存储数据存储器,可以通过指定键来进行数据的注册和获取。在Redis集群中,有多个哈希槽,通过键的哈希来分布存储数据。例如,最小配置下使用三个Redis主节点。为了保证数据的完整性,Redis从节点负责存储Redis主节点数据的副本。
如果发生了以下情况之一,即三个Redis主机中的一个由于某种故障而丢失,那么通过此结构,从节点将被晋升为主节点,以继续提供Redis集群的功能。另一方面,如果Redis的主节点同时停止了两个或更多,那么将停止集群功能,并停止读取和写入操作。
在构成案例1中,将主节点分散放置在不同的区域,同时也将从节点放置在同一区域。
将Redis主节点分散并部署在不同区域,并在每个区域内部署从节点作为配置示例。在这种配置中,如果其中一个区域停止运行,那么某个数据范围即一个槽的数据将会丢失。结果是Redis集群将无法正常工作,无法写入或读取数据。因此,配置方案1是在构建Redis集群时应避免的配置,可以称之为反模式。
将Master分散配置在Case 2区域中,将Slave配置在其他区域中。
此配置方案可以防止丢失重要数据。Redis集群将数据分散存储在多个哈希槽中。需要在每个槽单元中设置主节点,在该方案中,将主节点分布在Zone-1至3中。每个槽范围的从节点将保持Redis主节点数据的副本在其他区域上。使用这种配置,即使某个区域停止,服务也能继续运行。而且,在三个区域中,即使有两个区域处于灾难状态,数据也可以恢复为可读写状态。
将该案例分成3个区域,并在每个区域中分散配置主节点,为每个 Redis 节点分配一个 K8s 节点。
在之前的配置中,多个Redis节点在一个K8s节点上运行。而在配置案例3中,Redis节点和K8s节点一对一地运行。复制的方向与配置案例2相同,跨区实施。在这种情况下,除了配置案例2的特点外,还可以减轻K8s节点故障的影响。然而,增加K8s节点会导致云服务费用上升,因此需要考虑必要性和成本的平衡。
在所列构成案例中,具有成本和数据保全性平衡的是构成案例2,因此,在实际验证过程中确认它。
在多区域中使用状态全量设置的编写方法
使用K8s的StatefulSet控制器构建Redis集群非常方便。它可以按顺序管理Pod的启动和停止,并执行Pod数量的增加和减少,并且能够一起部署持久化卷。
然而,在使用StatefulSet控制器在多区域环境中时,在IKS上可能会遇到麻烦。这是因为StatefulSet的启动Pod和持久化卷的区域没有必要匹配。例如,Pod可能按指定在区域1启动,但是持久化卷却在区域2进行配置的问题。
通过为StatefulSet的清单添加以下标签,可以避免这个问题。为什么通过添加”region”和”zone”标签可以控制持久卷的供应位置还不清楚。可能是因为存储类中定义的供应商正在引用这些标签。整个清单代码可以在GitHub的https://github.com/takara9/redis-cluster/tree/master/iks-mzr-tok-multi-pod 中找到。
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis-cluster-1
labels:
app: redis-cluster
spec:
serviceName: redis-cluster
replicas: 3
selector:
matchLabels:
app: redis-cluster
region: jp-tok ## これら二つのラベルを追加する
zone: tok05 ## zone値は以下のnodeSelectorの値と一致しなくてはならない。
template:
metadata:
labels:
app: redis-cluster
antiaffinity: redis-node
region: jp-tok ## こちらにも、同じキーと値のセットのラベルを書いておく。
zone: tok05 ##
spec:
# ノードセレクターで、ポッドがデプロイされるゾーンを指定する。
# しかし、これだけでは永続ボリュームの配置先は同一リージョン内で不定となる。
nodeSelector:
failure-domain.beta.kubernetes.io/zone: tok05
<以下省略>
多区域 + 多节点 构建方法
在多个区域中启动Redis集群的清单已经放在GitHub的以下URL上,请您确认一下清单。
https://github.com/takara9/redis-cluster/tree/master/iks-mzr-tok-multi-pod
从配置映射和服务中应用。
$ kubectl apply -f redis-configmap.yml
configmap/redis-cluster created
$ kubectl apply -f redis-service.yml
service/redis-cluster created
首先,在两个区域中创建一个状态集合。
$ kubectl apply -f redis-cluster-mzr-1m.yml
statefulset.apps/redis-cluster-1 created
$ kubectl apply -f redis-cluster-mzr-2m.yml
statefulset.apps/redis-cluster-2 created
imac:iks-mzr-tok maho$ kubectl get po
NAME READY STATUS RESTARTS AGE
redis-cluster-1-0 1/1 Running 0 28m
redis-cluster-1-1 1/1 Running 0 26m
redis-cluster-1-2 1/1 Running 0 24m
redis-cluster-2-0 1/1 Running 0 28m
redis-cluster-2-1 1/1 Running 0 26m
redis-cluster-2-2 1/1 Running 0 24m
创建Redis集群的主节点和从节点
使用 redis-cluster-1-0 在 Redis 上执行管理命令 redis-cli,创建最小规模的集群。
Redis 的主节点和从节点将由 Redis 自动确定。在此过程中,将自动将主节点聚集到一个区域中。
如果无法正确分配区域,则在启动集群后需要手动执行故障转移,将其集中到一个区域中。
$ kubectl exec -it redis-cluster-1-0 -- redis-cli --cluster create --cluster-replicas 1 $(kubectl get pods -l 'app=redis-cluster' -o jsonpath='{range.items[*]}{.status.podIP}:6379 ')
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 172.30.155.83:6379 to 172.30.46.203:6379
Adding replica 172.30.155.84:6379 to 172.30.46.204:6379
Adding replica 172.30.155.82:6379 to 172.30.46.205:6379
< 中略 >
..
>>> Performing Cluster Check (using node 172.30.46.203:6379)
M: 7e9ab7262b2c48211afdce8e95c511a0296d0a1b 172.30.46.203:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
M: 47bb522b281cc046346fda7d29727d0f54f9e766 172.30.46.204:6379
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: cf7ef722d2b5248eb22f8058f3cba9c06582c3fc 172.30.155.82:6379
slots: (0 slots) slave
replicates 1f27ff665a44590151ea356a7879cf391ab54636
M: 1f27ff665a44590151ea356a7879cf391ab54636 172.30.46.205:6379
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: 430c82e5ba4173661c3776da7cda2443c5e4de81 172.30.155.84:6379
slots: (0 slots) slave
replicates 47bb522b281cc046346fda7d29727d0f54f9e766
S: 9265e13e2d2218f1649a4f10ef4de04dd6ca2147 172.30.155.83:6379
slots: (0 slots) slave
replicates 7e9ab7262b2c48211afdce8e95c511a0296d0a1b
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
当Redis集群的最小集合建立好后,再添加第三个状态集合的区域。
$ kubectl apply -f redis-cluster-mzr-3a.yml
statefulset.apps/redis-cluster-3 configured
imac:iks-mzr-tok maho$ kubectl get po
NAME READY STATUS RESTARTS AGE
redis-cluster-1-0 1/1 Running 0 32m
redis-cluster-1-1 1/1 Running 0 30m
redis-cluster-1-2 1/1 Running 0 28m
redis-cluster-2-0 1/1 Running 0 32m
redis-cluster-2-1 1/1 Running 0 30m
redis-cluster-2-2 1/1 Running 0 28m
redis-cluster-3-0 1/1 Running 0 3m52s
redis-cluster-3-1 1/1 Running 0 3m30s
redis-cluster-3-2 1/1 Running 0 3m12s
在第三个区域的Pod中,通过指定每个槽位的Redis主节点ID来配置Redis从节点。
将 redis-cluster-3-0 设为 redis-cluster-1-0 的从节点。
$ kubectl exec redis-cluster-1-0 -- redis-cli --cluster add-node --cluster-slave --cluster-master-id 7e9ab7262b2c48211afdce8e95c511a0296d0a1b $(kubectl get pod redis-cluster-3-0 -o jsonpath='{.status.podIP}'):6379 $(kubectl get pod redis-cluster-1-0 -o jsonpath='{.status.podIP}'):6379
将 redis-cluster-3-1 设置为 redis-cluster-1-1 的从节点。
$ kubectl exec redis-cluster-1-0 -- redis-cli --cluster add-node --cluster-slave --cluster-master-id 47bb522b281cc046346fda7d29727d0f54f9e766 $(kubectl get pod redis-cluster-3-1 -o jsonpath='{.status.podIP}'):6379 $(kubectl get pod redis-cluster-1-0 -o jsonpath='{.status.podIP}'):6379
将redis-cluster-3-2作为redis-cluster-1-2的从节点。
$ kubectl exec redis-cluster-1-0 -- redis-cli --cluster add-node --cluster-slave --cluster-master-id 1f27ff665a44590151ea356a7879cf391ab54636 $(kubectl get pod redis-cluster-3-2 -o jsonpath='{.status.podIP}'):6379 $(kubectl get pod redis-cluster-1-0 -o jsonpath='{.status.podIP}'):6379
Redis集群有3个区域构成。
通过以上操作,Redis主节点 x3(槽位),从节点(副本)x3(槽位)x2(区域)的设置已完成。
$ kubectl exec redis-cluster-1-0 -- redis-cli -c cluster nodes (実際は順不同、見やすさのため並べかえ済み)
7e9ab7262b2c48211afdce8e95c511a0296d0a1b 172.30.46.203:6379@16379 myself,master - 0 1564886560000 1 connected 0-5460
47bb522b281cc046346fda7d29727d0f54f9e766 172.30.46.204:6379@16379 master - 0 1564886563000 2 connected 5461-10922
1f27ff665a44590151ea356a7879cf391ab54636 172.30.46.205:6379@16379 master - 0 1564886565207 3 connected 10923-16383
cf7ef722d2b5248eb22f8058f3cba9c06582c3fc 172.30.155.82:6379@16379 slave 1f27ff665a44590151ea356a7879cf391ab54636 0 1564886566212 4 connected
9265e13e2d2218f1649a4f10ef4de04dd6ca2147 172.30.155.83:6379@16379 slave 7e9ab7262b2c48211afdce8e95c511a0296d0a1b 0 1564886564206 5 connected
430c82e5ba4173661c3776da7cda2443c5e4de81 172.30.155.84:6379@16379 slave 47bb522b281cc046346fda7d29727d0f54f9e766 0 1564886563000 6 connected
8185dfc3e13fffe360235e04c32b822fccdafffa 172.30.190.208:6379@16379 slave 7e9ab7262b2c48211afdce8e95c511a0296d0a1b 0 1564886562000 1 connected
abc6d8ca61162829429bac9fcc8f4d6d9ef79438 172.30.190.209:6379@16379 slave 47bb522b281cc046346fda7d29727d0f54f9e766 0 1564886563217 2 connected
eccce1ad7408e85a8f941d0e1875750503844b58 172.30.190.210:6379@16379 slave 1f27ff665a44590151ea356a7879cf391ab54636 0 1564886564000 3 connected
在这种情况下,区域1的节点集合为Redis主节点。为了了解在当前状态下的使用情况,我们决定保持当前状态,在进行模拟故障测试之前进行实施。
从Redis客户端的访问
redis-cluster的有状态集合是根据每个区域部署的,并创建了3个有状态集合。
与此同时,redis-cluster的服务已设置标签和选择器以返回3个区域中9个Pod的IP地址。
详细信息请参阅GitHub上的清单。
$ kubectl get po -l redis-cluster
No resources found.
imac:iks-mzr-tok-multi-pod maho$ kubectl get po -o wide -l app=redis-cluster
NAME READY STATUS RESTARTS AGE IP NODE
redis-cluster-1-0 1/1 Running 0 3h57m 172.30.46.203 10.193.10.58
redis-cluster-1-1 1/1 Running 0 3h57m 172.30.46.204 10.193.10.58
redis-cluster-1-2 1/1 Running 0 3h56m 172.30.46.205 10.193.10.58
redis-cluster-2-0 1/1 Running 0 3h52m 172.30.155.82 10.192.57.161
redis-cluster-2-1 1/1 Running 0 3h51m 172.30.155.83 10.192.57.161
redis-cluster-2-2 1/1 Running 0 3h51m 172.30.155.84 10.192.57.161
redis-cluster-3-0 1/1 Running 0 4h23m 172.30.190.208 10.212.5.223
redis-cluster-3-1 1/1 Running 0 4h20m 172.30.190.209 10.212.5.223
redis-cluster-3-2 1/1 Running 0 4h18m 172.30.190.210 10.212.5.223
$ kubectl get svc redis-cluster
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
redis-cluster ClusterIP None <none> 6379/TCP,16379/TCP 22h
$ kubectl get ep redis-cluster
NAME ENDPOINTS AGE
redis-cluster 172.30.155.82:6379,172.30.155.33:6379,172.30.155.84:6379 + 15 more... 22h
$ kubectl describe ep redis-cluster
Name: redis-cluster
Namespace: default
Labels: app=redis-cluster
Annotations: <none>
Subsets:
Addresses: 172.30.155.82,172.30.155.83,172.30.155.84,172.30.190.208,172.30.190.209,172.30.190.210,172.30.46.203,172.30.46.204,172.30.46.205
NotReadyAddresses: <none>
Ports:
Name Port Protocol
---- ---- --------
client 6379 TCP
gossip 16379 TCP
Events: <none>
Redis容器映像中已經安裝了redis-cli,我們可以以互動方式啟動並連線到Redis集群服務。我們可以進行寫入和讀取測試。使用服務名稱進行訪問,並向返回的Pod IP地址提問,如果這是另一個哈希槽,則會返回重定向消息。在重定向位置進行數據的寫入或讀取操作。
$ kubectl run redis-cli --image redis --restart=Never
$ kubectl exec -it redis-cli -- bash
root@redis-cluster-1-0:/data# redis-cli -c -h redis-cluster -p 6379
redis-cluster:6379> set key-a 1001
-> Redirected to slot [6672] located at 172.30.46.204:6379
OK
172.30.46.204:6379> set key-b 2042
OK
172.30.46.204:6379> set key-c 3408
-> Redirected to slot [14930] located at 172.30.46.205:6379
OK
172.30.46.205:6379> get key-a
-> Redirected to slot [6672] located at 172.30.46.204:6379
"1001"
172.30.46.204:6379> get key-b
"2042"
172.30.46.204:6379> get key-c
-> Redirected to slot [14930] located at 172.30.46.205:6379
"3408"
在实际的应用中,可以通过编程语言的库实现以上操作。
模拟区域故障的恢复(主服务器集中在特定区域的情况)
由于重新创建了Redis集群,请注意Pod的IP地址已更改。请验证发生故障、通信不可用时在一个数据中心(即区域)的行为。通过模拟区域故障,删除所有属于存在Redis主服务器的区域的Pod。换句话说,在以下示例中,redis-cluster-2-*的所有Pod都将消失。
在初期阶段,redis-cluster-2-* 存在着主节点。
$ kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE
redis-cluster-1-0 1/1 Running 0 112s 172.30.46.227 10.193.10.58
redis-cluster-1-1 1/1 Running 0 92s 172.30.46.228 10.193.10.58
redis-cluster-1-2 1/1 Running 0 74s 172.30.46.229 10.193.10.58
redis-cluster-2-0 1/1 Running 0 44m 172.30.155.89 10.192.57.161
redis-cluster-2-1 1/1 Running 0 42m 172.30.155.90 10.192.57.161
redis-cluster-2-2 1/1 Running 0 40m 172.30.155.91 10.192.57.161
redis-cluster-3-0 1/1 Running 0 27m 172.30.190.215 10.212.5.223
redis-cluster-3-1 1/1 Running 0 25m 172.30.190.216 10.212.5.223
redis-cluster-3-2 1/1 Running 0 23m 172.30.190.217 10.212.5.223
$ kubectl exec -it redis-cluster-2-0 -- redis-cli -c cluster nodes (見やすくするために、IPアドレスでソートしています。)
a42b74b5a3f8002aac192c9e936c28382ab4fca9 172.30.155.89:6379@16379 myself,master - 0 1564909420000 7 connected 10923-16383
a1bc886c988fb43538c96cf64261470d2b0a3070 172.30.155.90:6379@16379 master - 0 1564909420000 8 connected 0-5460
07fbf589aff23033581264a9734d8bd7d6a0c570 172.30.155.91:6379@16379 master - 0 1564909420000 9 connected 5461-10922
ff0d4b630bfbc015a192db93911d4c27d9ccc0fa 172.30.46.227:6379@16379 slave a1bc886c988fb43538c96cf64261470d2b0a3070 0 1564909422477 8 connected
4aab750764825dadf4fddb67eaf7c2df6101195d 172.30.46.228:6379@16379 slave 07fbf589aff23033581264a9734d8bd7d6a0c570 0 1564909421000 9 connected
643ac620d961badfef08c39e815724503dbd53de 172.30.46.229:6379@16379 slave a42b74b5a3f8002aac192c9e936c28382ab4fca9 0 1564909420000 7 connected
31c819ff92f73294c47480e7107d2cbaf2df1c42 172.30.190.215:6379@16379 slave a1bc886c988fb43538c96cf64261470d2b0a3070 0 1564909421470 8 connected
a93fbcda1dd4ff810aa39717195ddbfb1cc7292a 172.30.190.216:6379@16379 slave 07fbf589aff23033581264a9734d8bd7d6a0c570 0 1564909422000 9 connected
19dcb21943fcbdb6244052f2f76ba2dc652900bd 172.30.190.217:6379@16379 slave a42b74b5a3f8002aac192c9e936c28382ab4fca9 0 1564909423482 7 connected
模拟发生故障 (Mó nǐ fā gù
删除了 redis-cluster-2-* 系列的所有Pod。
$ kubectl delete -f redis-cluster-mzr-2m.yml
statefulset.apps "redis-cluster-2" deleted
$ kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
redis-cluster-1-0 1/1 Running 0 2m55s 172.30.46.227 10.193.10.58 <none> <none>
redis-cluster-1-1 1/1 Running 0 2m35s 172.30.46.228 10.193.10.58 <none> <none>
redis-cluster-1-2 1/1 Running 0 2m17s 172.30.46.229 10.193.10.58 <none> <none>
redis-cluster-3-0 1/1 Running 0 28m 172.30.190.215 10.212.5.223 <none> <none>
redis-cluster-3-1 1/1 Running 0 26m 172.30.190.216 10.212.5.223 <none> <none>
redis-cluster-3-2 1/1 Running 0 24m 172.30.190.217 10.212.5.223 <none> <none>
Redis主节点全部宕机了。
$ kubectl exec -it redis-cluster-1-0 -- redis-cli -c cluster nodes
a42b74b5a3f8002aac192c9e936c28382ab4fca9 172.30.155.89:6379@16379 master,fail? - 1564909448754 1564909446000 7 connected 10923-16383
a1bc886c988fb43538c96cf64261470d2b0a3070 172.30.155.90:6379@16379 master,fail? - 1564909448855 1564909445000 8 connected 0-5460
07fbf589aff23033581264a9734d8bd7d6a0c570 172.30.155.91:6379@16379 master,fail? - 1564909448754 1564909446950 9 connected 5461-10922
ff0d4b630bfbc015a192db93911d4c27d9ccc0fa 172.30.46.227:6379@16379 myself,slave a1bc886c988fb43538c96cf64261470d2b0a3070 0 1564909465000 1 connected
4aab750764825dadf4fddb67eaf7c2df6101195d 172.30.46.228:6379@16379 slave 07fbf589aff23033581264a9734d8bd7d6a0c570 0 1564909464019 9 connected
643ac620d961badfef08c39e815724503dbd53de 172.30.46.229:6379@16379 slave a42b74b5a3f8002aac192c9e936c28382ab4fca9 0 1564909465022 7 connected
31c819ff92f73294c47480e7107d2cbaf2df1c42 172.30.190.215:6379@16379 slave a1bc886c988fb43538c96cf64261470d2b0a3070 0 1564909467035 8 connected
a93fbcda1dd4ff810aa39717195ddbfb1cc7292a 172.30.190.216:6379@16379 slave 07fbf589aff23033581264a9734d8bd7d6a0c570 0 1564909464000 9 connected
19dcb21943fcbdb6244052f2f76ba2dc652900bd 172.30.190.217:6379@16379 slave a42b74b5a3f8002aac192c9e936c28382ab4fca9 0 1564909466032 7 connected
由于3个Redis主节点停止,集群状态变为停止状态 cluster_state:fail。在Redis集群中,如果同时有2个或更多节点停止,则会导致整个集群停止,这是设计规定。
然而,槽状态仍为正常,可以确定数据没有丢失。
在这种情况下,由于Redis集群已停止,因此无法从客户端进行读取和写入操作。
$ kubectl exec -it redis-cluster-1-0 -- redis-cli -c cluster info
cluster_state:fail
cluster_slots_assigned:16384
cluster_slots_ok:0
cluster_slots_pfail:16384
cluster_slots_fail:0
cluster_known_nodes:9
cluster_size:3
cluster_current_epoch:9
<以下省略>
集群的恢复操作
确认如何从Redis集群因区域故障而停止的状态中恢复Redis服务。
由于Redis集群停止运行,Redis从服务器无法晋升为主服务器。因此,需要通过手动操作将从服务器切换到主服务器。
在下面的示例中,我们执行了两次Redis节点的故障转移操作后,Redis集群恢复正常运行。
但是第三个从服务器已经晋升为主服务器,因此发生了错误。但是这不被视为问题。
执行「cluster failover takeover」命令必须在Redis从节点上进行。
通过此命令,Redis从节点将夺取主节点的权限,并将自己升级为Redis主节点。
由于在1个区域中创建了3个插槽的主节点,因此需要进行以下3个步骤。
imac:iks-mzr-tok-multi-pod maho$ kubectl exec -it redis-cluster-1-0 -- redis-cli -c cluster failover takeover
OK
imac:iks-mzr-tok-multi-pod maho$ kubectl exec -it redis-cluster-1-1 -- redis-cli -c cluster failover takeover
OK
imac:iks-mzr-tok-multi-pod maho$ kubectl exec -it redis-cluster-1-2 -- redis-cli -c cluster failover takeover
(error) ERR You should send CLUSTER FAILOVER to a replica
确认集群恢复后的情况。
发生故障的主服务器保持停止状态,手动进行故障转移的主服务器正在运行中。
当然,如果查看Redis集群状态,cluster_state为”ok”。
$ kubectl exec -it redis-cluster-1-0 -- redis-cli -c cluster nodes (見易さのため IPアドレスでソートしました)
a42b74b5a3f8002aac192c9e936c28382ab4fca9 172.30.155.89:6379@16379 master,fail - 1564909448754 1564909446000 7 connected
a1bc886c988fb43538c96cf64261470d2b0a3070 172.30.155.90:6379@16379 master,fail - 1564909448855 1564909445000 8 connected
07fbf589aff23033581264a9734d8bd7d6a0c570 172.30.155.91:6379@16379 master,fail - 1564909448754 1564909446950 9 connected
ff0d4b630bfbc015a192db93911d4c27d9ccc0fa 172.30.46.227:6379@16379 myself,master - 0 1564909596000 10 connected 0-5460
4aab750764825dadf4fddb67eaf7c2df6101195d 172.30.46.228:6379@16379 master - 0 1564909597569 11 connected 5461-10922
643ac620d961badfef08c39e815724503dbd53de 172.30.46.229:6379@16379 master - 0 1564909596563 12 connected 10923-16383
31c819ff92f73294c47480e7107d2cbaf2df1c42 172.30.190.215:6379@16379 slave ff0d4b630bfbc015a192db93911d4c27d9ccc0fa 0 1564909595562 10 connected
a93fbcda1dd4ff810aa39717195ddbfb1cc7292a 172.30.190.216:6379@16379 slave 4aab750764825dadf4fddb67eaf7c2df6101195d 0 1564909597000 11 connected
19dcb21943fcbdb6244052f2f76ba2dc652900bd 172.30.190.217:6379@16379 slave 643ac620d961badfef08c39e815724503dbd53de 0 1564909598575 12 connected
$ kubectl exec -it redis-cluster-1-0 -- redis-cli -c cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:9
cluster_size:3
cluster_current_epoch:12
处理故障节点
如果被淘汰的区域复活了,它会自动作为从属者重新纳入。
$ kubectl apply -f redis-cluster-mzr-2m.yml
statefulset.apps/redis-cluster-2 created
$ kubectl exec -it redis-cluster-1-0 -- redis-cli -c cluster nodes
a42b74b5a3f8002aac192c9e936c28382ab4fca9 172.30.155.92:6379@16379 slave 643ac620d961badfef08c39e815724503dbd53de 0 1564910872000 12 connected
a1bc886c988fb43538c96cf64261470d2b0a3070 172.30.155.93:6379@16379 slave ff0d4b630bfbc015a192db93911d4c27d9ccc0fa 0 1564910871000 10 connected
07fbf589aff23033581264a9734d8bd7d6a0c570 172.30.155.94:6379@16379 slave 4aab750764825dadf4fddb67eaf7c2df6101195d 0 1564910874024 11 connected
ff0d4b630bfbc015a192db93911d4c27d9ccc0fa 172.30.46.227:6379@16379 myself,master - 0 1564910873000 10 connected 0-5460
4aab750764825dadf4fddb67eaf7c2df6101195d 172.30.46.228:6379@16379 master - 0 1564910872014 11 connected 5461-10922
643ac620d961badfef08c39e815724503dbd53de 172.30.46.229:6379@16379 master - 0 1564910870005 12 connected 10923-16383
31c819ff92f73294c47480e7107d2cbaf2df1c42 172.30.190.215:6379@16379 slave ff0d4b630bfbc015a192db93911d4c27d9ccc0fa 0 1564910873020 10 connected
a93fbcda1dd4ff810aa39717195ddbfb1cc7292a 172.30.190.216:6379@16379 slave 4aab750764825dadf4fddb67eaf7c2df6101195d 0 1564910875028 11 connected
19dcb21943fcbdb6244052f2f76ba2dc652900bd 172.30.190.217:6379@16379 slave 643ac620d961badfef08c39e815724503dbd53de 0 1564910870000 12 connected
模拟区域故障的恢复(Redis主节点被分布在各个区域的情况下)
最初的状态是,Redis主节点集中在redis-cluster-1-*。换句话说,它们集中在Zone-1。
$ kubectl exec -it redis-cluster-1-0 -- redis-cli -c cluster nodes
ff0d4b630bfbc015a192db93911d4c27d9ccc0fa 172.30.46.227:6379@16379 myself,master - 0 1564968490000 10 connected 0-5460
4aab750764825dadf4fddb67eaf7c2df6101195d 172.30.46.228:6379@16379 master - 0 1564968490405 11 connected 5461-10922
643ac620d961badfef08c39e815724503dbd53de 172.30.46.229:6379@16379 master - 0 1564968489000 12 connected 10923-16383
31c819ff92f73294c47480e7107d2cbaf2df1c42 172.30.190.215:6379@16379 slave ff0d4b630bfbc015a192db93911d4c27d9ccc0fa 0 1564968491414 10 connected
a93fbcda1dd4ff810aa39717195ddbfb1cc7292a 172.30.190.216:6379@16379 slave 4aab750764825dadf4fddb67eaf7c2df6101195d 0 1564968493419 11 connected
19dcb21943fcbdb6244052f2f76ba2dc652900bd 172.30.190.217:6379@16379 slave 643ac620d961badfef08c39e815724503dbd53de 0 1564968492417 12 connected
a42b74b5a3f8002aac192c9e936c28382ab4fca9 172.30.155.92:6379@16379 slave 643ac620d961badfef08c39e815724503dbd53de 0 1564968491000 12 connected
a1bc886c988fb43538c96cf64261470d2b0a3070 172.30.155.93:6379@16379 slave ff0d4b630bfbc015a192db93911d4c27d9ccc0fa 0 1564968491000 10 connected
07fbf589aff23033581264a9734d8bd7d6a0c570 172.30.155.94:6379@16379 slave 4aab750764825dadf4fddb67eaf7c2df6101195d 0 1564968487000 11 connected
$ kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
redis-cluster-1-0 1/1 Running 0 16h 172.30.46.227 10.193.10.58 <none> <none>
redis-cluster-1-1 1/1 Running 0 16h 172.30.46.228 10.193.10.58 <none> <none>
redis-cluster-1-2 1/1 Running 0 16h 172.30.46.229 10.193.10.58 <none> <none>
redis-cluster-2-0 1/1 Running 0 16h 172.30.155.92 10.192.57.161 <none> <none>
redis-cluster-2-1 1/1 Running 0 16h 172.30.155.93 10.192.57.161 <none> <none>
redis-cluster-2-2 1/1 Running 0 16h 172.30.155.94 10.192.57.161 <none> <none>
redis-cluster-3-0 1/1 Running 0 16h 172.30.190.215 10.212.5.223 <none> <none>
redis-cluster-3-1 1/1 Running 0 16h 172.30.190.216 10.212.5.223 <none> <none>
redis-cluster-3-2 1/1 Running 0 16h 172.30.190.217 10.212.5.223 <none> <none>
以下是对该终端的操作记录的处理结果:通过执行以下命令,将172.30.46.228:6379的5461-10922数据迁移到redis-cluster-2-1 172.30.155.93,并将172.30.46.229:6379的10923-16383数据迁移到redis-cluster-3-2 172.30.190.217。根据结果可以看出,Redis主节点已经被成功迁移。
$ kubectl exec -it redis-cluster-2-1 -- redis-cli -c cluster failover
OK
$ kubectl exec -it redis-cluster-3-2 -- redis-cli -c cluster failover
OK
$ kubectl exec -it redis-cluster-1-0 -- redis-cli -c cluster nodes
31c819ff92f73294c47480e7107d2cbaf2df1c42 172.30.190.215:6379@16379 slave a1bc886c988fb43538c96cf64261470d2b0a3070 0 1564968794000 13 connected
a93fbcda1dd4ff810aa39717195ddbfb1cc7292a 172.30.190.216:6379@16379 slave 4aab750764825dadf4fddb67eaf7c2df6101195d 0 1564968791638 11 connected
19dcb21943fcbdb6244052f2f76ba2dc652900bd 172.30.190.217:6379@16379 master - 0 1564968792641 14 connected 10923-16383
ff0d4b630bfbc015a192db93911d4c27d9ccc0fa 172.30.46.227:6379@16379 myself,slave a1bc886c988fb43538c96cf64261470d2b0a3070 0 1564968793000 10 connected
4aab750764825dadf4fddb67eaf7c2df6101195d 172.30.46.228:6379@16379 master - 0 1564968792000 11 connected 5461-10922
643ac620d961badfef08c39e815724503dbd53de 172.30.46.229:6379@16379 slave 19dcb21943fcbdb6244052f2f76ba2dc652900bd 0 1564968794648 14 connected
a42b74b5a3f8002aac192c9e936c28382ab4fca9 172.30.155.92:6379@16379 slave 19dcb21943fcbdb6244052f2f76ba2dc652900bd 0 1564968792000 14 connected
a1bc886c988fb43538c96cf64261470d2b0a3070 172.30.155.93:6379@16379 master - 0 1564968791000 13 connected 0-5460
07fbf589aff23033581264a9734d8bd7d6a0c570 172.30.155.94:6379@16379 slave 4aab750764825dadf4fddb67eaf7c2df6101195d 0 1564968795653 11 connected
进行模拟故障测试
在进行模拟故障测试之前,我们需要进行初始状态确认。确认”cluster_state:ok”显示集群正常运行。然后,将删除redis-cluster-1-*的相关Pod。
$ kubectl exec -it redis-cluster-1-0 -- redis-cli -c cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
<以下省略>
$ kubectl delete -f redis-cluster-mzr-1m.yml
statefulset.apps "redis-cluster-1" deleted
确认状态
可以通过”cluster_state:ok”来确认即使Zone 1的Pod消失了,服务依然正常运行。
$ kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE
redis-cluster-2-0 1/1 Running 0 16h 172.30.155.92 10.192.57.161
redis-cluster-2-1 1/1 Running 0 16h 172.30.155.93 10.192.57.161
redis-cluster-2-2 1/1 Running 0 16h 172.30.155.94 10.192.57.161
redis-cluster-3-0 1/1 Running 0 16h 172.30.190.215 10.212.5.223
redis-cluster-3-1 1/1 Running 0 16h 172.30.190.216 10.212.5.223
redis-cluster-3-2 1/1 Running 0 16h 172.30.190.217 10.212.5.223
$ kubectl exec -it redis-cluster-2-0 -- redis-cli -c cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
<以下省略>
以下是一个列出Redis节点的显示。如果在这里发生了双重故障,并且redis-cluster-2系列停止了,显然可以推断出Redis集群将停止。
$ kubectl exec -it redis-cluster-2-0 -- redis-cli -c cluster nodes
ff0d4b630bfbc015a192db93911d4c27d9ccc0fa 172.30.46.227:6379@16379 slave,fail a1bc886c988fb43538c96cf64261470d2b0a3070 1564968882898 1564968880493 13 connected
4aab750764825dadf4fddb67eaf7c2df6101195d 172.30.46.228:6379@16379 master,fail - 1564968882899 1564968882499 11 connected
643ac620d961badfef08c39e815724503dbd53de 172.30.46.229:6379@16379 slave,fail 19dcb21943fcbdb6244052f2f76ba2dc652900bd 1564968882898 1564968881000 14 connected
a42b74b5a3f8002aac192c9e936c28382ab4fca9 172.30.155.92:6379@16379 myself,slave 19dcb21943fcbdb6244052f2f76ba2dc652900bd 0 1564968978000 7 connected
a1bc886c988fb43538c96cf64261470d2b0a3070 172.30.155.93:6379@16379 master - 0 1564968979845 13 connected 0-5460
07fbf589aff23033581264a9734d8bd7d6a0c570 172.30.155.94:6379@16379 master - 0 1564968977838 15 connected 5461-10922
31c819ff92f73294c47480e7107d2cbaf2df1c42 172.30.190.215:6379@16379 slave a1bc886c988fb43538c96cf64261470d2b0a3070 0 1564968979000 13 connected
a93fbcda1dd4ff810aa39717195ddbfb1cc7292a 172.30.190.216:6379@16379 slave 07fbf589aff23033581264a9734d8bd7d6a0c570 0 1564968980849 15 connected
19dcb21943fcbdb6244052f2f76ba2dc652900bd 172.30.190.217:6379@16379 master - 0 1564968981851 14 connected 10923-16383
总结
在微服务化的过程中,人们往往只关注Istio,但对于云原生应用来说,无状态化是一个重要的服务。Redis并不是通过将其分布在云中的多个区域,即多个数据中心,来提高可用性和数据保持性的解决方案。在考虑好容器软件与多区域部署要求的前提下,我们才能确定K8s对象的配置。
而在应用层面进行集群化配置的软件,如Redis,在Kubernetes上也需要充分考虑。Red Hat提出的Operator正是为了减轻这种系统管理的负担。我想尽快安排时间进行确认。
请提供相关参考资料
-
- 動的プロビジョニング: ステートフル・セット作成時の PVC の作成, https://cloud.ibm.com/docs/containers?topic=containers-file_storage#file_dynamic_statefulset
Redisクラスタのチュートリアル,https://redis.io/topics/cluster-tutorial
Redisのコマンドライン・クライアント,https://redis.io/topics/rediscli
Redisコマンド,https://redis.io/commands