分布式系统(E07):控制面共识与数据面复制
“控制面使用共识”经常被压缩成一句过强的判断:只要控制日志安全,整个系统的数据就安全且可用。Kafka KRaft、Kubernetes/etcd与HBase/ZooKeeper都能推翻这种推论。控制面决定当前配置、归属或期望状态;业务字节由另一套存储和复制路径保存;执行者是否仍有权写外部资源,还需要第三类证据。
分布式系统(E06):从 PlusCal 反例到 IronFleet 证明边界四层状态需要四组证据
三组系统的组件名称不同,但故障分析可以沿四层展开。
flowchart TB
CL[控制日志<br/>顺序与法定集合] --> AO[权威对象<br/>配置、归属、期望状态]
AO --> EX[执行者<br/>broker、controller、RegionServer]
DR[业务数据副本<br/>partition log、PV、HFile/WAL] --> EX
RE[恢复证据<br/>ISR、快照、install receipt] --> EX
EX --> EF[外部效果<br/>需要epoch或资源侧校验]
| 层次 | 要回答的问题 | 不能拿什么替代 |
|---|---|---|
| 控制日志 | 哪条元数据变化已经提交 | leader内存、单节点落盘 |
| 权威对象 | 当前topic、Pod、Region归属是什么 | 业务数据是否还存在 |
| 业务数据副本 | 消息、卷内容、HFile由谁持有 | 元数据快照 |
| 恢复与撤权 | 新执行者拿到了什么,旧执行者为何会被拒绝 | “控制面已经选出新主”这句话 |
这个分层是一种可迁移的诊断模式:先为每一层找独立证据,再判断跨层连接是否成立。控制日志里的单值只解决第一层的一部分问题。
Kafka:metadata HW与partition HW不是一个水位
Kafka 4.3.1的KRaft metadata quorum是一条单分区Raft日志。active controller生成topic、partition、ISR和配置等metadata record;voter多数复制后,metadata high watermark才越过这些记录。standby controller重放已提交记录,broker从控制面接收新的元数据映像。
业务消息走另一条路径。每个topic partition仍有partition leader、follower、动态ISR和自己的high watermark。leader根据ISR副本的日志末端推进partition HW;acks=all等待当前ISR,而不是所有assigned replicas,更不是controller多数派。耐久分析至少还要给出replication factor、ISR、min.insync.replicas和unclean leader election设置。
flowchart LR
subgraph CP[控制面]
C1[controller voter] --> ML[metadata log]
C2[controller voter] --> ML
C3[controller voter] --> ML
ML --> MI[metadata HW]
end
subgraph DP[数据面]
PL[partition leader] --> F1[ISR follower]
PL --> F2[ISR follower]
F1 --> PH[partition HW]
F2 --> PH
end
MI --> PL
controller quorum只剩少数派时,新的元数据变化和需要controller推动的故障转移不能提交。broker磁盘上的partition log不会因此自动消失,但这不足以证明已有分区必然继续服务:broker fencing状态、当时ISR、partition leader状态和客户端请求类型都会影响结果。数据仍在、控制决策可提交、请求可完成是三个谓词。
KRaft还给broker注册分配incarnation和epoch,并通过heartbeat维护fenced状态。这些机制防止旧broker继续被当成当前成员,却不能为任意外部副作用提供通用fencing。业务若在消费消息后调用数据库或支付接口,仍要在目标资源侧做幂等或世代检查。
版本边界也影响运维。Kafka 4.x不再提供ZooKeeper模式;官方迁移路径要求先在3.9 bridge release完成转换,再升级到4.x。迁移阶段由KRaft记录主状态并异步写回ZooKeeper,不能写成两个存储的同步原子提交。KAFKA-21062记录的3.9.2迁移确认问题仍提醒:协议设计、发布版本和迁移实现必须分开核对。
Kubernetes:etcd保存API对象,不保存工作负载的一切
Kubernetes把API对象存入etcd。Deployment、Pod、Node、Secret、ConfigMap、Event和Lease都属于这类控制面状态;容器进程、镜像、节点日志、PersistentVolume里的业务字节和外部数据库不属于etcd快照。
kube-apiserver是可水平扩展的API前端,不是Raft leader。etcd成员内部另有Raft leader。kube-controller-manager与kube-scheduler的多实例则通过Kubernetes Lease选择活跃实例。三个“leader”处在不同协议层,不能合成一个主节点概念。
flowchart LR
U[客户端] --> API[kube-apiserver实例]
API --> ETCD[etcd API对象]
CM[controller-manager<br/>Lease holder] --> API
SCH[scheduler<br/>Lease holder] --> API
K[kubelet] -->|watch PodSpec / write status| API
K --> RT[容器运行时]
RT --> PV[卷与外部数据]
ETCD -.不包含.-> PV
etcd丢失多数派后,默认一致读写不能取得新的共识结果。Kubernetes官方运维文档只说已调度Pod“可能继续运行”,同时明确不能调度新Pod。“可能”不能提升为SLA:节点故障后的替代Pod、扩缩容、滚动更新、EndpointSlice刷新和新的绑定都依赖控制路径;应用还可能依赖DNS、存储、凭证或外部服务。
Lease也不是严格fencing。Kubernetes v1.37.1的client-go源码明确说明leader election不保证任意时刻只有一个客户端实际执行关键区。租约续期、进程暂停和时钟速率会留下旧holder与新holder短时重叠的可能。若controller要操作云盘、负载均衡器或其他外部系统,目标API仍需幂等键、compare-and-set或世代令牌。
恢复etcd快照只恢复API对象。旧快照会让revision回退,仍持有旧watch进度的informer可能无法自然发现回退。etcd v3.6建议Kubernetes恢复使用revision bump并mark compacted,使旧watch失效后重新list。快照恢复、缓存重建和PV恢复是三项操作。
HBase:ZooKeeper、hbase:meta与HDFS各管一段
HBase 3.0.0中,HMaster负责元数据操作、Region assignment、负载均衡和故障恢复;RegionServer承载普通get、put和delete。hbase:meta保存Region目录及其位置。WAL、MemStore刷出的HFile/StoreFile和HDFS块副本承担用户数据的持久化与恢复。
ZooKeeper保存协调和发现所需的短状态,例如Master地址、集群成员与会话相关信息,而不是用户表字节。HBase 3.0默认把外部客户端的registry从直接访问ZooKeeper改为向active或standby Master查询;客户端定位Region后仍直连RegionServer。这个变化应称为“ZooKeeper-less client connection”,不是“HBase内部已经移除ZooKeeper”。
flowchart LR
C[HBase client] --> MR[Master registry]
MR -->|返回meta位置| C
C --> META[hbase:meta<br/>由RegionServer承载]
META -->|返回Region位置| C
C -->|用户数据RPC| RS[RegionServer]
RS --> WAL[WAL]
RS --> HF[HFile / StoreFile]
WAL --> HDFS[HDFS块副本]
HF --> HDFS
ZK[ZooKeeper<br/>协调与session] --> HM[HMaster]
ZK --> RS
HM --> META
HBase 2.x和3.0的assignment由AMv2/ProcedureV2推进,不能继续套用1.x时代“Region transition由ZooKeeper多写者znode驱动”的描述。RegionServer失效后,Master还要检测session过期、拆分或回放WAL、重新分配Region并等待打开。备用Master接管不等于用户Region已恢复。
同样需要拆开三种“复制”:HDFS复制物理块;Region Replica提供读取高可用性,TIMELINE读可能陈旧;跨集群复制从WAL异步传播选定column family的edit。Region assignment只是转移服务归属。它既不复制一整份Region,也不能替代上述任何数据恢复机制。
版本核验不能只看滚动文档页。HBase 3.0.0已于2026年8月发布,但官网当前Reference Guide标识为4.0.0-alpha-1-SNAPSHOT,并保留部分旧版本段落。本文的3.0行为以3.0.0设计文档、对应Jira版本和发布页为界,不把滚动Guide当作冻结的3.0规范。
三类故障不能合并成“主挂了”
flowchart TD
F[故障观察] --> Q{控制法定集合还在吗}
Q -->|否| QC[不能提交新控制状态]
Q -->|是| D{目标数据有恢复证据吗}
D -->|否| DM[新归属不能恢复字节]
D -->|是| E{旧执行者会被资源拒绝吗}
E -->|否| SE[仍可能重复外部效果]
E -->|是| OK[在已声明边界内继续恢复]
| 故障 | 控制面证据 | 数据面证据 | 可以下的结论 |
|---|---|---|---|
| controller/etcd/ZooKeeper丢多数 | 无新提交或协调进展 | 旧字节可能仍在 | 不能把字节存在写成服务可用 |
| broker/PV/HDFS副本丢失 | 控制记录可能仍完整 | 数据恢复门槛不足 | 元数据不能重造业务字节 |
| 旧broker/controller/RegionServer继续运行 | 新归属可能已提交 | 旧进程仍能发请求 | 需要协议或资源侧epoch/fencing |
| 从控制面快照恢复 | 恢复到某个元数据点 | 业务备份另算 | 还要处理缓存、revision与数据RPO |
安全性与活性要分别声明。控制日志的安全性依赖多数派交集、持久化和非拜占庭故障模型;业务数据耐久依赖ISR、HDFS副本、PV或其他存储自己的确认规则;外部单写者依赖写入点拒绝旧世代。活性还要求控制多数可通信、足够数据副本可用、执行者能取得新状态,以及恢复流程没有停在快照、watch或WAL回放中。
实验:元数据提交不能代替数据和撤权
本篇实验是一个产品中立的Python有限模型。它不模拟Kafka、Kubernetes或HBase协议,只把控制日志、数据副本、安装收据和资源侧epoch分成四个对象。
1 | |
Python 3.12.3实际运行的三项门槛均通过。三控制节点只剩一个时,epoch 2归属不能提交,d1和d2上的两份值仍存在;程序明确把“服务是否可用”留为未推断。第二个场景要求目标节点持有与epoch、内容摘要绑定的安装收据,拿到收据后才能切换;故意绕过门槛会成功提交指向空节点的元数据,随后读不到值。第三个场景里,未检查epoch的外部资源同时接受旧worker和新worker;资源高水位推进到epoch 2后,epoch 1写被拒绝,epoch 2写通过。
sequenceDiagram
participant C as control log
participant N as new worker e2
participant O as old worker e1
participant R as external resource
C->>N: commit owner=e2
C->>R: advance high-water to e2
O->>R: write with e1
R-->>O: reject stale epoch
N->>R: write with e2
R-->>N: accept
完整输出见观察结果,运行环境和来源见实验证据,验证边界见验证说明。模型没有网络、磁盘、崩溃恢复、并发控制器和真实组件,因此不能证明三个产品的可用性、耐久性或性能。
故障排查用证据四联表
故障报告若只写“主节点恢复”或“共识正常”,信息还不够。下面四行应分别取得证据。
| 看到的现象 | 先找的证据 | 常见误判 |
|---|---|---|
| 不能改配置或重调度 | 控制日志leader、quorum、commit/HW | 业务数据已经丢失 |
| 读取失败或副本重建停滞 | ISR、PV、WAL/HFile、复制进度 | 再选一次主就能恢复字节 |
| 两个执行者都在工作 | broker epoch、Lease、session、资源高水位 | 控制记录单值就等于物理单活 |
| 快照恢复后对象异常 | 快照点、revision、watch重建、业务RPO | 控制面快照是整套系统备份 |
这套模式适用于更多控制器架构:先问“谁决定”,再问“谁保存”,最后问“旧授权在哪里被拒绝”。三个答案落在不同组件时,监控、备份和演练也必须分别覆盖。
两个推演练习
Kafka的三个controller仍有多数派,是否足以证明一个RF=3分区可以继续确认写入?
不足。controller多数派只能推动元数据决策。分区能否确认写入还取决于partition leader、当前ISR、acks、min.insync.replicas和unclean election策略。反过来,broker日志仍在也不证明controller故障期间可以完成所有管理与故障转移。
恢复一份etcd快照后,Deployment和Pod对象都出现了,是否等于应用已经完整恢复?
不等于。还需处理revision回退和informer缓存,确认节点执行状态、镜像、网络、Secret加密密钥、PV内容与外部数据库。etcd快照的恢复点也决定快照之后哪些API更新已经丢失。
E08将进入网络与观测:eRPC、尾延迟和分布式追踪解决的是请求路径与测量问题,不能用控制面健康替代端到端时延证据。
参考资料
- Apache Kafka,KIP-500、KIP-595与Kafka 4.3 Replication Design。
- Apache Kafka,Kafka 4.3 KRaft Operations与4.3.1源码。
- Kubernetes,Components、Leases与API Concepts。
- Kubernetes,Operating etcd clusters与v1.37.1 leader election源码。
- etcd,v3.6 API Guarantees、Failures与Disaster Recovery。
- Apache HBase,Reference Guide、3.0.0 HBASE-18095设计与Downloads。
- Apache ZooKeeper,Programmer’s Guide 3.9.6。
- Chang等,Bigtable: A Distributed Storage System for Structured Data,OSDI 2006。
