2. 容器镜像基础:镜像分层结构(Layer)、Manifest与Config、镜像仓库(Registry)的架构

容器镜像是容器化应用交付的核心单元。理解镜像的内部结构——分层文件系统、元数据描述(Manifest & Config)以及远程存储分发系统(Registry),是优化镜像拉取性能的前提。本节将拆解这三个关键组件,为后续的网络拓扑感知调度奠定知识基础。

2.1 镜像分层结构(Layer)

容器镜像采用分层(Layer)文件系统设计,这是实现存储复用、增量传输和快速启动的关键。每一层代表一组文件变更(添加、修改或删除),所有层叠加后构成容器的完整根文件系统。

2.1.1 分层原理与只读特性

  • 基础层(Base Layer):通常源自基础操作系统(如 Ubuntu、Alpine),提供基本的用户空间工具和库。
  • 中间层(Intermediate Layers):由 Dockerfile 中的每条指令(如 RUN、COPY、ADD)生成。每条指令创建一个新层。
  • 顶层(Top Layer):容器运行时在只读镜像层之上创建的可写层(Container Layer),所有运行时修改(写日志、生成临时文件)均在此层,容器销毁时该层随之消失。
关键优化点: 分层结构使得多个容器可以共享相同的基础层和中间层。在集群节点上,如果已有某镜像的底层缓存,拉取新镜像时只需传输差异层(增量层),大幅减少网络传输量。

2.1.2 层的内容与存储格式

每一层在物理存储上是一个压缩的 tar 包(tarball),包含该层引入的文件系统快照。层之间通过内容寻址(Content-Addressable)进行标识,即使用该层内容的 SHA256 哈希值作为唯一 ID。

属性 说明
层 ID 基于层内容的 SHA256 摘要,确保不可篡改和唯一性。
存储格式 压缩 tar 包(通常为 .tar.gz 或 .tar.zst),解压后为文件系统快照。
元数据 包含文件权限、所有者、时间戳等,存储在层对应的 JSON 文件中。
父子关系 通过 parent 字段指向上一层,形成链式结构。

2.2 Manifest 与 Config:镜像的元数据描述

镜像的物理层数据需要一份“目录”来描述其组成和配置。这份目录由 Manifest(清单)和 Config(配置)两部分构成,遵循 OCI(Open Container Initiative)镜像规范。

2.2.1 Config 文件

Config 是一个 JSON 对象,记录了镜像的运行时配置和层的历史信息。它不包含文件系统数据,而是描述容器启动时应该如何配置。

{
  "created": "2024-01-15T10:00:00Z",
  "architecture": "amd64",
  "os": "linux",
  "config": {
    "Env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],
    "Cmd": ["/bin/bash"],
    "WorkingDir": "/app",
    "ExposedPorts": {"8080/tcp": {}}
  },
  "rootfs": {
    "type": "layers",
    "diff_ids": [
      "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4",
      "sha256:86e8f2f7c9b1a0c6c7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c"
    ]
  },
  "history": [
    {"created": "2024-01-14T08:00:00Z", "created_by": "/bin/sh -c apt-get update"},
    {"created": "2024-01-15T10:00:00Z", "created_by": "COPY app /app"}
  ]
}
  • rootfs.diff_ids:按顺序列出每一层未压缩内容的 SHA256 哈希值,用于验证层完整性。
  • history:记录构建历史,用于审计和缓存失效判断。

2.2.2 Manifest 文件

Manifest 是一个 JSON 列表(或单个对象),指向 Config 文件和所有层的压缩后的存储位置(通常是 Registry 上的 Blob 地址)。它是 Registry 返回给客户端的第一个元数据。

{
  "schemaVersion": 2,
  "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
  "config": {
    "mediaType": "application/vnd.docker.container.image.v1+json",
    "size": 7023,
    "digest": "sha256:fc1a6b8f2c4d5e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e"
  },
  "layers": [
    {
      "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
      "size": 32654,
      "digest": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
    },
    {
      "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
      "size": 12345,
      "digest": "sha256:86e8f2f7c9b1a0c6c7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c"
    }
  ]
}
拉取流程中的关键步骤: 客户端首先通过 GET /v2/<name>/manifests/<reference> 获取 Manifest,解析出 Config 和所有 Layer 的 digest,然后并发下载这些 Blob。Manifest 的大小直接影响首次元数据获取的延迟。

2.3 镜像仓库(Registry)的架构

Registry 是镜像的远程存储和分发中心。一个高效的 Registry 架构需要支持高并发、低延迟的拉取,以及跨地域的镜像同步。

2.3.1 核心组件与数据模型

Registry 内部将镜像数据抽象为两种资源:Manifest 和 Blob。Blob 即镜像层的数据块(压缩 tar 包),Manifest 是引用 Blob 的索引。

组件 存储内容 访问方式
Blob Store 镜像层数据(压缩 tar 包)、Config 文件 通过 digest(SHA256)进行内容寻址
Manifest Store 镜像清单(JSON),引用 Config 和 Layers 的 digest 通过 tag 或 digest 访问
Tag Store 标签到 Manifest digest 的映射(如 latest → sha256:abc...) 通过 tag 查询

2.3.2 典型 Registry 架构(以 Harbor 为例)

企业级 Registry(如 Harbor、Docker Registry 2.0)通常采用分层架构,以支持高可用和水平扩展。

镜像仓库(Registry)分层架构 镜像仓库(Registry)分层架构 客户端(Docker / containerd / kubelet) 负载均衡(Nginx / HAProxy) Registry API(认证 / 授权 / 路由) 存储后端(Storage Backend) Blob Store(层数据) Manifest Store Tag Store 物理存储:本地文件系统 / S3 / GCS / Azure Blob / Ceph

2.3.3 镜像拉取的核心流程

当客户端(如 kubelet)发起拉取请求时,涉及以下步骤:

  1. 认证与授权:客户端向 Registry API 发送凭证,获取访问令牌。
  2. 获取 Manifest:通过 GET /v2/<name>/manifests/<tag> 获取镜像清单,解析出 Config 和所有 Layer 的 digest 及大小。
  3. 获取 Config:通过 GET /v2/<name>/blobs/<digest> 下载 Config 文件,验证镜像配置。
  4. 并发下载 Layers:根据 Manifest 中的 layers 列表,并发发起多个 Blob 下载请求。Registry 支持 Range 请求,支持断点续传。
  5. 解压与验证:客户端对下载的压缩层进行解压,并计算其 SHA256 摘要与 Manifest 中的 digest 比对,确保数据完整性。
  6. 挂载层:将解压后的层按顺序叠加到容器的根文件系统。
网络拓扑感知的切入点: 在步骤 4(并发下载 Layers)中,如果客户端能够感知到与 Registry 之间的网络拓扑(如节点是否在同一机架、同一可用区、是否经过跨区域链路),就可以智能调度下载请求——优先从拓扑距离最近的 Registry 副本或 P2P 缓存节点拉取层数据,从而显著降低跨区域带宽消耗和拉取延迟。

2.4 本章小结

容器镜像的分层结构实现了高效的存储复用和增量传输;Manifest 与 Config 提供了镜像的完整元数据描述,是客户端与 Registry 交互的契约;Registry 架构通过内容寻址和分层存储支撑了大规模镜像分发。理解这三者的关系,是后续设计网络拓扑感知调度策略的基础——优化目标正是让“层数据的传输路径”尽可能短、尽可能快。