【kube-apiserver】storage.Interface 与 etcd3:codec、prefix 与 CRUD 路径
内容提要
kube-apiserver通过storage.Interface与etcd交互,存储protobuf序列化数据,key由前缀拼接,value经Transformer加密。GuaranteedUpdate用etcd Txn实现乐观并发写,Create/Delete/Watch各有路径。排障需关注etcd延迟、转换失败等指标,区分apiserver与etcd问题。
延伸解读
排障时先分清是 apiserver 还是 etcd 的问题
文章指出,etcd 健康但 apiserver 报错的情况并不少见,例如加密配置变更后旧密文无法解密,表现为读取对象 500 错误,而 etcd 指标正常。此时应优先查看 apiserver 的 storage_transformation_failures_total 指标,而不是急于排查 etcd。同样,etcd_request_duration_seconds 高并不等于 etcd 集群本身有问题,可能是网络延迟或 apiserver 侧加密开销导致。区分这两层是定位故障的第一步。
GuaranteedUpdate 的乐观并发与重试代价
GuaranteedUpdate 通过 etcd 的 Txn 比较 mod_revision 实现原子更新,避免竞争窗口。但高并发下,多个 controller 同时更新同一对象时,内部重试会放大 etcd 读写次数,这是乐观并发的固有代价。文章建议使用 server-side apply 减少冲突概率,但并不能完全消除。理解这一点有助于解释为什么某些场景下 etcd 负载异常升高。
Watch 路径中的 410 Gone 可能来自 etcd 侧
当 Watch 请求的 startRevision 已被 etcd 压缩(compact)时,etcd 会返回 ErrCompacted,导致 apiserver 的 Watch 关闭并向上传播错误。这解释了 410 Gone 的一个来源,另一个来源是 cacher 侧。排障时若遇到 410,需检查 etcd 的 compaction 策略与 apiserver 的 resourceVersion 映射是否合理。
Q&A
kube-apiserver 在 etcd 中存储的对象数据是什么格式?
kube-apiserver 在 etcd 中存储的是 protobuf 序列化后的字节流,而不是 JSON。key 由 pathPrefix 和 resourcePrefix 拼接而成,value 会经过 value.Transformer 处理(可能是加密或压缩)。
storage.Interface 在 kube-apiserver 中扮演什么角色?
storage.Interface 定义了 kube-apiserver 与后端存储的完整合约,包括 Create、Delete、Get、GetList、GuaranteedUpdate、Watch、Count 等方法。它使得 apiserver 在理论上可以替换后端,但在生产环境中唯一的正式实现是 etcd3/store。
kube-apiserver 如何实现乐观并发控制?
kube-apiserver 通过 GuaranteedUpdate 实现乐观并发控制,它使用 etcd3 的 Txn(compare-and-swap)机制。具体流程是:先读取当前值,调用 tryUpdate 生成新对象,然后构造 Txn,比较 mod_revision 是否匹配,匹配则执行 Put,否则重试。
value.Transformer 在存储路径中处于什么位置?
value.Transformer 位于 etcd 字节和 codec 字节之间。写入时,codec 编码后的明文经过 TransformToStorage 转换为 etcd 存储的字节(可能加密);读取时,etcd 字节经过 TransformFromStorage 还原为明文 codec 字节。默认是 identity,启用 encryption at rest 后为 AES-GCM 或 KMS 加密。
Watch 如何从 etcd3 gRPC stream 映射到 apiserver 事件?
Watch 调用 etcd3 gRPC Watch.Watch,携带 startRevision,接收 WatchResponse,每个 Event 包含 Type 和 Kv。然后对 Kv.Value 调用 value.Transformer.TransformFromStorage 解密,再用 codec 解码为 Go 对象,生成 watch.Event 推入 channel。
如何区分 apiserver 慢和 etcd 慢?
可以通过 etcd_request_duration_seconds 指标来区分。如果该指标高,但 etcd 侧 etcd_server_proposals_failed_total 正常,则问题可能出在网络延迟或 etcd 节点负载;如果 storage_transformation_failures_total 非零,则优先检查 encryption at rest 配置。
etcd 中存储的 key 结构是怎样的?
etcd 中存储的 key 由 pathPrefix、resourcePrefix、namespace 和 name 拼接而成,例如 /registry/pods/default/nginx。pathPrefix 默认是 /registry,由 --etcd-prefix 控制;resourcePrefix 是资源类型前缀,如 pods、deployments。
GuaranteedUpdate 在什么情况下会返回 409 Conflict?
当 preconditions.ResourceVersion 不匹配(客户端传了旧 rv)时,GuaranteedUpdate 在检查 preconditions 阶段直接返回 Conflict,不会进入 Txn 重试。如果没有 rv 约束,Txn 失败会触发内部重试,最终成功或返回 500。