2. 镜像拉取核心流程:kubelet → CRI → containerd → 镜像仓库
当用户在 Pod 中指定 image: nginx:1.25 时,K8s 节点上会触发一条完整的镜像拉取链路。理解这条链路的每个环节及其组件交互,是定位“镜像拉取失败”故障的基础。
2.1 完整链路总览
镜像拉取请求从 kubelet 发起,经过 CRI 接口、容器运行时(containerd),最终到达镜像仓库。整个流程可概括为以下 5 个阶段:
| 阶段 | 组件 | 核心动作 | 关键接口/协议 |
|---|---|---|---|
| ① 调度与触发 | kubelet | Pod 调度到本节点后,kubelet 根据 PodSpec 中的 image 字段发起拉取请求 | 内部 Pod 管理循环 |
| ② CRI 调用 | kubelet → CRI shim | kubelet 通过 CRI(Container Runtime Interface)gRPC 接口调用 PullImage() |
gRPC / CRI protobuf |
| ③ 运行时处理 | containerd | containerd 接收请求,解析镜像名称,检查本地缓存,决定是否拉取 | containerd 内部 API |
| ④ 仓库交互 | containerd → 镜像仓库 | 通过 HTTPS 与镜像仓库(Docker Hub / Harbor / 私有仓库)交互,拉取 manifest 和 layer | HTTPS / Docker Registry HTTP API V2 |
| ⑤ 存储与就绪 | containerd → kubelet | 拉取完成后,containerd 将镜像解压存储到本地,返回成功状态给 kubelet | CRI gRPC 响应 |
2.2 各组件交互详解
2.2.1 kubelet 侧:Pod 管理循环与镜像决策
kubelet 是节点上的“Pod 管家”,它通过内部的 syncLoop 持续检查 Pod 状态。当发现某个容器对应的镜像不存在时,kubelet 会执行以下步骤:
- 读取镜像拉取策略:检查 PodSpec 中的
imagePullPolicy(Always / IfNotPresent / Never)。 - 构造 CRI 请求:将镜像名称、认证信息(imagePullSecrets)封装为
PullImageRequest。 - 调用 CRI gRPC:通过 Unix Socket(默认
/run/containerd/containerd.sock)发送请求。
排查提示:如果 kubelet 日志中出现
Failed to pull image 但未显示具体原因,请先检查 kubelet 与 containerd 之间的 CRI socket 是否连通。使用 crictl info 验证 CRI 连接状态。
2.2.2 CRI 接口:标准化调用桥梁
CRI(Container Runtime Interface)是 K8s 与容器运行时之间的 gRPC 协议。镜像拉取相关的核心 RPC 定义如下:
// 来自 kubelet 的 CRI 接口定义(简化)
service RuntimeService {
// 拉取镜像
rpc PullImage(PullImageRequest) returns (PullImageResponse) {}
// 列出镜像
rpc ListImages(ListImagesRequest) returns (ListImagesResponse) {}
// 检查镜像是否存在
rpc ImageStatus(ImageStatusRequest) returns (ImageStatusResponse) {}
}
message PullImageRequest {
ImageSpec image = 1; // 镜像名称 + tag/digest
AuthConfig auth = 2; // 认证信息(来自 imagePullSecrets)
string sandbox_config = 3; // 沙箱配置(Pod 级别)
}
CRI 层的关键作用在于:解耦 kubelet 与具体运行时。无论底层是 containerd、CRI-O 还是其他运行时,kubelet 都通过同一套 gRPC 接口通信。
2.2.3 containerd 侧:镜像拉取的核心引擎
containerd 收到 PullImage 请求后,内部执行以下流程:
- 镜像名称解析:将
nginx:1.25解析为完整的仓库地址(默认追加docker.io/library/)。 - 本地缓存检查:查询本地
/var/lib/containerd/下的镜像存储,如果已存在且策略允许,直接返回。 - 认证准备:从请求中提取
AuthConfig,或读取 containerd 配置文件中的 registry 认证。 - 发起 HTTPS 请求:向镜像仓库发送 GET 请求,获取镜像 manifest(清单文件)。
- 并行拉取 layer:根据 manifest 中的 layer 列表,并发下载各层数据(支持断点续传)。
- 解压与存储:将下载的 layer 解压到本地 snapshotter 挂载点,更新元数据。
关键配置路径:containerd 的镜像拉取行为受
/etc/containerd/config.toml 控制,包括 registry 镜像加速、TLS 配置、最大并发拉取数等。
2.2.4 镜像仓库交互:HTTPS 与 Registry API
containerd 与镜像仓库之间的通信遵循 Docker Registry HTTP API V2 协议,核心交互步骤:
| 步骤 | HTTP 请求 | 说明 |
|---|---|---|
| 1. 认证(可选) | GET /v2/ → 401 → GET / |
如果仓库需要认证,先获取 Bearer |
| 2. 获取 Manifest | GET /v2/<name>/manifests/<reference> |
返回镜像的 manifest JSON(包含 layer 列表) |
| 3. 下载 Blob(Layer) | GET /v2/<name>/blobs/<digest> |
按 digest 下载每个 layer 的压缩包 |
常见故障点:如果镜像仓库使用自签名 TLS 证书,containerd 默认会拒绝连接。需要在
/etc/containerd/certs.d/ 下配置仓库的 CA 证书或跳过 TLS 验证(不推荐生产环境)。
2.3 链路故障排查速查表
当镜像拉取失败时,可以按以下链路顺序逐层排查:
| 排查层 | 检查命令/方法 | 典型错误 |
|---|---|---|
| kubelet 层 | journalctl -u kubelet -n 50 |
Failed to pull image "xxx": rpc error |
| CRI 层 | crictl pull nginx:1.25 |
connection refused(socket 不通) |
| containerd 层 | ctr image pull docker.io/library/nginx:1.25 |
not found / unauthorized |
| 仓库层 | curl -v https://registry-1.docker.io/v2/ |
SSL certificate problem / timeout |