2. Docker Registry 原理:Registry 架构、镜像存储结构、Pull 流程详解

Docker Registry 是容器镜像的存储与分发中心,理解其内部架构、存储模型以及 Pull 流程,是优化镜像拉取速度、设计高效缓存策略的基础。本节从三个维度深入剖析 Registry 的工作原理。

2.1 Registry 架构概览

Docker Registry 采用典型的 Client-Server 架构,核心组件包括前端 API、认证授权层、存储后端以及垃圾回收模块。其设计目标是高可用、可扩展且与存储后端解耦。

组件 职责 说明
HTTP API 对外暴露 RESTful 接口 处理 GET /v2/、HEAD /v2/<name>/blobs/<digest> 等请求
认证授权 认证与访问控制 支持 Bearer 、Basic Auth,可对接 LDAP/OIDC
存储驱动 抽象底层存储系统 支持 filesystem、S3、GCS、Azure Blob、OCI 等
垃圾回收 清理未被引用的 Blob 通过 registry garbage-collect 命令触发
通知 Webhook 推送事件到外部系统 用于 CI/CD 触发、镜像同步、安全扫描
架构要点: Registry 本身是无状态的,所有持久化数据均委托给存储后端。这使得 Registry 可以水平扩展,并通过 CDN 或对象存储加速全球分发。

2.2 镜像存储结构:Manifest、Blob、Config

Docker 镜像并非一个单一文件,而是由 Manifest(清单)、Config(配置) 和 Layer Blob(层数据块) 组成的逻辑集合。这种分层结构是镜像拉取加速的核心基础。

  • Manifest(清单):描述镜像的元数据,包含 Config 的 Digest、所有 Layer Blob 的 Digest 列表以及媒体类型。Registry 通过 Manifest 定位整个镜像。
  • Config(配置):JSON 格式的镜像配置,包含环境变量、工作目录、入口点、暴露端口以及容器历史命令。Config 本身也是一个 Blob,通过 Digest 引用。
  • Layer Blob(层数据块):每一层文件系统的变更集(tar 包),通过 Content-Addressable 方式存储,以 SHA256 摘要作为唯一标识。所有层叠加后构成容器的根文件系统。
镜像存储结构:Manifest → Config → Layer Blob Manifest (v2) Config (JSON) Layer Blob 1 Layer Blob 2 Layer Blob N Content-Addressable 存储 每个 Blob 由 SHA256 摘要 唯一标识,不可变。 相同层在不同镜像间共享。
关键概念: Manifest 的媒体类型决定了镜像格式。Docker 使用 application/vnd.docker.distribution.manifest.v2+json,OCI 使用 application/vnd.oci.image.manifest.v1+json。多架构镜像则使用 Manifest List(索引)。

2.3 Pull 流程详解

当客户端执行 docker pull 时,Registry 与客户端之间发生一系列 HTTP 交互。理解每一步的请求与响应,有助于定位拉取慢的瓶颈。

  1. 认证握手: 客户端向 /v2/ 发起请求,Registry 返回 401 Unauthorized 并携带 Www-Authenticate 头。客户端向认证服务获取 Bearer 。
  2. 获取 Manifest: 客户端携带 请求 GET /v2/<name>/manifests/<tag>。Registry 返回 Manifest JSON,包含 Config 和所有 Layer 的 Digest。
  3. 获取 Config: 客户端通过 GET /v2/<name>/blobs/<config_digest> 拉取镜像配置。该步骤通常很快,因为 Config 体积很小(几 KB)。
  4. 并行拉取 Layer Blob: 客户端根据 Manifest 中的 Layer Digest 列表,并发发起 GET /v2/<name>/blobs/<layer_digest> 请求。Registry 返回 Layer 的 tar 数据流。
  5. 解压与缓存: 客户端收到 Layer 数据后,逐层解压并缓存到本地 /var/lib/docker/overlay2/(或 containerd 的 snapshotter)。
Docker Pull 流程时序 Docker Client Registry 1. GET /v2/ (401 + Www-Authenticate) 2. GET /v2/<name>/manifests/<tag> 3. GET /v2/<name>/blobs/<config_digest> 4. GET /v2/<name>/blobs/<layer_digest_1> 4. GET /v2/<name>/blobs/<layer_digest_2> 4. GET /v2/<name>/blobs/<layer_digest_N> 5. 客户端解压 Layer 并写入本地存储(overlay2 / snapshotter)
性能瓶颈分析: Pull 流程中最耗时的阶段是 步骤 4(并行拉取 Layer Blob)。瓶颈通常出现在:网络带宽、Registry 存储后端 IOPS、Layer 体积过大、或客户端并发数限制。优化方向包括:使用 P2P 分发(如 Dragonfly)、启用 Registry 缓存、或采用镜像预热。

2.4 关键参数与优化指标

参数 说明 优化建议
max-concurrent-downloads 客户端并行拉取 Layer 的并发数 默认 3,可调至 6~10(需考虑带宽)
max-download-attempts 拉取失败重试次数 默认 5,网络不稳定时可适当增加
storage.driver Registry 存储后端类型 生产环境推荐 S3/GCS,避免本地文件系统
HTTP_PROXY Registry 出口代理 用于跨地域拉取时加速
实战要点: 在 Kubernetes 环境中,kubelet 默认使用 containerd 作为容器运行时。containerd 的 Pull 流程与 Docker 类似,但通过 CRI 接口调用。理解 Registry 原理后,可以针对性地配置 registry-mirrors 或部署 harbor 作为本地缓存 Registry,显著降低跨公网拉取延迟。