设置灾难恢复复制
灾难恢复(spec.activeRedis.mode: peerof)是一种热备拓扑:上游实例将定向复制链路扇出到一个或多个下游实例;如果上游数据中心发生故障,每个下游实例都可以随时提升为主实例。设置灾难恢复包括两部分 — 在每个 Redis 实例上启用跨数据中心复制,以及建立从下游实例到上游实例的连接。
对于每个数据中心都接受写入的替代拓扑,请参阅设置 Active-Active 复制。
在灾难恢复组中,每个 Redis 实例的角色并不固定,可以是上游实例,也可以是下游实例。下游实例不会限制数据写入,但写入下游实例的数据将被从上游实例同步的数据覆盖,或者由于数据类型冲突而导致同步失败。
目录
选择适用于 Redis 版本的操作步骤Redis 7.2 — 新模块步骤 1:在每个数据中心创建 peer-auth 凭证专用账户(建议)实例的默认账户(备用方案)步骤 2:在上游实例上启用复制步骤 3:公开上游 proxy步骤 4:在下游实例上启用复制步骤 5:在下游侧创建连接步骤 6:验证Redis 6.0 — 旧版模块上游侧使用 LoadBalancer 作为上游 Proxy 的访问地址下游侧预检查删除连接凭证轮换选择适用于 Redis 版本的操作步骤
两个模块版本的设置操作步骤不同。请按照与 Redis 版本匹配的部分执行。
不支持 Redis 6.0 与 Redis 7.2 之间的复制。请参阅模块版本及适用性。
Redis 7.2 — 新模块
新模块需要 peer-auth 凭证:复制组中的每个实例都通过 spec.activeRedis.redisUserRef 绑定一个 RedisUser。operator 会将该凭证推送到每个节点,入站和出站 peer 链路都会使用它进行身份验证。未绑定凭证时启用复制会在准入阶段被拒绝,并且该模块会拒绝所有入站 peer 复制。
只有当入站 peer 提供的凭证与本地 peer-auth 凭证匹配时,模块才会接受该 peer。因此,组中的每个成员都必须携带相同的用户名和相同的密码值。如果两个数据中心的凭证不同,则在准入阶段会拒绝建立它们之间的链路:
绑定的账户可以是为此创建的 custom,也可以是实例自身的 default 账户。system 账户始终会被拒绝。
步骤 1:在每个数据中心创建 peer-auth 凭证
专用账户(建议)
custom RedisUser 可使复制凭证与实例密码分离:绑定默认账户会扩大该密码的授权范围 — 任何持有该密码的实体都可以充当复制 peer — 并使凭证轮换与实例密码轮换关联起来。
在每个数据中心创建相同的账户。密码 Secret 与 RedisUser 对象是一对一关系,因此每个数据中心都需要自己的 Secret 对象;跨数据中心需要匹配的是密码值,而不是 Secret 名称。
密码会在准入阶段进行策略检查:长度为 8 到 32 个字符,并且必须同时包含字母、数字和特殊字符。不符合要求的值会在创建 RedisUser 时被 password should consists of letters, number and special characters 拒绝。
等待账户就绪后再绑定 — 除非其阶段为 Success,否则绑定会被拒绝:
实例的默认账户(备用方案)
也可以改为绑定实例自身的默认账户。这是 Redis 的 6.0 链路一直使用的账户,因此现有组中的每个成员都已经携带匹配的密码。
每个实例都有默认账户 RedisUser,但只有在创建实例时使用了 spec.passwordSecret,该账户才会持有密码 Secret。对于未设置密码创建的实例,账户仍然存在,并报告 Success,但不携带凭证;绑定该账户会使实例的 ActiveRedis 保持为 Failed:
请先设置实例密码,或按上述方式绑定专用账户。
绑定该账户要求组中的每个成员都携带相同的 default 密码。这不会自动完成 — 每个实例的密码取决于其自身的 spec.passwordSecret — 并且每个成员都需要自己的 Secret 对象:由多个实例共享的 Secret 会被它们各自的 RedisUser 控制器争用,永远无法稳定下来。在连接链路之前,请先在每个成员上设置相同的密码值。
默认账户 RedisUser 按实例命名:
检查它是否已在两个数据中心完成配置:
绑定本身将在步骤 2 中与其余复制配置一起设置。
步骤 2:在上游实例上启用复制
serviceID 的取值范围为 [0-15],并且在复制组内必须唯一。之后无法更改该值,启用复制后也无法关闭复制。
首次启用复制时,实例会对其数据 Pod 执行滚动重启 — 此时会创建 proxy,并将模块交付到每个 Pod。实例会离开 Ready,其 ActiveRedis 会在 Pod 滚动期间处于 Pending:
继续之前请等待两者都稳定下来。步骤 4 中的下游实例也适用同样情况,而步骤 5 会连接上游实例,因此上游实例也必须先稳定下来。
实例还必须满足新模块的限制条件,这些条件由准入机制强制执行:
customConfig.appendonly不得为yes— 新模块仅支持 RDB。customConfig.databases必须为<= 16— 模块拒绝加载超过 16 个数据库,否则 Pod 会进入崩溃循环。
步骤 3:公开上游 proxy
启用复制后,proxy 默认提供一个 NodePort 访问地址。单个基于 NodePort 的访问地址不具备高可用性。在生产环境中,请使用 LoadBalancer 地址:
每个实例都会创建一个名为 activeredis-proxy-<instance-name> 的 proxy Service。
对于 Sentinel 实例,实例名称需要添加
rfr-前缀。
peerof 实例的 proxy Service 公开一个端口 6379。proxy 在该端口上承载 RESP 控制平面和复制流,并按会话区分二者,因此下游只需要访问一个地址 — 即填入 spec.addresses 的地址。灾难恢复模式不会创建其他端口,数据中心之间也无需开放其他端口。
连接准入会连接其为复制流解析出的端点;如果该端点不可访问,则拒绝连接。当 spec.peerPort 未设置时,该端点就是 spec.addresses[0] 中的地址本身。
步骤 4:在下游实例上启用复制
按照步骤 1 创建下游实例的 peer-auth 账户 — 用户名和密码值必须与上游实例相同。然后使用不同的 serviceID 启用复制:
这也会重启下游实例的数据 Pod。请等待实例达到 Ready 后再执行步骤 5。
步骤 5:在下游侧创建连接
ActiveRedisConnection 创建在下游集群中。spec.instance 指定本地(下游)实例,spec.addresses 指向上游 proxy 的 RESP 端点。
连接名称会成为模块的 peer 名称,其命名限制比 Kubernetes 更严格:
- 必须以字母开头 —
1conn会被拒绝; reset、clear、all和list是保留字,会被拒绝。
一个实例最多可以持有一个 ActiveRedisConnection — 即其自身的上游链路。一个上游连接多个下游的扇出拓扑,是通过在每个下游集群中创建一个连接来构建的,并且这些连接都指向同一个上游实例。
步骤 6:验证
PEERS 统计上游链路数量,DPEERS 统计下游链路数量,因此每一侧都会显示一个值,并将另一项留空 — 零值不会显示。上游实例的扇出为 status.downstreamPeerCount。
检查连接以查看每个 shard 的同步详细信息:
status.shards[].status 表示 shard 的连接状态,status.shards[].syncStatus 表示数据同步状态:PartialSync(Oplog 的增量同步)或 FullSync(正在同步 RDB)。流被应用时,offset 和 opId 会持续推进 — 如果某个 Connected shard 中这两个值停滞不前,就说明需要进一步检查。
status.shards[].versionState 记录模块自身对上游版本的判断 — unknown、verified 或 violating。因版本不一致而被挂起的 shard 还会携带非零的 versionHoldSince。
status.credentialMode: peer-auth-global 表示模块侧的 peer 记录不包含内联凭证 — 出站连接会读取节点本地的 peer-auth 凭证,因此凭证轮换会在下一次重新拨号时生效。
原生 Redis Sentinel 模式没有 shard 的概念。此处将 Sentinel 的主节点-从节点对抽象为
shard 0,以便与 Redis cluster 模式使用相同的数据结构。
Redis 6.0 — 旧版模块
Redis 6.0 包含冻结的旧版模块,为兼容现有实例而保留。它仅支持 Disaster Recovery,不支持 Active-Active 模式,也不支持模块级 peer 身份验证。在 Redis 6.0 上启用复制时会返回准入警告,建议使用 Redis 7.2。对于新部署,请使用 Redis 7.2。
上游侧
首先需要创建 Redis 实例。
启用 Disaster Recovery
此命令会返回准入警告,建议使用 Redis 7.2;随后应用补丁。实例接着会对其数据 pod 执行滚动重启以启动代理,因此请等待实例再次处于 Ready 状态后再继续。
使用 LoadBalancer 作为上游 Proxy 的访问地址
为实例启用灾难恢复支持后,灾难恢复 Proxy 默认提供 NodePort 访问地址。单个基于 NodePort 的访问地址不具备高可用性。在生产环境中,可以使用 LoadBalancer 地址为 Proxy 提供访问。
每个灾难恢复实例都会创建一个 Proxy Service,其名称遵循以下格式:activeredis-proxy-<instance-name>。
对于 Sentinel 实例,实例名称需要添加
rfr-前缀
下游侧
启用 Disaster Recovery
serviceID 的范围为 [0-15]。在同一个灾难恢复集群中,serviceID 不能重复。
等待实例和 peer-auth 绑定
此侧也会滚动重启其数据 pod。在实例仍处于滚动重启状态时,随后创建的连接会被拒绝 — 它进行身份验证时使用的 RedisUser 必须报告 Success,而节点恢复期间不会报告该状态:
operator 会在后续 reconcile 中添加绑定,而绑定仍为空时连接会被拒绝。请等待实例处于 Ready 状态,并且此命令输出一个名称:
配置 Disaster Recovery 连接
注意替换上游地址。
Redis 6.0 链接使用实例的默认账户进行身份验证。operator 会为你记录这一点:它首次 reconcile 启用了复制但没有绑定的实例时,会将 spec.activeRedis.redisUserRef 设置为该实例自身默认账户的 RedisUser — 集群实例为 drc-acl-<instance-name>-default,Sentinel 实例为 rfr-acl-<instance-name>-default。不会预置任何内容:该账户已经存在,其中的密码就是链接使用的密码。因此,两个数据中心都必须使用相同的 default 密码 — 密码的值相同,但分别位于各自的 Secret 对象中。
连接上没有凭证字段。spec.secretName 已被移除,仍设置该字段的清单会因 unknown field "spec.secretName" 而被拒绝。
在 v5.1 之前,Redis 6.0 上的 Disaster Recovery 是该产品提供的唯一跨数据中心复制方式,链接凭证位于连接上:ActiveRedisConnection.spec.secretName 指向一个包含 password 且不包含其他内容的 Secret。
其中从未有过用户名。未设置用户名时,旧版模块会发送一个单参数的 AUTH <password>,而 Proxy 会将其解析为实例的默认账户 — 因此 Redis 6.0 链接始终使用 default 进行身份验证,无论 Secret 的名称是什么。因此,两个数据中心一直都必须使用相同的 default 密码。
v5.1 从 ActiveRedisConnection 和 ActiveRedisInspection 中移除了 spec.secretName,并且仅从 spec.activeRedis.redisUserRef 获取凭证。现有组无需进行任何干预:升级后的首次 reconcile 中,operator 会在每个复制实例上写入绑定,使其指向该实例自身默认账户的 RedisUser。链路上的任何内容都不会改变 — 仍是同一个账户、同一个密码,只是读取位置发生了变化 — 运行中的链接在升级期间会保持正常。
发生变化的是清单。已存储的 ActiveRedisConnection 仍可正常工作,但下一次 apply 时,仍设置 secretName 的清单会被拒绝,并且该字段已从 kubectl explain 中移除。删除该字段,并通过实例的绑定管理凭证。
也可以在 Redis 6.0 上绑定 custom RedisUser — 按照 Redis 7.2 操作步骤中的第 1 步 创建该账户,并在启用复制的同一个补丁中将 spec.activeRedis.redisUserRef 设置为该账户。已经设置的绑定不会被自动绑定替换,之后链接会使用该账户而不是 default 进行身份验证。
但你无法获得轮换安全性。Redis 6.0 链接会将凭证保存在模块的 peer 记录中,链接建立时凭证即被冻结其中,而旧版路径没有自动修复重连机制。更改密码后,运行中的链接会继续工作,直到其会话下一次断开 — 此后链接会保持断开状态:
要恢复连接,需要删除 ActiveRedisConnection 并重新创建它,以便使用当前凭证重新配置 peer。在 Redis 6.0 上规划凭证轮换时,应将其视为链接的短暂中断;或者升级到 Redis 7.2,其会在每次拨号时读取凭证。
检查 Disaster Recovery 连接状态
这里,status.shards[0].status 表示 shard 的连接状态,status.shards[0].syncStatus 表示数据同步状态。同步状态可以是 PartialSync(表示 Oplog 的增量同步)或 FullSync(表示正在同步 RDB)。
原生 Redis Sentinel 模式没有 shard 的概念。此处将 Sentinel 的主节点-从节点对抽象为
shard 0,以便与 Redis cluster 模式使用相同的数据结构。
预检查
在接受连接之前,平台会对上游执行一组预检查。Web Console 通过 Inspect 按钮提供这些检查;在 ActiveRedisConnection 准入期间也会自动执行这些检查,如果检查失败,连接将被拒绝,并显示相应消息。这些检查由 ActiveRedisInspection 资源提供支持。
在 Redis 7.2 上,检查会使用数据路径实际使用的 peer-auth 凭证进行拨号,因此检查成功还可以确认两个数据中心使用相同的凭证。此外,检查还会对其为复制流解析出的端点执行普通 TCP 拨号,即 spec.addresses[0] 中的地址,除非 spec.peerPort 将两者拆分。
删除连接
删除 ActiveRedisConnection 会根据其 spec.teardownPolicy 拆除链路:
Decommission 是永久离线的数据中心应使用的正确拆除方式——已分离但永不返回的对等端会持续占用其他实例的垃圾回收下限。它无法改回为 Detach:
该决定会记录在对等端的 serviceID 中,并在重启后保留。它改变的是此实例为该对等端保留的内容:该 serviceID 的 Oplog 保留、墓碑垃圾回收和堆积量记录都会被释放,且不会再次启用。重新创建与该对等端的连接并不会受到阻止,立即创建的连接仍会从两端当时持有的状态恢复——因此,重新创建链路并不能撤销停用;如果对等端预计会恢复,应将其分离,而不是停用。
凭证轮换
在 Redis 7.2 上,对等链路不携带内联凭证:出站拨号会读取节点本地的 peer-auth 凭证,该凭证由 operator 在每次 reconcile 时重新推送。要进行轮换,请原地更新每个数据中心的 peer-auth RedisUser 的密码 Secret,使其使用相同的新值,并逐个数据中心操作。RedisUser controller 会重新应用 ACL,operator 会重新推送凭证,proxy 会在下一次重建时接受新凭证。已建立的链路会在整个过程中保持健康并持续复制,包括数据中心持有不同值的时间窗口;轮换后断开的链路会使用当前凭证重新拨号。
不过,该窗口并非毫无限制:值不同时,创建链路会被拒绝,因为准入检查会使用数据路径将使用的凭证拨号连接上游。在为任何成员接入新链路之前,请先完成所有成员的轮换。
绑定专用 custom 账户时,轮换实例自身的密码 Secret 是独立操作,不会影响对等链路。
绑定实例的 default 账户时,其密码 Secret 就是 peer-auth Secret:轮换实例密码时,复制凭证也会随之轮换,复制组中的其他成员也必须跟进。这正是专用账户可以避免的耦合。
以上内容均不适用于 Redis 6.0。其链路会在模块的对等端记录中内联保存凭证,该凭证在链路接通时即已固定;此路径没有自动修复式的重新接线机制——因此,轮换后的链路只能运行到其会话下一次断开为止,随后会报告 shard 0 status is Disconnected 并保持该状态。请删除并重新创建 ActiveRedisConnection,以使用当前凭证重新接线。