将灾难恢复组从 Redis 6.0 升级到 Redis 7.2

草稿

此操作步骤为草稿。它描述了 Alauda Cache Service E1 v5.1.0 中实现的升级路径,但尚未完成生产验证。请先在非生产环境中进行演练,为每个成员创建经过验证的备份,并在针对生产数据运行之前联系 Alauda 支持团队。

Redis 6.0 搭载冻结的旧版复制模块。Redis 7.2 搭载新模块,该模块新增了 Active-Active 模式、强制对等身份验证和支持安全轮换的凭证。将现有灾难恢复组从 6.0 移至 7.2,需要对每个成员执行就地版本升级,然后重新连接各链接。

此次升级涉及的内容

升级期间复制会中断

两代模块无法互操作——跨代复制并非设计支持的路径。组无法在继续复制的同时逐个升级成员。第一个成员完成升级后复制即会停止,只有在所有成员都运行 Redis 7.2 且链接已重新创建后,复制才会恢复。

请规划一个覆盖整个组的维护窗口,并在此期间让客户端流量保持在单个成员上。

新模块生效后,各方面的变化如下:

方面Redis 6.0Redis 7.2
链接身份验证实例的 default 账户,通过 spec.activeRedis.redisUserRef 自动绑定绑定了 spec.activeRedis.redisUserRef 的对等身份验证 RedisUser;建议使用专用的 custom 账户
对等传输Proxy RESP 端口(6379)相同的 Proxy RESP 端口(6379)——一个端点承载两种传输
持久化建议使用 RDB仅支持 RDB——不接受 appendonly=yes
数据库数量标准databases 必须为 <= 16
Active-Active 模式不可用可用

前置条件

开始之前,请确认组中每个成员均满足以下条件。

  1. 每个承载成员的集群中的 operator 均为 v5.1.0 或更高版本。

  2. 每个成员都有经过验证的备份。请参阅备份与恢复。

  3. 持久化仅使用 RDB。如果任何成员设置了 customConfig.appendonly: "yes",准入检查将拒绝升级。请在升级前将其更改为 no。

    $ kubectl -n default get redis s6-dc1 -o jsonpath='{.spec.customConfig.appendonly}'
  4. 数据库数量不超过 16 个。如果任何成员将 customConfig.databases 设置为高于 16 的值,升级将被拒绝。该模块拒绝加载超过 16 个数据库,pod 将进入崩溃循环。

    $ kubectl -n default get redis s6-dc1 -o jsonpath='{.spec.customConfig.databases}'
  5. 上游 Proxy 端点保持可访问。新模块通过 Redis 6.0 链接已使用的同一 Proxy 端点进行复制,因此 Disaster Recovery 组无需开放新端口。升级不会重新创建 Proxy Service——它会保留其地址、端口以及在 NodePort 模式下的节点端口——因此请保留现有的防火墙规则和负载均衡器中针对 Proxy RESP 端口的条目,最后可以使用你在步骤 6 中记录的相同地址重新创建链接。

  6. 记录当前拓扑。记录每个成员的 serviceID,以及哪个成员持有指向哪个上游的 ActiveRedisConnection。稍后需要重新创建这些链接。

    $ kubectl -n default get activeredisconnections -o wide
    $ kubectl -n default get redis s6-dc1 -o jsonpath='{.spec.activeRedis.serviceID}'

操作步骤

步骤 1:暂停组活动

将客户端写入流量定向到你打算保留为上游的成员,并确认下游成员已追上。

$ kubectl -n default get activeredisconnections
NAME              INSTANCE   STATUS    MESSAGE   AGE
conn-dc2-to-dc1   s6-dc2     Healthy             8d

继续之前,确认 status.shards[].syncStatus 为 PartialSync,并且偏移量正在推进。

步骤 2:删除链接

在每个下游集群中删除 ActiveRedisConnection。使用默认的 Detach 拆除策略——成员会在升级后返回组中,因此应保留其模块侧的记录状态。

$ kubectl -n default delete activeredisconnections conn-dc2-to-dc1
此处不要使用 Decommission

