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. 将层挂载到本地存储驱动,构建可读写容器层
1. 客户端发起 pull 请求(指定镜像名称与标签)
2. 解析镜像名称,获取 Registry 地址与仓库路径
3. 与 Registry 建立 HTTPS 连接,进行身份认证(如需)
4. 获取镜像 Manifest(清单),解析层列表与配置
5. 并发下载各层 Blob(分层数据)
6. 验证层摘要(Digest)与解压
7. 将层挂载到本地存储驱动,构建可读写容器层
2.2 分层下载机制详解
Docker 镜像由多个只读层(Layer)叠加而成,每个层对应 Dockerfile 中的一条指令。分层下载的核心优势在于:层可以被复用和缓存,本地已存在的层无需重复下载。
| 层类型 | 来源 | 存储位置 | 是否可写 |
|---|---|---|---|
| 基础层(Base Layer) | FROM 指令指定的基础镜像 | Registry + 本地缓存 | 否(只读) |
| 中间层(Intermediate Layer) | RUN / COPY / ADD 等指令 | Registry + 本地缓存 | 否(只读) |
| 容器层(Container Layer) | 容器运行时写入 | 本地存储驱动 | 是(读写) |
分层下载优化要点:
• 基础层通常较大(如 Ubuntu、Alpine),但一旦下载会被缓存,后续拉取相同基础镜像的容器时直接复用。
• 中间层按 Dockerfile 指令顺序生成,修改 Dockerfile 中靠前的指令会导致后续所有层失效(缓存失效)。
• 灾备场景下,建议将不变的基础层提前预热到本地或镜像仓库代理(Registry Mirror)。
• 基础层通常较大(如 Ubuntu、Alpine),但一旦下载会被缓存,后续拉取相同基础镜像的容器时直接复用。
• 中间层按 Dockerfile 指令顺序生成,修改 Dockerfile 中靠前的指令会导致后续所有层失效(缓存失效)。
• 灾备场景下,建议将不变的基础层提前预热到本地或镜像仓库代理(Registry Mirror)。
2.3 Registry 交互过程
Docker 与 containerd 均遵循 OCI Distribution Spec 与 Registry 交互。交互过程分为以下关键步骤:
- Ping / API 版本协商:客户端向 Registry 的
/v2/端点发送 GET 请求,确认 Registry 支持 v2 API。 - 身份认证:若 Registry 需要认证,返回 401 Unauthorized 并携带
Www-Authenticate头,客户端使用 Basic Auth 或 Bearer 进行认证。 - 获取 Manifest:客户端请求
/v2/<name>/manifests/<reference>,获取镜像清单(支持 Docker Manifest V2 Schema 2 或 OCI Image Manifest)。 - 获取 Blob(层数据):根据 Manifest 中的层 Digest(SHA256 摘要),客户端并发请求
/v2/<name>/blobs/<digest>下载各层。 - Mount 请求(可选):若目标 Registry 支持跨仓库 Blob 挂载,客户端可发送
POST /v2/<name>/blobs/uploads/?mount=<digest>&from=<repository>避免重复上传。
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,且资源占用更低。
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. 定期执行镜像预热脚本,确保灾备节点缓存最新层数据。
1. 使用 containerd + zstd 压缩镜像,减少传输体积。
2. 配置本地 Registry Mirror,将常用基础镜像提前同步到灾备站点。
3. 调高 max-concurrent-downloads 至 8,配合 P2P 分发工具降低 Registry 压力。
4. 定期执行镜像预热脚本,确保灾备节点缓存最新层数据。