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 响应
镜像拉取核心链路:kubelet → CRI → containerd → 镜像仓库 kubelet Pod 管理循环 CRI gRPC PullImage() containerd 镜像管理 镜像仓库 Docker Hub / Harbor gRPC containerd 内部 HTTPS ① 调度与触发 Pod 调度到节点 kubelet 读取 image 字段 ② CRI 调用 kubelet 调用 PullImage() gRPC 请求到 containerd ③ 运行时处理 解析镜像名称 检查本地缓存 ④ 仓库交互 拉取 manifest 拉取 layer ⑤ 存储与就绪 解压存储 → 返回成功 返回状态

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 请求后,内部执行以下流程:

  1. 镜像名称解析:将 nginx:1.25 解析为完整的仓库地址(默认追加 docker.io/library/)。
  2. 本地缓存检查:查询本地 /var/lib/containerd/ 下的镜像存储,如果已存在且策略允许,直接返回。
  3. 认证准备:从请求中提取 AuthConfig,或读取 containerd 配置文件中的 registry 认证。
  4. 发起 HTTPS 请求:向镜像仓库发送 GET 请求,获取镜像 manifest(清单文件)。
  5. 并行拉取 layer:根据 manifest 中的 layer 列表,并发下载各层数据(支持断点续传)。
  6. 解压与存储:将下载的 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