2. Kubernetes 镜像拉取基础:kubelet 镜像拉取流程、镜像层与 manifest 结构
2.1 镜像拉取的核心参与者
在 Kubernetes 集群中,镜像拉取并非由 kube-apiserver 或 scheduler 直接执行,而是由每个节点上的 kubelet 组件负责。kubelet 通过 CRI(Container Runtime Interface)与容器运行时(如 containerd、CRI-O)交互,最终由运行时完成镜像层的下载与解包。
从容量规划角度看,理解这一流程有助于定位拉取瓶颈:是 kubelet 的并发控制不足,还是运行时下载带宽受限,或是镜像仓库的限流策略导致。
容量规划要点: 镜像拉取是节点启动 Pod 时最耗时的操作之一。大规模集群中,若多个 Pod 同时调度到同一节点,kubelet 的串行拉取队列可能成为性能瓶颈。
2.2 kubelet 镜像拉取流程
kubelet 内部维护一个 imageManager,负责管理镜像拉取请求。其核心流程如下:
- Pod 调度到节点:kubelet 从 apiserver 监听到 Pod 绑定事件,开始创建 Pod 沙箱。
- 解析容器镜像:kubelet 读取 Pod Spec 中的
image字段,提取镜像名称与标签(如nginx:1.25)。 - 检查本地缓存:通过 CRI 调用
ImageStatus检查镜像是否已存在于节点本地存储。 - 拉取决策:若本地不存在,或
imagePullPolicy为Always,则触发拉取。 - 加入拉取队列:kubelet 的 imageManager 将请求加入一个 串行队列(默认串行拉取,可通过
--serialize-image-pulls调整)。 - CRI 调用 PullImage:kubelet 调用容器运行时的
PullImagegRPC 接口,传递镜像引用与认证信息。 - 运行时下载镜像层:容器运行时根据 manifest 获取镜像层列表,逐层下载并解包到本地存储。
- 返回结果:拉取完成后,kubelet 更新 Pod 状态,继续后续的容器创建流程。
并发控制关键参数: kubelet 的
--serialize-image-pulls 默认为 true(串行拉取)。若设置为 false,可并行拉取多个镜像,但会增加磁盘 I/O 与网络带宽压力。容量规划时需根据节点规格与镜像大小权衡。
2.3 镜像层与 manifest 结构
容器镜像由多个只读层(Layer)叠加而成,每一层对应 Dockerfile 中的一条指令。镜像的元数据由 manifest(清单)描述,它是镜像仓库中最重要的索引文件。
2.3.1 镜像层结构
每个镜像层是一个压缩的 tar 包,包含文件系统的增量变更。层与层之间通过 内容寻址(Content-Addressable)方式标识,使用 SHA256 摘要作为唯一 ID。
| 层类型 | 说明 | 示例 |
|---|---|---|
| 基础层 | 来自基础镜像(如 ubuntu:22.04),包含操作系统文件 | sha256:abc123... |
| 中间层 | 由 RUN、COPY、ADD 等指令生成 | sha256:def456... |
| 可写层 | 容器运行时创建的临时层,容器删除后消失 | (非镜像层) |
容量规划陷阱: 镜像层数过多(超过 50 层)会导致 manifest 解析变慢,且拉取时需逐层校验摘要。建议将 RUN 指令合并,减少层数。
2.3.2 Manifest 结构
Manifest 是镜像的“目录文件”,包含以下关键字段:
- schemaVersion:清单格式版本,目前主流为 2。
- mediaType:媒体类型,如
application/vnd.docker.distribution.manifest.v2+json。 - config:指向镜像配置文件的引用,包含环境变量、入口点、默认命令等。
- layers:镜像层列表,每个元素包含
digest(摘要)、size(压缩后大小)、mediaType。
以下是一个简化版的 manifest 示例:
{
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"config": {
"mediaType": "application/vnd.docker.container.image.v1+json",
"size": 7023,
"digest": "sha256:config_digest_here"
},
"layers": [
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 28000000,
"digest": "sha256:layer1_digest"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 1500000,
"digest": "sha256:layer2_digest"
}
]
}
2.4 镜像拉取策略与容量影响
Kubernetes 支持三种 imagePullPolicy,直接影响拉取频率与节点资源消耗:
| 策略 | 行为 | 容量规划建议 |
|---|---|---|
Always |
每次启动 Pod 都拉取镜像 | 适用于开发环境;生产环境应避免,除非镜像标签为 :latest |
IfNotPresent |
仅当本地不存在时拉取 | 生产环境推荐,减少不必要的网络与磁盘开销 |
Never |
仅使用本地镜像,不存在则报错 | 适用于预加载镜像的离线环境 |
最佳实践: 生产环境使用
IfNotPresent 并配合 imagePullSecrets 认证。对于频繁更新的应用,使用语义化版本标签(如 v1.2.3)而非 :latest,避免触发不必要的 Always 拉取。
2.5 镜像拉取性能关键指标
从容量规划角度,以下指标需要持续监控:
- 拉取延迟(Pull Latency):从发起拉取到镜像就绪的时间,受镜像大小、层数、带宽影响。
- 拉取并发数(Concurrent Pulls):kubelet 同时处理的拉取请求数量,默认串行。
- 镜像缓存命中率(Cache Hit Rate):本地已有镜像的比例,越高则拉取越少。
- 仓库限流(Rate Limiting):镜像仓库(如 Docker Hub)对匿名或免费用户的拉取次数限制。
容量规划公式: 节点启动 Pod 的总时间 ≈ 镜像拉取时间 + 容器创建时间 + 应用启动时间。其中镜像拉取时间 = 镜像大小 / 有效带宽 × 层数因子(因逐层校验增加约 10%~20% 开销)。