Active-Active 运维

设置 Active-Active 复制组后对其进行运行:保持成员时钟一致,调整 gossip 和成员资格计时器,以及添加或移除成员。有关设置操作步骤,请参阅 Setup;有关这些事项为何重要,请参阅 Architecture。

时钟同步

同步时钟是 Active-Active 的前提条件

该模块会测量时钟偏差并针对其发出告警,但绝不会阻止写入,也不会因此丢弃对等节点。保持每个成员的时钟同步是 operator 的责任。在复制组的每个集群的每个节点上运行时间同步守护进程(ntpd、chronyd 或平台等效项),最好使用同一个时间源,并监控下面所述的告警。

此要求特定于 Active-Active。在 Disaster Recovery 模式下,只有上游节点接收 Application 写入,因此不存在需要由时钟偏差进行仲裁的并发冲突写入。

时钟为何重要

以下两种影响都与时钟之间的差距成正比,并且发生时都不会产生错误。

  • 冲突解决由墙上时钟时间决定。 last-write-wins 规则会比较 Hybrid Logical Clock,其物理组件是本地墙上时钟的毫秒数。因此,时钟快于其他成员的成员会赢得本应输掉的冲突:另一成员在实际时间中较晚写入的值,会被快时钟成员较早的写入静默覆盖。Command Support 中的每个 last-write-wins 命令都会受到影响,包括 hashes 和 sorted sets 使用的按字段解决方式。
  • 已删除的数据可能重新出现。 删除操作会作为 tombstone 保留,直到每个活动成员都确认该删除操作,且 tombstone 至少已有五分钟。此时间下限按墙上时钟时间测量。当偏差接近该时间的一半时,一个成员可能释放 tombstone,而来自时钟偏差成员的并发添加仍在传输中,导致已删除元素在那里重新出现。这是真正的收敛中断,而不是暂时性不一致。

如何测量差值

  • 在 peer-port 握手期间,成员交换四个时间戳,并计算经过往返校正的偏移量,即 NTP 使用的相同计算方式。对于某个 peer 获取的样本,会保留往返时间最短的样本;样本过时后会被替换,因此排队延迟不会使读数产生偏差。
  • 在两次握手之间,gossip 会为每个成员刷新一个较粗略的偏移量,大约每个 gossip 轮次一次。
  • 模块大约每秒一次将所有成员中的最大绝对偏移量作为当前偏差,并据此评估两个告警级别。

偏移量始终以相对于读取它的成员的毫秒数 remote clock − local clock 报告。正值表示所列成员的时钟快于你正在读取的成员。

告警级别

级别阈值后果
提示spec.tuning.maxClockSkewMs — 模块默认值为 2000 毫秒冲突解决出现偏差,快时钟会获胜,但成员仍会收敛。记录为警告;计入 clock_skew_violations。
严重固定为 150000 毫秒(150 秒),即五分钟 tombstone 时间下限的一半。不可配置。已删除的数据可能在某个成员上重新出现。记录为 CRITICAL;计入 clock_skew_critical_violations。立即同步时钟。

偏差降至其阈值的 0.8 倍以下后,告警会在带滞回的情况下自动清除。无需重启。

如何检查差值

从资源开始,该资源会报告本地实例已知的每个成员的偏移量:

$ kubectl -n default get activeredismesh mesh-dc1 \
    -o jsonpath='{range .status.members[*]}{.address}{"\t"}{.state}{"\t"}{.clockOffsetMs}{"\n"}{end}'
	alive	
redis-dc2.example.com:6379   alive   -5
redis-dc3.example.com:6379   alive   4180

第一行是本地成员。它不包含地址和偏移量,因为它是其他偏移量的测量基准,因此这里只能读取远程行。

对于聚合仪表和归因标志,请在实例本身上读取。请针对分片的主节点运行此命令:只有主节点参与 gossip,因此从节点上的网格计数器仅描述被动到达从节点的内容,并不代表网格的视图。

$ redis-cli INFO activeredis | grep -E \
    'mesh_max_clock_skew_ms|current_max_skew_ms|peak_skew_ms|clock_skew_violations|clock_skew_critical_violations|local_clock_is_outlier'
字段含义
mesh_max_clock_skew_ms当前在此节点上生效的提示阈值。
current_max_skew_ms当前到任一成员的最大绝对偏移量。这是应针对其配置告警的测量值。
peak_skew_ms自节点启动以来的高水位标记。
clock_skew_violations已发生的提示事件次数。
clock_skew_critical_violations已发生的严重事件次数。
local_clock_is_outlier当此节点的时钟与其他所有时钟不一致时为 1。