Decommission 会永久释放对等端的 Oplog 保留下限和墓碑垃圾回收下限,且无法撤销。它适用于已永久停用的数据中心,而不适用于升级。

步骤 3:将每个成员升级到 Redis 7.2

更改每个成员上的 spec.version。实例会执行滚动重启。

$ kubectl -n default patch redis s6-dc1 --type=merge --patch='{"spec": {"version": "7.2"}}'

等待实例恢复到 Ready,再升级下一个成员:

$ kubectl -n default get redis s6-dc1
升级顺序

由于步骤 2 中已删除链接,不存在需要据此安排升级顺序的活动复制,因此可以按任意顺序升级成员。

对于之后对活动组执行的同代升级,顺序规则仍然重要:安全顺序是先下游、后上游,因为下游可以应用版本相同或更旧的上游的 Oplog,但不能应用更新版本的 Oplog。如果在活动下游之前提升上游版本,准入检查会发出警告,模块会暂停受影响的边——到版本差异得到解决之前,向该下游的复制都会暂停。

升级期间建立的链接会暂停,而不会中断

此升级同样受这一保护机制约束。如果上游已经运行 Redis 7.2,而下游仍运行 6.0 时创建连接,该连接会被接受,然后暂停——它不会执行复制,也不会损坏任一端:

$ kubectl -n default get activeredisconnections
NAME              INSTANCE   STATUS   MESSAGE                                                                                                  AGE
conn-dc2-to-dc1   s6-dc2     Paused   upstream version 7.2 newer than local version 6.0; waiting for local upgrade (safe order upgrades the downstream first)   18s

在本地成员完成升级之前,它会保持为 Paused。按照本操作步骤,在步骤 2 中删除链接、在步骤 5 中重新创建链接,就不会遇到这种情况;如果时机错误地创建了链接,这就是其表现形式。

步骤 4:确认对等身份验证绑定

新模块要求每个实例都具有对等身份验证凭证。对于已启用跨数据中心复制的实例,平台会自动绑定一个凭证——请参阅自动对等身份验证绑定。

检查每个成员最终使用的绑定:

$ kubectl -n default get redis s6-dc1 -o jsonpath='{.spec.activeRedis.redisUserRef}'
rfr-acl-s6-dc1-default

$ kubectl -n default get redisusers rfr-acl-s6-dc1-default
NAME                     INSTANCE   USERNAME   PHASE     AGE
rfr-acl-s6-dc1-default   s6-dc1     default    Success   8d

如果绑定为空,或者 RedisUser 阶段不是 Success,请按照如果尚未完成绑定中的说明解决,然后再继续。

每个成员必须使用相同的凭证

只有当入站对等端提供的凭证——用户名和密码——与本地对等身份验证凭证匹配时,模块才会接受该对等端。自动绑定使用每个实例的 default 账户,因此已经相互复制的成员会收敛到同一个凭证:共享的 default 密码正是其 Redis 6.0 链接用于身份验证的密码。

如果你在任何成员上手动绑定了凭证,请在创建任何链接之前,确认每个成员都使用相同的用户名和相同的密码值。

步骤 5:重新创建链接

在每个下游集群中创建 ActiveRedisConnection。现在不再有凭证字段——链接使用实例的对等身份验证绑定——上游地址与 Redis 6.0 链接使用的地址相同:

$ cat << EOF | kubectl -n default create -f -
apiVersion: redis.middleware.alauda.io/v1alpha1
kind: ActiveRedisConnection
metadata:
  name: conn-dc2-to-dc1
spec:
  instance: s6-dc2
  addresses:
  - 192.168.1.10:6379
  teardownPolicy: Detach
EOF

准入检查会解析对等身份验证绑定,使用数据路径将采用的凭证执行预检,并连接上游端点。如果此时失败,错误会指出具体问题。

步骤 6:验证

$ kubectl -n default get activeredisconnections conn-dc2-to-dc1
NAME              INSTANCE   STATUS    MESSAGE   AGE
conn-dc2-to-dc1   s6-dc2     Healthy             45s

