土法炼钢 · 系统与基础设施

【etcd】Kubernetes 控制面耦合:apiserver、resourceVersion 与 Node Lease

文章导航

分类入口
distributedkubernetes
标签入口
#etcd#kubernetes#apiserver#resourceversion#node-lease#watch#mvcc#control-plane#v3.5.33

目录

kube-apiserver 返回 504 Gateway Timeout 时,值班笔记里常见两种归因:「etcd 挂了」和「apiserver 负载高」。两者都可能对,但修复动作完全不同——前者要查 Leader、WAL fsync、quota;后者可能是 watch cache 未命中、准入链路过长,或 controller 从过旧 resourceVersion 追赶触发 List 风暴。若把 apiserver 当成 etcd 的透明代理,会把 Watch 背压误判成 Raft 选举问题。

前 12 篇已把 etcd 服务器内核拆到五轴精度(Raft 单组WALMVCCWatchLeaseCompaction/quota)。本文只钉 Kubernetes 控制面在链路上的落点:谁直接连 etcd、resourceVersion 与 Revision 的对应关系、Node Lease 如何占用 Lease 轴,以及 apiserver 分层缓存如何改变 etcd 负载形态。

本篇在系列中的位置

篇目 核心内容
第 12 篇 · Compaction 与容量 auto-compaction、defrag、quota
第 13 篇 · K8s 控制面耦合 apiserver、resourceVersion、Node Lease
第 14 篇 · 运维与升级 member change、backup/restore、升级门
系列目录 全部篇目

版本锚定:etcd v3.5.33(源码 tag v3.5.33)。Kubernetes 控制面常见嵌入 3.5.x;apiserver 存储接口行为以你集群的 K8s 小版本为准,本文机制不超前写未钉 tag 的 etcd 能力。Watch/MVCC 百科速览见 distributed/50;apiserver 实现全书不在此展开。


一、分层:谁直接碰 etcd

Kubernetes 集群状态的唯一持久化后端是 etcd(原生栈;Kine 等兼容层见 第 16 篇 边界一句)。但几乎没有任何业务组件直接连 etcd——scheduler、controller-manager、kubelet、kubectl 都走 kube-apiserver 的 REST API。

flowchart LR
  kubectl["kubectl / controllers / kubelet"]
  apiserver["kube-apiserver"]
  wcache["apiserver watch cache"]
  etcd["etcd cluster"]
  kubectl --> apiserver
  apiserver --> wcache
  wcache -->|"ListAndWatch"| etcd
  apiserver -->|"direct read/write on miss"| etcd
层级 组件 与 etcd 的关系
客户端 controller、operator、kubelet 经 apiserver;不持有 etcd 证书
聚合层 kube-apiserver 唯一直连 etcd 的控制面组件;认证/授权/准入在此
缓存层 apiserver watch cache(按资源类型) 合并对 etcd 的 Watch;List 可走缓存
持久层 etcd v3 扁平 KV;不理解 Pod/Deployment 语义

对 etcd 而言,apiserver 写入的是 protobuf 序列化后的 opaque bytes,key 形如 /registry/pods/default/nginxKubernetes 文档 · Configure and upgrade etcd,A 级)。排障时要把「对象语义」和「KV 字节」分开:etcd 轴看 Revision、quota、Watch;apiserver 轴看 List 分页、watch cache 一致性、准入超时。

工程间隙:apiserver 默认会对 etcd 读做 Serializable 一致性(走本地缓存或 follower 转发),对写与部分读走 线性一致路径。K8s 文档与 etcd 文档对「读是否最新」的表述层级不同——故障时应分别查 apiserver 日志与 etcdctl endpoint status,不要混成一条「etcd 慢」。


二、resourceVersion:从 API 字段到 Revision

metadata.resourceVersion 是 Kubernetes API 的乐观并发控制Watch 起点的核心字段。在 etcd v3 栈上,它对应对象在 etcd 中的 mod revision(该 key 最后一次变更时的全局 Revision),而不是 create revision。

etcd 的 Revision 是全局单调递增的 64 位整数(main + sub 用于同一事务内排序,见 第 4 篇)。每次成功的 Put/Txn 提交都会产生新 Revision。apiserver 在解码 etcd 中的对象后,把 mod revision 写入 resourceVersion 字符串返回给客户端。

概念 Kubernetes API etcd v3
全局变更序 间接:各对象 resourceVersion 可比较同 key 新旧 全局 Revision
Watch 起点 ?watch=true&resourceVersion=<rv> Watch 的 start_revision
冲突检测 UpdateresourceVersion;不匹配则 409 Txn compare on mod revision
全量同步 resourceVersion=""0 的 List Range + 当前 rev

List-Watch 控制循环(声明式 API 的基础)在 etcd 侧依赖两件事(distributed/50 §一):

  1. Watch 持久、有序——同一 stream 上事件按 Revision 递增,不丢不重(在 compaction 窗口内)。
  2. ErrCompacted 时必须重 List——若 controller 保存的 resourceVersion 已被 auto-compaction 清掉,Watch 无法从历史 rev 追赶,只能全量 List 重建本地缓存。

因此大集群里 compaction 保留窗口controller 断连时长 共同决定 catch-up 成本——这是 Watch 轴问题,不是 Raft 选举问题。apiserver watch cache 会缓冲 etcd 事件,但 cache 本身仍从 etcd ListAndWatch 种子化;etcd compaction 过 aggressive 时,cache 与 client 都会收到 ErrCompacted 语义。

常见误区:把 resourceVersion 当成「集群全局逻辑时钟」去比较不同对象的新旧——跨对象比较 resourceVersion 大小没有线性一致语义;只有同一资源类型、同一对象的 Update 冲突检测才有意义。


