2. 镜像拉取原理:Docker/containerd 镜像拉取流程、分层下载机制、registry 交互过程

镜像拉取是容器运行时的核心操作,理解其底层原理对于优化灾备场景下的拉取速度、减少带宽消耗至关重要。本章深入剖析 Docker 与 containerd 的拉取流程、分层下载机制以及与 Registry 的交互细节。

2.1 镜像拉取整体流程

无论是 Docker Engine 还是 containerd,镜像拉取都遵循“客户端 → Registry → 存储驱动”的通用路径。核心差异在于运行时架构与镜像存储管理方式。

核心流程(Docker / containerd 通用):
1. 客户端发起 pull 请求(指定镜像名称与标签)
2. 解析镜像名称,获取 Registry 地址与仓库路径
3. 与 Registry 建立 HTTPS 连接,进行身份认证(如需)
4. 获取镜像 Manifest(清单),解析层列表与配置
5. 并发下载各层 Blob(分层数据)
6. 验证层摘要(Digest)与解压
7. 将层挂载到本地存储驱动,构建可读写容器层
镜像拉取整体流程(Docker / containerd) 客户端(Docker/nerdctl) 镜像名称解析(Registry + 仓库路径) Registry 认证( / Basic Auth) 获取 Manifest(清单 + 层列表) 并发下载各层 Blob(分层下载) 存储驱动挂载(overlay2 / devicemapper)

2.2 分层下载机制详解

Docker 镜像由多个只读层(Layer)叠加而成,每个层对应 Dockerfile 中的一条指令。分层下载的核心优势在于:层可以被复用和缓存,本地已存在的层无需重复下载。

层类型 来源 存储位置 是否可写
基础层(Base Layer) FROM 指令指定的基础镜像 Registry + 本地缓存 否(只读)
中间层(Intermediate Layer) RUN / COPY / ADD 等指令 Registry + 本地缓存 否(只读)
容器层(Container Layer) 容器运行时写入 本地存储驱动 是(读写)
分层下载优化要点:
• 基础层通常较大(如 Ubuntu、Alpine),但一旦下载会被缓存,后续拉取相同基础镜像的容器时直接复用。
• 中间层按 Dockerfile 指令顺序生成,修改 Dockerfile 中靠前的指令会导致后续所有层失效(缓存失效)。
• 灾备场景下,建议将不变的基础层提前预热到本地或镜像仓库代理(Registry Mirror)。

2.3 Registry 交互过程

Docker 与 containerd 均遵循 OCI Distribution Spec 与 Registry 交互。交互过程分为以下关键步骤:

  1. Ping / API 版本协商:客户端向 Registry 的 /v2/ 端点发送 GET 请求,确认 Registry 支持 v2 API。
  2. 身份认证:若 Registry 需要认证,返回 401 Unauthorized 并携带 Www-Authenticate 头,客户端使用 Basic Auth 或 Bearer 进行认证。
  3. 获取 Manifest:客户端请求 /v2/<name>/manifests/<reference>,获取镜像清单(支持 Docker Manifest V2 Schema 2 或 OCI Image Manifest)。
  4. 获取 Blob(层数据):根据 Manifest 中的层 Digest(SHA256 摘要),客户端并发请求 /v2/<name>/blobs/<digest> 下载各层。
  5. Mount 请求(可选):若目标 Registry 支持跨仓库 Blob 挂载,客户端可发送 POST /v2/<name>/blobs/uploads/?mount=<digest>&from=<repository> 避免重复上传。
Registry 交互时序(OCI Distribution Spec) 客户端 Registry 1. GET /v2/ (Ping) 2. 401 + Www-Authenticate 3. GET /v2/ (Bearer ) 4. 200 OK 5. GET /v2/<name>/manifests/<tag> 6. 返回 Manifest(层 Digest 列表) 7. GET /v2/<name>/blobs/<digest> (并发) 8. 返回 Blob 二进制流 说明:并发下载数由 max-concurrent-downloads 控制(默认 3),灾备场景可适当调高以加速拉取

2.4 Docker 与 containerd 拉取流程差异

维度 Docker Engine containerd(通过 CRI / nerdctl)
拉取入口 Docker CLI → dockerd → distribution 模块 nerdctl / kubelet → containerd → 内置 pull 逻辑
镜像存储 /var/lib/docker/(overlay2 子目录) /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/
Manifest 格式 Docker Manifest V2 Schema 2(默认) OCI Image Manifest(默认)
并发下载控制 max-concurrent-downloads(daemon.json) 通过 containerd 配置或 CRI 参数控制
镜像缓存 本地层缓存 + 镜像仓库代理(mirror) 本地层缓存 + 支持 registry.mirrors 配置
灾备关键差异:
containerd 的镜像存储结构更扁平,层数据直接以 Blob 形式存储,不依赖 Docker 的 graphdriver 层。在灾备恢复场景中,containerd 的镜像预热和快照恢复速度通常优于 Docker Engine,且资源占用更低。

2.5 分层下载的并发与缓存策略

分层下载的并发度直接影响拉取速度。以下是关键参数与优化建议:

  • 并发下载数:Docker 默认 3,containerd 默认 3~5。灾备场景可调至 6~10(需考虑网络带宽与 Registry 限流)。
  • 层缓存命中:本地已存在的层(相同 Digest)直接跳过下载,仅下载缺失层。灾备场景建议使用 Registry Mirror 或 P2P 分发工具(如 Dragonfly) 提升缓存命中率。
  • 层压缩格式:gzip(默认)vs zstd(OCI 标准扩展)。zstd 压缩率更高、解压速度更快,适合灾备场景下的带宽优化。
  • 并行解压:下载完成后,各层解压可并行进行(CPU 密集型),需确保宿主机有足够 CPU 资源。
灾备优化实践:
1. 使用 containerd + zstd 压缩镜像,减少传输体积。
2. 配置本地 Registry Mirror,将常用基础镜像提前同步到灾备站点。
3. 调高 max-concurrent-downloads 至 8,配合 P2P 分发工具降低 Registry 压力。
4. 定期执行镜像预热脚本,确保灾备节点缓存最新层数据。