3. K8s资源对象与etcd映射:Pod、Service、ConfigMap等资源在etcd中的存储路径与数据结构
Kubernetes 将所有资源对象的状态持久化存储在 etcd 中。理解资源对象在 etcd 中的存储路径与数据结构,是进行一致性复核、故障排查和压测分析的基础。本章将深入剖析 Pod、Service、ConfigMap 等核心资源在 etcd 中的存储机制。
3.1 存储路径结构:/registry/ 前缀与资源分组
所有 K8s 资源对象均存储在 etcd 的 /registry/ 根路径下,遵循统一的层级结构:
/registry/<资源组>/<资源类型>/<命名空间>/<资源名称>
| 资源对象 | etcd 存储路径示例 | 说明 |
|---|---|---|
| Pod | /registry/pods/default/nginx-pod |
核心组 v1,无资源组前缀 |
| Service | /registry/services/specs/default/my-service |
specs 存储 Service 规格,endpoints 存储端点 |
| ConfigMap | /registry/configmaps/default/app-config |
核心组 v1 |
| Deployment | /registry/deployments/default/web-deploy |
apps/v1 组 |
| Secret | /registry/secrets/default/db-secret |
核心组 v1 |
| Node | /registry/minions/node-01 |
集群级别资源,无命名空间 |
关键路径差异: Service 资源在 etcd 中分为
specs 和 endpoints 两个子路径存储,分别保存 Service 的规格定义和实际端点列表。这种分离设计使得端点变更不会触发 Service 规格的版本更新。
3.2 数据结构:序列化与版本控制
etcd 中存储的 K8s 资源对象以 Protobuf 序列化 格式保存,并通过 etcd v3 的 key-value 模型 管理。每个资源对象包含以下核心字段:
- Key:资源路径,如
/registry/pods/default/nginx-pod - Value:Protobuf 编码的完整资源对象,包含 metadata、spec、status 等
- CreateRevision:该 key 创建时的 etcd 全局修订版本号
- ModRevision:该 key 最后一次修改时的修订版本号
- Version:K8s 资源对象的内部版本号(resourceVersion)
- Lease:关联的 etcd lease ID(用于 TTL 自动清理)
一致性复核关键字段:
ModRevision 和 resourceVersion 是判断资源对象是否发生变更的核心依据。在压测沙盘中,通过对比这两个字段可以快速检测资源状态是否与预期一致。
3.3 Pod 资源在 etcd 中的完整数据结构
以 Pod 为例,其 etcd 存储的 Protobuf 结构(简化后)如下:
{
"metadata": {
"name": "nginx-pod",
"namespace": "default",
"uid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"resourceVersion": "123456",
"creationTimestamp": "2025-01-15T10:30:00Z",
"labels": {"app": "nginx"}
},
"spec": {
"containers": [{
"name": "nginx",
"image": "nginx:1.25",
"ports": [{"containerPort": 80}]
}],
"nodeName": "node-01"
},
"status": {
"phase": "Running",
"podIP": "10.244.1.5",
"conditions": [
{"type": "Ready", "status": "True"}
]
}
}
注意: etcd 中存储的是完整的资源对象,包括
status 字段。这意味着 etcd 的写入频率不仅受用户操作影响,还受控制器(如 kubelet、controller-manager)的状态更新影响。在压测场景中,状态更新可能占 etcd 写入量的 60% 以上。
3.4 资源对象与 etcd 映射关系图
下图展示了 K8s 核心资源在 etcd 中的存储路径层级与数据流向:
3.5 资源对象版本号与 etcd 修订版本的关系
K8s 资源对象的 resourceVersion 与 etcd 的 ModRevision 存在直接映射关系:
| 概念 | 所属系统 | 作用范围 | 变更时机 | 一致性复核用途 |
|---|---|---|---|---|
| resourceVersion | Kubernetes | 单个资源对象 | 资源 spec 或 status 变更时递增 | 判断资源是否被更新 |
| ModRevision | etcd | 全局所有 key | 任何 key 创建/更新/删除时递增 | 判断 etcd 层面是否发生变更 |
| CreateRevision | etcd | 单个 key | key 创建时固定 | 判断资源是否被重建 |
压测沙盘一致性复核策略: 在压测过程中,通过定期抓取 etcd 的
ModRevision 和 K8s 资源的 resourceVersion,对比两者的变化趋势。如果 ModRevision 递增但 resourceVersion 未变化,说明 etcd 层面发生了非资源变更的操作(如 lease 续期),需要进一步排查。
3.6 Service 的特殊存储结构:specs 与 endpoints 分离
Service 资源在 etcd 中采用双路径存储设计,这是 K8s 中唯一一个将规格与状态分离存储的资源类型:
- /registry/services/specs/<namespace>/<service-name>:存储 Service 的规格定义(ClusterIP、端口映射、选择器等)
- /registry/services/endpoints/<namespace>/<service-name>:存储 Service 对应的端点列表(Pod IP + 端口)
这种分离设计带来的优势:
- 端点变更(Pod 扩缩容、重启)不会触发 Service 规格的版本更新
- 减少不必要的 etcd 写入和 watch 事件
- 端点控制器可以独立更新 endpoints 路径,不影响 Service 规格
一致性复核陷阱: 在压测沙盘中,如果只监听
/registry/services/specs/ 路径的变更,可能会遗漏端点变化。必须同时监听 /registry/services/endpoints/ 路径,才能完整捕获 Service 相关的所有 etcd 变更。
3.7 ConfigMap 与 Secret 的存储特点
ConfigMap 和 Secret 作为配置类资源,在 etcd 中具有以下存储特点:
| 特性 | ConfigMap | Secret |
|---|---|---|
| 存储路径 | /registry/configmaps/<ns>/<name> |
/registry/secrets/<ns>/<name> |
| 数据编码 | 明文 UTF-8 字符串 | Base64 编码(非加密) |
| 数据大小限制 | etcd 默认 1.5 MiB(可通过 --max-request-bytes 调整) |
同 ConfigMap |
| 变更频率 | 低(配置更新时触发) | 低(密钥轮换时触发) |
| watch 事件类型 | PUT(创建/更新)、DELETE | PUT(创建/更新)、DELETE |
压测建议: ConfigMap 和 Secret 的变更频率通常较低,但在大规模集群中(1000+ 节点),如果每个 Pod 都挂载 ConfigMap,etcd 的 watch 连接数会显著增加。建议在压测沙盘中模拟 ConfigMap 批量更新场景,测试 etcd 的 watch 性能瓶颈。
3.8 资源对象在 etcd 中的生命周期状态机
下图展示了 K8s 资源对象从创建到删除在 etcd 中的状态转换:
3.9 一致性复核实践:etcd 与 K8s 资源映射验证
在压测沙盘中,可以通过以下步骤验证 K8s 资源对象与 etcd 存储的一致性:
- 获取 K8s 资源列表:使用
kubectl get pods -o json获取所有 Pod 的resourceVersion - 获取 etcd 对应 key 列表:使用
etcdctl get /registry/pods/ --prefix --keys-only获取所有 Pod 的 etcd key - 对比数量:确保 K8s 资源数量与 etcd key 数量一致
- 对比版本号:随机抽取资源,对比
resourceVersion与 etcd 的ModRevision是否匹配 - 检查孤儿资源:查找 etcd 中存在但 K8s 中不存在的资源(或反之)
自动化复核脚本示例:
#!/bin/bash
# 一致性复核:对比 K8s Pod 与 etcd 存储
K8S_PODS=$(kubectl get pods --all-namespaces -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' | sort)
ETCD_PODS=$(etcdctl get /registry/pods/ --prefix --keys-only | sed 's|/registry/pods/||' | sort)
# 对比数量
echo "K8s Pods count: $(echo "$K8S_PODS" | wc -l)"
echo "etcd Pods count: $(echo "$ETCD_PODS" | wc -l)"
# 查找差异
diff <(echo "$K8S_PODS") <(echo "$ETCD_PODS") || echo "差异发现!"
压测沙盘最佳实践: 建议在压测前后各执行一次一致性复核,记录 etcd 的
ModRevision 变化量。如果压测期间 ModRevision 变化量远大于预期资源变更数,可能存在控制器频繁更新状态的问题,需要进一步排查。