4. 镜像拉取流程剖析:kubelet → CRI → containerd → registry 交互、镜像层与 manifest
镜像拉取是容器运行时最频繁的操作之一,其延迟直接影响 Pod 启动时间,进而影响 etcd 备份流程中依赖的 sidecar 容器就绪速度。理解从 kubelet 到 registry 的完整调用链,以及镜像层(Layer)与清单(Manifest)的数据结构,是优化 RPO 的基础。
4.1 端到端调用链:kubelet → CRI → containerd → registry
当 kubelet 决定在节点上启动一个 Pod 时,若所需镜像不存在于本地缓存,会触发以下四层调用:
- kubelet:通过 CRI(Container Runtime Interface)gRPC 接口调用
ImageService.PullImage()。 - CRI shim(containerd 内置):将 CRI 请求转换为 containerd 内部任务,调用
criService.PullImage()。 - containerd:核心守护进程,通过
images.Store检查本地缓存,若未命中则调用puller.Pull()。 - registry 交互:containerd 使用 distribution 客户端与远端 registry(如 Docker Hub、Harbor、ECR)进行 HTTP/HTTPS 通信,拉取 manifest 和 blob(镜像层)。
关键延迟点: 网络往返(registry 延迟)、层解压(decompress)、层验证(digest 校验)。在 etcd 备份场景中,若备份 sidecar 镜像较大,这些延迟会直接推高 RPO。
4.2 镜像层(Layer)与 Manifest 数据结构
容器镜像由一组只读层(Layer)和一个清单文件(Manifest)组成。每一层对应 Dockerfile 中的一条指令(如 RUN、COPY),层之间通过内容寻址(Content-Addressable Storage, CAS)关联。
4.2.1 镜像层(Layer)
- 定义:每个层是一个 tar 归档文件,包含文件系统的增量变更。
- 标识:通过 SHA256 digest 唯一标识,格式为
sha256:<64位十六进制>。 - 存储:registry 和 containerd 均以 blob 形式存储层数据,解压后挂载到联合文件系统(overlayfs)。
- 复用:多个镜像可共享相同层(如基础镜像层),减少拉取和存储开销。
RPO 优化启示: 若 etcd 备份 sidecar 镜像与集群中已有镜像共享基础层(如 alpine:3.18),则拉取时只需下载差异层,大幅缩短就绪时间。建议在构建备份镜像时固定基础镜像版本,最大化层复用率。
4.2.2 Manifest(清单)
Manifest 是一个 JSON 文档,描述镜像的元数据、层列表和配置。OCI 规范定义了两种格式:
| 属性 | 说明 | 示例值 |
|---|---|---|
schemaVersion |
清单格式版本,目前固定为 2 | 2 |
mediaType |
清单的媒体类型 | application/vnd.docker.distribution.manifest.v2+json |
config |
镜像配置对象的 digest 和大小 | {"mediaType":"...","size":7023,"digest":"sha256:..."} |
layers |
层列表,按从底到顶顺序排列 | [{"mediaType":"...","size":12345,"digest":"sha256:..."}, ...] |
以下是一个简化的 Manifest 示例:
{
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"config": {
"mediaType": "application/vnd.docker.container.image.v1+json",
"size": 7023,
"digest": "sha256:a1b2c3d4e5f6..."
},
"layers": [
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 2812345,
"digest": "sha256:layer1_digest..."
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 567890,
"digest": "sha256:layer2_digest..."
}
]
}
4.3 拉取流程的并行与串行阶段
containerd 在拉取镜像时,并非所有步骤都是串行的。理解并行度有助于设计 RPO 优化策略:
- 串行阶段:获取 Manifest(必须首先完成,以获取层列表和 digest)。
- 并行阶段:层下载(blob fetch)可并发进行,containerd 默认使用 3 个并发 goroutine。
- 串行阶段:层解压和挂载(需按顺序,因为 overlayfs 依赖层叠顺序)。
注意: 若 registry 对并发请求有限制(如 Docker Hub 的匿名拉取限速),并行下载反而可能触发限速,导致整体拉取时间增加。生产环境建议使用镜像缓存(如 Harbor 代理缓存)或 P2P 分发(如 Dragonfly)。
4.4 拉取流程中的 RPO 关键指标
在 etcd 备份场景中,以下指标直接影响 RPO:
| 指标 | 定义 | 对 RPO 的影响 |
|---|---|---|
| 镜像大小 | 所有层压缩后总字节数 | 越大则拉取时间越长,RPO 窗口扩大 |
| 层复用率 | 本地已缓存层占总层数的比例 | 复用率越高,需下载的层越少,RPO 越低 |
| registry 延迟 | 从发起请求到收到第一个字节的时间 | 高延迟会显著增加拉取总时间 |
| 并发下载数 | containerd 同时下载的层数(默认 3) | 适当增加可加速,但需注意 registry 限速 |
| 解压时间 | 层下载完成后解压到文件系统的时间 | CPU 密集型,大层解压可能成为瓶颈 |
优化建议: 对于 etcd 备份 sidecar 镜像,建议将镜像大小控制在 50MB 以内,使用多阶段构建(multi-stage build)仅保留必要二进制文件,并固定基础镜像版本以提升层复用率。若集群规模较大,可部署镜像缓存节点(如 Harbor 代理)或使用 P2P 分发工具(如 Dragonfly)来降低 registry 延迟和并发限速影响。