确认:

  • 连接状态为 Healthy;
  • status.credentialMode 为 peer-auth-global;
  • status.shards[].status 为 Connected,并且偏移量正在推进;
  • status.shards[].syncStatus 为 PartialSync——新模块会从 Redis 6.0 链接停止时的偏移量继续处理链接,因此重新创建的链接会恢复复制,而不会重新发送数据集。如果此处出现 FullSync,表示保留的状态不可用,正在重新发送整个数据集;它仍会最终收敛,但会产生一次完整传输的开销;
  • 上游的 ActiveRedis 报告预期的 status.downstreamPeerCount;
  • 在上游写入的键会出现在每个下游中,同时升级前已有的键也仍然存在。

然后恢复正常的客户端流量。

自动对等身份验证绑定

新模块要求必须存在对等身份验证绑定,否则会阻止执行升级的更新操作。平台通过记录 Redis 6.0 链接一直使用的凭证来处理这一问题。

Redis 6.0 链接从未携带用户名:其凭证中只有密码,没有其他内容,因此模块发送了一个单参数的 AUTH,由代理将其解析为实例的默认帐户。该帐户就是链接凭证。因此,operator 会绑定实例自身的默认帐户 RedisUser:

实例架构绑定的 RedisUser
Clusterdrc-acl-<instance-name>-default
Sentinel、standalonerfr-acl-<instance-name>-default

不会配置任何内容。该帐户已经存在——实例自身的 reconcile 会创建它——其中的密码正是链接使用的密码,因此绑定不会改变网络传输中的任何字节。

对于启用了复制且尚未设置绑定的任何 Redis 6.0 实例,绑定会在其第一次 reconcile 时应用,因此通常会在开始操作前就已就位。如果版本变更先到达,defaulting webhook 会填入相同的名称。

自动路径的范围
  • 在 Redis 7.2 上创建并启用复制的全新实例必须显式提供绑定——没有绑定时会被拒绝。
  • 手动绑定中出现拼写错误时,会显示为 RedisUser not found,而不会被静默创建。
  • 已设置的绑定绝不会被覆盖。
自动绑定会将链接绑定到实例密码

由于它绑定的是 default 帐户,因此组中的每个成员都必须使用相同的 default 密码,链接才能完成身份验证——Redis 6.0 链接原本就是如此,并且现在轮换该密码也会同时轮换复制凭证。

如需使二者相互独立,请在每个成员上创建专用的 custom RedisUser——所有成员使用相同的用户名和相同的密码值——然后改为绑定该凭证;请参阅灾难恢复设置的步骤 1。请在步骤 5 之前,并在每个成员上完成此操作。

如果绑定尚未就位

绑定仅会因一个原因而延迟:默认帐户的 RedisUser 尚不存在。实例自身的 reconcile 会创建它,绑定则会在后续的一次处理过程中写入。如果实例已经处于 Ready 状态后绑定仍为空,请手动设置:

$ kubectl -n default patch redis s6-dc1 --type=merge \
    --patch='{"spec": {"activeRedis": {"redisUserRef": "rfr-acl-s6-dc1-default"}}}'

升级后

组运行在 Redis 7.2 上后,以下功能即可使用:

  • Active-Active 模式:每个数据中心都接受写入,而不是遵循单个上游。请参阅设置 Active-Active Replication。请注意,spec.activeRedis.mode 用于选择实例的连接方式;将现有 Disaster Recovery 组迁移到 Active-Active 是一项单独的变更,不在本操作步骤的范围内。
  • 支持轮换的凭证。对等链接不再在连接配置时固定凭证,因此轮换 peer-auth 密码不会在下次重新连接时导致链接中断。
  • 停用拆除,用于永久退役某个数据中心。

本草案的已知缺口

  • 此操作步骤要求整个组进入完整的维护窗口。目前没有受支持的方法可以在保持复制运行的同时升级组。
  • 不支持从 Redis 7.2 降级回 Redis 6.0——但没有任何机制阻止此操作。将 spec.version 改回 6.0 会被 admission 接受,并立即开始将 pod 滚动更新为较旧的镜像。如果必须放弃升级,请从先决条件中创建的备份恢复各成员,而不要反转版本。
  • Web Console 不提供此升级的引导流程;上述步骤由 CLI 驱动。