bigywater.tech
Kubernetes · 98

etcd 里的 key:对象在 etcd 中的路径与编码

Kubernetes 的对象在 etcd 里到底长什么样?key 怎么命名?为什么 resourceVersion 是个全局递增的数字?label 一下算不算一次写?这篇直接用 etcdctl 看 key 的结构、value 的编码和 watch 事件,把 API 层和存储层对起来。

623 个 key
这个小集群 /registry/ 下的 key 总数,其中 events 占 244 个
k8s\x00
内置资源 value 的魔数,表示 protobuf 存储格式;CRD 对象则是 JSON
145750 = 145750
API 里的 resourceVersion 与 etcd 里 key 的 mod_revision 完全相同

key 的结构

实测 · 本机执行输出
  /registry/ 下共 623 个 key;按第一段(资源类型)统计的前 8 个:[('events', 244), ('clusterroles', 79), ('clusterrolebindings', 66), ('serviceaccounts', 57), ('configmaps', 25), ('apiregistration.k8s.io', 24), ('pods', 18), ('rolebindings', 14)]
  /registry/configmaps/k92/cm1                           存在
  /registry/namespaces/k92                               存在
  /registry/k92.example.com/widgets/k92/w1               存在
  /registry/clusterroles/view                            存在
  /registry/minions/kx-main-worker                       存在
  /registry/leases/kube-node-lease/kx-main-worker        存在
  /registry/deployments/kube-system/coredns              存在
  规律:/registry/<资源>/<命名空间>/<名字>(集群级资源没有命名空间段);CRD 的资源前面多一段 API 组;节点的路径是历史遗留的 minions;Service 分成 services/specs 和 services/endpoints
  services 下的前几个 key:['/registry/services/endpoints/default/kubernetes', '/registry/services/endpoints/kube-system/kube-dns', '/registry/services/endpoints/kube-system/metrics-server']

这个小集群的 /registry/ 下共 623 个 key,数量最多的是 events(244)、clusterroles(79)、clusterrolebindings(66)、serviceaccounts(57)、configmaps(25)。规律:ConfigMap 是 /registry/configmaps/k92/cm1(资源/命名空间/名字),集群级的 ClusterRole 是 /registry/clusterroles/view(没有命名空间段),CRD 对象是 /registry/k92.example.com/widgets/k92/w1(前面多一段 API 组),节点是历史遗留的 /registry/minions/<节点名>,Service 分成 services/specs 和 services/endpoints 两棵树。

value 的编码

实测 · 本机执行输出
  ConfigMap cm1:202 字节;开头 b'k8s\x00'(魔数 k8s\x00 = protobuf 存储格式);可读片段:['ConfigMap', '*$27fe4aa0-53e9-4849-83df-6540cb0db5dd2', 'kubectl-create', 'Update', 'FieldsV1:"', ' {"f:data":{".":{},"f:hello":{}}}B']
  Widget w1(CRD 对象):747 字节;开头 b'{"apiVersion":"k92.example.com';是 JSON:True;能 json.loads 吗:True
  同一个 ConfigMap 经 API Server 读出来是 JSON(317 字节),比 etcd 里的 protobuf(202 字节)大——存储格式比 API 格式紧凑

内置资源的 value 是 protobuf:ConfigMap cm1 共 202 字节,开头是魔数 k8s\x00,能读出 ConfigMap、kubectl-create、Update、FieldsV1(managedFields)等片段。CRD 对象 w1 是 747 字节的 JSON,以 {"apiVersion":"k92.example.com… 开头。同一个 ConfigMap 经 API Server 读出来的 JSON 是 317 字节,比 etcd 里的 protobuf 大——存储格式比 API 格式紧凑。

resourceVersion = mod_revision

实测 · 本机执行输出
  etcd 里 key 的 create_revision=145750;mod_revision=145750;version=1
  API 里 cm1 的 resourceVersion=145750(与 mod_revision 相同:True)
  patch 一次之后:mod_revision=145760(create_revision 仍是 145750);version=2;resourceVersion=145760
  新建另一个 cm2:resourceVersion=145761,比 cm1 的 145760 大(全局 revision 只增不减,所有对象共用一条时间线)

etcd 里 cm1 的 create_revision=145750、mod_revision=145750、version=1;API 里的 resourceVersion=145750,与 mod_revision 完全相同。patch 一次之后 mod_revision=145760(create_revision 不变)、version=2、resourceVersion=145760;再新建 cm2,resourceVersion=145761,比 cm1 的更大——revision 是整个 etcd 全局递增的,所有对象共用一条时间线(这也是 k6 里 resourceVersion 只能比较、不能当计数的原因)。

API 操作对应的 etcd 事件

实测 · 本机执行输出
  etcdctl watch /registry/configmaps/k92/ --prefix 收到的事件(类型, key 末段, mod_revision, version):
    ('PUT', 'cm3', 145766, 1)
    ('PUT', 'cm3', 145767, 2)
    ('DELETE', 'cm3', 145770, None)
  含义:create / label 都是 PUT(label 是一次新的写入,version 加 1);delete 是 DELETE 事件,这就是 API Server 的 watch 的数据来源

用 etcdctl watch /registry/configmaps/k92/ --prefix 同时在 API 层操作 cm3:create 对应 PUT(mod_revision 145766,version 1),label 也是一次 PUT(145767,version 2),delete 对应 DELETE(145770,没有 value)。API Server 的 watch 就是把 etcd 的 watch 事件转发出去。

分步演示

etcd 里的对象
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / kind(Docker 里的单节点或多节点真实集群,节点是 Docker Desktop 虚拟机里的容器)/ kubectl;用空的 kubeconfig 之外的独立 kubeconfig,不碰用户自己的集群;计时用 date +%s%N / kubectl 事件时间戳,数字每次运行会有小幅抖动。三节点 kind 集群 kx-main(Kubernetes v1.37.0)的单成员 etcd;通过 kubectl exec 进 etcd 静态 Pod 运行 etcdctl,value 用 -w json 读出后在宿主机上解 base64;key 数是这个时刻 /registry/ 下的总数,会随集群活动变化;watch 用另起的 kubectl exec 进程接收;每种情形各一次
    • 没有验证:etcd 内容在开启静态加密后的变化(见 k59);Event 的 TTL lease;自定义资源的多版本存储(storage version)与转换(见 k4);聚合 API 的数据存放位置;apiserver 的 --etcd-prefix 参数;用 auger 之类工具解码 protobuf value
    京ICP备2026067502号-1京公网安备11010502063487号