三、apiserver watch cache 与 etcd 负载

若每个 controller 都直接 Watch etcd,大型集群会出现 Watch 连接数 × 资源类型 的放大。apiserver 的 watch cache(按 GroupVersionResource 分片的存储缓存)设计意图是:

flowchart TD
  c1["controller A"]
  c2["controller B"]
  inf["SharedInformer local store"]
  wc["apiserver watch cache"]
  etcd["etcd"]
  c1 --> inf
  c2 --> inf
  inf -->|"ListAndWatch"| wc
  wc --> etcd

对 etcd 五轴的含义

现象 更可能轴 说明
apiserver CPU 高、etcd QPS 正常 apiserver / cache 准入、序列化、cache 索引
etcd etcd_server_proposals_failed_total Raft Leader 或磁盘 fsync
大量 Watch 断开重连 Watch ErrCompacted 或网络;触发 List 风暴
mvcc: database space exceeded MVCC / quota 与 apiserver 超时不一定同时

apiserver 存储接口实现(storage.Interface、cacher 层)不在本系列重写——排障时若 etcd 指标健康而 apiserver LIST 超时,应查 apiserver 的 etcd_request_duration_seconds 与 watch cache 是否 stale,而不是先 defrag。

Events 分片:Kubernetes 1.22+ 支持 --etcd-servers-overrides,将 /events 前缀路由到独立 etcd 集群(K8s 文档)。Events 写入量远大于 Pod/Deployment;分片后 主 etcd 的 MVCC/quota 轴与 events etcd 分列运维——第 14 篇 backup 应对每个后端分别 snapshot。


四、Node Lease:Lease 轴上的 K8s 心跳

Kubernetes 1.14+ 起,节点心跳默认通过 coordination.k8s.io/v1 Lease 实现,替代频繁更新 Node.status(减少 etcd 写入放大)。kubelet 周期性 renew Lease;Lease 过期后节点被标记为 NotReady。

在 etcd 层,每个 Node Lease 对应:

环节 组件 etcd 机制
创建/续约 kubelet → apiserver → etcd Lease grant / KeepAlive
TTL 到期 etcd lessor key 删除 → Watch 事件
控制面反应 node-controller Watch Lease/Node Watch 轴

Leader 切换与 TTL:KeepAlive 不经 Raft 提交(第 10 篇);Leader 故障时新 Leader 需加载 Lease 状态。etcd 3.4+ 的 Lease checkpoint(v3.5.33 已稳定)缓解 Leader 切换后 TTL 重置——K8s 生产栈应确认 checkpoint 已启用(集群级 etcd 参数,非 kubelet 配置)。

规模压力\(N\) 个节点 ≈ \(N\) 个持续续约的 Lease,加上 Pod/Endpoint 等对象的 Watch churn。5000 节点集群的 Lease 续约是 写入 QPS 的底噪——这不是「调 kubelet 心跳间隔」 alone 能消掉的,quota 与 auto-compaction 必须按 第 12 篇 预留。

与 Jepsen 边界:基于 Lease 的分布式锁在特定故障序下不安全(distributed/53);K8s Node Lease 语义由控制面定义,不等同于通用 etcd Lock API。排障 NotReady 漂移时先查 Lease 轴(etcd Lease 是否存在、TTL)与 网络,不要把 Node 问题默认等同 Raft 无 Leader。


五、症状分列:apiserver 超时 vs etcd 轴

值班口令:先判 apiserver 是否穿透 etcd,再点名 etcd 五轴(完整表见 第 15 篇)。

入口症状 先查 若异常则轴
kubectl get apiserver 日志、etcd 延迟指标 MVCC 读 / Raft ReadIndex
kubectl write 409 预期冲突 正常 optimistic concurrency
controller 不再 reconcile controller --v 日志、Watch 断开 Watch → ErrCompacted
全集群 NotReady Node Lease、etcd Leader Lease + Raft
apiserver 504 etcd_request_duration_seconds 分列 etcd vs apiserver
仅 Events 异常 events etcd 端点 分集群 MVCC
flowchart TD
  slow["apiserver slow or 504"]
  slow --> pierce{"etcd request slow\nin apiserver metrics?"}
  pierce -->|"No"| api["apiserver admission cache auth"]
  pierce -->|"Yes"| axis["Name etcd axis"]
  axis --> r["Raft leader WAL"]
  axis --> m["MVCC quota compaction"]
  axis --> w["Watch ErrCompacted"]
  axis --> l["Lease Node heartbeat"]

restore 与 resourceVersion 回退:从 etcd snapshot 恢复若未做 revision bump,controller 本地 resourceVersion 可能高于 etcd 当前 Revision,导致 Watch 行为异常。Kubernetes 语境下 restore 应使用 etcdutl snapshot restore--bump-revision--mark-compactedetcd v3.5 Disaster recovery,A 级)——详见 第 14 篇


六、本篇边界

题目 归属 本篇
etcd Revision / treeIndex / bbolt 第 4–7 篇 只引用
Watch synced/unsynced / victims 第 9 篇 只引用
apiserver 存储源码 / cacher 实现 K8s 源码 指针,不重写
scheduler 调度算法 K8s 组件 不写
Helm 安装 etcd 运维菜谱 不写
跨方案 QPS 对比 禁止

参考资料

规范 / 官方文档(A)

源码(A)

站内

论文 / 报告(B)

实验台账


上一篇Compaction、Defrag 与容量

下一篇运维与升级

读完这篇,下一步读什么

优先读同系列或同问题的下一篇,把单篇消费变成主题集群。


By .