也可以直接从模块获取每个成员的偏移量,每行显示为 clock_offset_ms。请针对分片的主节点运行,原因同上:

$ redis-cli as.mesh nodes

如何确认时钟偏差导致了问题

  1. 确认告警确实已触发。 clock_skew_violations 或 clock_skew_critical_violations 不为零,并且 Redis 日志中包含匹配的行。提示行以 clock-skew advisory: 开头,严重行以 CRITICAL clock-skew (resurrection risk): 开头;两者都会指明问题成员的 svc=(其 serviceID)和测得的偏移量。任何早于告警发生的问题都不是时钟偏差问题。
  2. 确定哪个时钟有误。 如果 local_clock_is_outlier 为 1,则此节点的时钟是异常值,应修复此节点。否则,错误时钟属于具有最大 clockOffsetMs 的成员。
  3. 检查是否具备归因条件。 只有至少有三个成员时才能确定责任。在双成员组中,模块只能报告这两个成员存在分歧;请改为将两个节点分别与外部时间参考进行比较。
  4. 将症状与级别对应起来。 实际时间中较晚写入的值却输给了较早的写入,这与提示级别相符。某个成员上删除的元素后来重新出现,这与严重级别相符——检查 clock_skew_critical_violations 是否在同一时间窗口内增加。
  5. 不要将 hlc_drift_rejected 读取为偏差测量值。 INFO activeredis 的 crdt 部分中的该计数器,会统计在时间戳比较中落败的写入。普通的并发写入期间该计数器也会增加。当它与非零的 current_max_skew_ms 一同上升时,它只是佐证症状,仅此而已——current_max_skew_ms 才是测量值。

修正问题时钟后,告警会自行清除。已经以错误方式解决的数据不会重新解决:因偏差比较而落败的值会继续丢失,重新出现的元素也会继续存在,直到再次将其删除。

调优

spec.tuning 将可选的 gossip 和成员资格计时器推送到每个节点。

字段模块默认值描述
gossipIntervalMs1000gossip 轮次间隔。
suspectTimeoutMs8000无响应成员变为 suspect 前的等待时间。
deadTimeoutMs15000可疑成员变为 dead 前的等待时间。
banTTLMsapproximately 1 hour成员被遗忘后应用的基础封禁时长。
maxClockSkewMs2000时钟偏差提示告警阈值,单位为毫秒。请参阅时钟同步。
无法通过资源禁用 maxClockSkewMs

maxClockSkewMs 的 API 描述指出,0 会禁用告警。对于模块设置而言确实如此,但无法通过 spec.tuning 设置:只有大于零的值才会被推送到节点,因此 0 表示“什么都不推送”——模块会保留其自身的 2000 毫秒默认值,或上次推送的值。

提高阈值只会静默提示级别。严重级别固定为 150 秒,无法更改或禁用。

$ kubectl -n default patch activeredismesh mesh-dc1 --type=merge \
    --patch='{"spec": {"tuning": {"gossipIntervalMs": 500}}}'
调优值具有持久性(alpha)

只有值大于零的字段才会被推送。清除之前设置的字段不会重置已配置的 pod——它们会保留上次的值,直到重启。模块会交叉验证这些值的组合,并拒绝不一致的组合;相关情况会显示在 mesh 状态中。

管理成员关系

添加成员

在新实例上启用 Active-Active 模式,然后使用现有成员作为种子创建其 ActiveRedisMesh——请参阅设置。新成员会加入网格,并通过 gossip 被整个网格发现——现有成员无需更新其种子列表,网格即可完成收敛。不过,仍建议更新这些列表,以便某个成员在原始种子不可达的情况下重启后仍能重新加入。

移除成员

  1. 从存活成员的 spec.seeds 中移除离开成员的地址。
  2. 删除离开成员的 ActiveRedisMesh 资源,然后删除其 Redis 实例。
不支持编排式遗忘(alpha 限制)

没有资源级别的编排机制来遗忘或解除封禁已离开的成员。其记录会根据模块自身的计时器从存活成员的成员关系中过期移除,在此期间,网格阶段可能会报告 Degraded。存活成员在整个过程中仍会彼此继续复制。

限制

Active-Active 模式继承了限制和风险中列出的约束,包括成员之间匹配的架构和分片布局、[0-15] serviceID 范围,以及仅支持 RDB 持久化。

由于每个成员都接受写入,Active-Active 会持续执行冲突解决规则。在将应用指向 Active-Active 组之前,请阅读命令支持——其中列出了哪些命令会被拒绝、哪些命令会在本地运行而不进行复制,以及每个会复制的命令如何解决冲突。