Active-Active 运维
设置 Active-Active 复制组后对其进行运行:保持成员时钟一致,调整 gossip 和成员资格计时器,以及添加或移除成员。有关设置操作步骤,请参阅 Setup;有关这些事项为何重要,请参阅 Architecture。
时钟同步
该模块会测量时钟偏差并针对其发出告警,但绝不会阻止写入,也不会因此丢弃对等节点。保持每个成员的时钟同步是 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 报告。正值表示所列成员的时钟快于你正在读取的成员。
告警级别
偏差降至其阈值的 0.8 倍以下后,告警会在带滞回的情况下自动清除。无需重启。
如何检查差值
从资源开始,该资源会报告本地实例已知的每个成员的偏移量:
第一行是本地成员。它不包含地址和偏移量,因为它是其他偏移量的测量基准,因此这里只能读取远程行。
对于聚合仪表和归因标志,请在实例本身上读取。请针对分片的主节点运行此命令:只有主节点参与 gossip,因此从节点上的网格计数器仅描述被动到达从节点的内容,并不代表网格的视图。
也可以直接从模块获取每个成员的偏移量,每行显示为 clock_offset_ms。请针对分片的主节点运行,原因同上:
如何确认时钟偏差导致了问题
- 确认告警确实已触发。
clock_skew_violations或clock_skew_critical_violations不为零,并且 Redis 日志中包含匹配的行。提示行以clock-skew advisory:开头,严重行以CRITICAL clock-skew (resurrection risk):开头;两者都会指明问题成员的svc=(其serviceID)和测得的偏移量。任何早于告警发生的问题都不是时钟偏差问题。 - 确定哪个时钟有误。 如果
local_clock_is_outlier为1,则此节点的时钟是异常值,应修复此节点。否则,错误时钟属于具有最大clockOffsetMs的成员。 - 检查是否具备归因条件。 只有至少有三个成员时才能确定责任。在双成员组中,模块只能报告这两个成员存在分歧;请改为将两个节点分别与外部时间参考进行比较。
- 将症状与级别对应起来。 实际时间中较晚写入的值却输给了较早的写入,这与提示级别相符。某个成员上删除的元素后来重新出现,这与严重级别相符——检查
clock_skew_critical_violations是否在同一时间窗口内增加。 - 不要将
hlc_drift_rejected读取为偏差测量值。INFO activeredis的crdt部分中的该计数器,会统计在时间戳比较中落败的写入。普通的并发写入期间该计数器也会增加。当它与非零的current_max_skew_ms一同上升时,它只是佐证症状,仅此而已——current_max_skew_ms才是测量值。
修正问题时钟后,告警会自行清除。已经以错误方式解决的数据不会重新解决:因偏差比较而落败的值会继续丢失,重新出现的元素也会继续存在,直到再次将其删除。
调优
spec.tuning 将可选的 gossip 和成员资格计时器推送到每个节点。
maxClockSkewMs 的 API 描述指出,0 会禁用告警。对于模块设置而言确实如此,但无法通过 spec.tuning 设置:只有大于零的值才会被推送到节点,因此 0 表示“什么都不推送”——模块会保留其自身的 2000 毫秒默认值,或上次推送的值。
提高阈值只会静默提示级别。严重级别固定为 150 秒,无法更改或禁用。
只有值大于零的字段才会被推送。清除之前设置的字段不会重置已配置的 pod——它们会保留上次的值,直到重启。模块会交叉验证这些值的组合,并拒绝不一致的组合;相关情况会显示在 mesh 状态中。
管理成员关系
添加成员
在新实例上启用 Active-Active 模式,然后使用现有成员作为种子创建其 ActiveRedisMesh——请参阅设置。新成员会加入网格,并通过 gossip 被整个网格发现——现有成员无需更新其种子列表,网格即可完成收敛。不过,仍建议更新这些列表,以便某个成员在原始种子不可达的情况下重启后仍能重新加入。
移除成员
- 从存活成员的
spec.seeds中移除离开成员的地址。 - 删除离开成员的
ActiveRedisMesh资源,然后删除其Redis实例。
没有资源级别的编排机制来遗忘或解除封禁已离开的成员。其记录会根据模块自身的计时器从存活成员的成员关系中过期移除,在此期间,网格阶段可能会报告 Degraded。存活成员在整个过程中仍会彼此继续复制。
限制
Active-Active 模式继承了限制和风险中列出的约束,包括成员之间匹配的架构和分片布局、[0-15] serviceID 范围,以及仅支持 RDB 持久化。
由于每个成员都接受写入,Active-Active 会持续执行冲突解决规则。在将应用指向 Active-Active 组之前,请阅读命令支持——其中列出了哪些命令会被拒绝、哪些命令会在本地运行而不进行复制,以及每个会复制的命令如何解决冲突。