3. 镜像拉取流程详解:从 kubelet 到容器运行时,再到镜像仓库的完整拉取链路

当 Deployment 的 Pod 调度到某节点后,kubelet 负责执行实际的镜像拉取操作。理解这条从 kubelet → 容器运行时 → 镜像仓库的完整链路,是定位拉取超时故障的基础。任何一个环节的延迟或失败,都会表现为 Pod 状态 ImagePullBackOff 或 ErrImagePull。

3.1 整体链路架构

镜像拉取并非由 kubelet 直接发起 HTTP 请求,而是通过容器运行时接口(CRI)委托给容器运行时(如 containerd、CRI-O、Docker Engine)。运行时再根据镜像名称,依次查询本地缓存、配置的镜像仓库(Registry),最终通过 HTTPS 下载镜像层。

关键角色: kubelet(调度与状态管理)→ CRI shim(gRPC 协议转换)→ 容器运行时(镜像层管理)→ 镜像仓库(存储与分发)。
镜像拉取完整链路流程图 镜像拉取完整链路流程图 kubelet Pod 状态管理 CRI gRPC PullImage 接口 容器运行时 containerd/CRI-O 镜像仓库 Registry 链路关键说明: 1. kubelet 通过 CRI 的 PullImage gRPC 调用,将镜像名称和凭证传给运行时。 2. 运行时先检查本地缓存(已下载的镜像层),若不存在则发起 HTTPS 请求。 3. 镜像仓库返回 manifest 和层数据,运行时解压并存储到本地文件系统。

3.2 逐层调用细节

3.2.1 kubelet 侧:SyncPod 与镜像拉取触发

kubelet 的核心控制循环 SyncLoop 在检测到 Pod 需要更新时,调用 SyncPod 方法。该方法内部按顺序执行:挂载卷 → 拉取镜像 → 创建容器 → 启动容器。镜像拉取由 runtimeService.PullImage() 触发,这是一个 gRPC 调用,定义在 kubelet/apis/cri/services.go 中。

步骤 kubelet 组件 动作 超时控制
1 SyncPod 计算期望状态,生成 PodSandbox 配置 Pod 级别启动超时(默认 5m)
2 imageManager.EnsureImageExists 检查本地是否已有镜像,若无则调用 PullImage 单次拉取超时(默认 1m)
3 CRI RemoteRuntimeService.PullImage 构造 PullImageRequest,通过 gRPC 发送 gRPC 连接超时(默认 30s)
排查提示: kubelet 日志中搜索 SyncPod 或 EnsureImageExists 可定位到具体拉取请求。若日志显示 context deadline exceeded,说明 gRPC 调用超时,问题可能在运行时或网络层。

3.2.2 CRI 层:gRPC 协议与 PullImage 接口

CRI(Container Runtime Interface)是 kubelet 与容器运行时之间的标准 gRPC 接口。核心的 PullImage 接口定义如下:

// PullImageRequest 包含镜像名称、凭证、沙箱配置
message PullImageRequest {
  // 镜像的完整引用,如 docker.io/library/nginx:1.21
  string image = 1;
  // 镜像仓库认证信息
  AuthConfig auth = 2;
  // PodSandbox 配置(用于镜像拉取策略)
  PodSandboxConfig sandbox_config = 3;
}

// PullImageResponse 返回镜像的唯一标识
message PullImageResponse {
  string image_ref = 1;  // 如 sha256:abc123...
}

kubelet 将 imagePullSecrets 中的凭证转换为 AuthConfig,随请求发送。运行时收到请求后,解析镜像名称中的 registry 地址、仓库名和标签。

3.2.3 容器运行时侧:镜像解析与层下载

以 containerd 为例,其内部处理流程如下:

  1. 镜像名称解析: 将 nginx:1.21 补全为 docker.io/library/nginx:1.21,提取 registry 地址。
  2. 本地缓存检查: 查询 /var/lib/containerd/io.containerd.content.v1.content 中的 blob 索引,若所有层都存在则直接返回。
  3. manifest 拉取: 向 registry 的 /v2/<name>/manifests/<tag> 发起 GET 请求,获取镜像 manifest(描述层列表和配置)。
  4. 层下载: 根据 manifest 中的层 digest,并发下载每个 blob(/v2/<name>/blobs/<digest>),支持断点续传。
  5. 解压与挂载: 下载完成后解压层数据,写入 snapshotter 挂载点,生成容器根文件系统。
容器运行时内部镜像拉取流程 容器运行时内部镜像拉取流程(containerd 示例) CRI PullImage 名称解析 本地缓存检查 拉取 Manifest 并发下载层 解压与挂载 说明:本地缓存检查命中时,直接返回镜像引用,跳过 manifest 和层下载。超时通常发生在 manifest 拉取或层下载阶段。

3.3 镜像仓库交互协议

容器运行时与镜像仓库之间使用 Docker Registry HTTP API V2 进行通信,默认通过 HTTPS。核心交互流程如下:

请求类型 API 端点 用途 常见超时原因
认证 /v2/ 或 /v2/auth 获取 Bearer (若需认证) 认证服务响应慢、凭证错误
Manifest 拉取 /v2/<name>/manifests/<reference> 获取镜像 manifest(JSON 格式) 仓库负载高、manifest 体积大
Blob 拉取 /v2/<name>/blobs/<digest> 下载镜像层数据(tar.gz) 带宽不足、层体积大、连接不稳定
健康检查 /v2/_catalog 或 / 探测仓库是否可达 DNS 解析失败、网络不通
常见陷阱: 许多拉取超时并非真正超时,而是认证失败后重试导致的累积延迟。检查 imagePullSecrets 是否过期、仓库地址是否正确(如是否遗漏了 library/ 前缀)。

3.4 超时故障点总结

基于上述链路,超时故障可归纳为以下三类,对应不同的排查方向:

  • kubelet → CRI 超时: 通常表现为 context deadline exceeded,原因可能是运行时 hang 住、磁盘 I/O 瓶颈、或 containerd 服务异常。排查方向:检查 crictl info、systemctl status containerd。
  • 运行时 → 仓库网络超时: 表现为 dial tcp: i/o timeout 或 TLS handshake timeout。排查方向:节点 DNS 配置、代理设置、防火墙规则、仓库 CDN 可用性。
  • 层下载缓慢: 表现为 downloading layer ... 0 B/100 MB 长时间无进度。排查方向:节点带宽、并发拉取数限制(max_concurrent_downloads)、镜像层大小。
最佳实践: 在节点上手动执行 crictl pull nginx:1.21 可以绕过 kubelet,直接测试运行时到仓库的链路。若手动拉取成功,问题大概率在 kubelet 侧或 Pod 配置;若失败,则聚焦网络或仓库配置。