在Docker Registry v2 中,镜像是以分层的形式存储的,采用了内容寻址和分层存储的机制。

各个结构的示意图如下:

QQ_1732587635298

假设存储一个名为 my-app:latest 的镜像,目录结构如下:

registry/
└── docker
    └── registry
        └── v2
            ├── blobs/
            │   └── sha256/
            │       ├── <digest-of-layer-1>/
            │       ├── <digest-of-layer-2>/
            │       ├── <digest-of-layer-3>/
            │       └── <digest-of-config>/
            └── repositories/
                └── my-app/
                    └── _manifests/
                        └── tags/
                            └── latest/
                                └── current/
                                    └── link

下面分别介绍各个部分的结构.

image name

镜像名:镜像名是用户访问镜像的基础标识,通常以<仓库名>:<标签>的形式表示,例如:my-registry.com/my-app:latest.

tag

标签:镜像标签是对具体镜像版本的友好引用。标签本身并不包含实际数据,而是指向一个Manifest,多个标签可以指向同一个 manifest,实现镜像的版本管理和复用。

tags/latest/current/link 指向对应的 Manifest。

manifest

manifest是一个JSON 格式的文本文件,存储在 Docker Registry 中,用于描述一个镜像的组成部分,主要包括以下三部分:

  • 镜像的各层(Layers)的 Digest。

  • 镜像的配置(Config),包括启动命令、环境变量等。

  • 层的媒体类型(Media Type)和大小。

manifest是一个JSON 格式的文本文件,存储在 Docker Registry 中,用于描述一个镜像的组成部分。存储在manifests文件夹下。

manifest结构举例如下:

{
  "schemaVersion": 2,
  "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
  "config": {
    "mediaType": "application/vnd.docker.container.image.v1+json",
    "size": 7023,
    "digest": "sha256:c54a2cc56cbb2f0406b44acb6b5f5b3dbb21f19d50f8eedc5bba7d6a9dd67f58"
  },
  "layers": [
    {
      "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
      "size": 32654,
      "digest": "sha256:ec4a7858f0b2561c830233f6e1f3b6a08efc1d7d706032d22b9a6f0d69a7c4cc"
    },
    {
      "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
      "size": 16724,
      "digest": "sha256:4cf1eda4d058d6915a4ea26453ed309b6937383c6e5b51a6da64e24835a5c303"
    },
    {
      "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
      "size": 73109,
      "digest": "sha256:8c662931926fae5a8fd0d7a3096f7b44c29b28a8e6a4b5e2a48b00601234abcd"
    }
  ]
}

layer

镜像是由多个层组成的,层是文件系统的增量变化记录,每一层存储新增修改或删除的文件.层之间是有序的,构成了镜像的完整文件系统。每一层在 Docker Registry 或本地存储中被保存为一个压缩的 tar 文件,包含该层的文件和目录结构。

  • 添加文件。

  • 修改文件。

  • 删除文件。

镜像的每一层以内容哈希值(Digest, SHA256)为文件名存储在 blobs目录中。每一层的 Blob 存储在 blobs/sha256下。

分层的具体内容

每一层表示镜像构建过程中对文件系统的增量修改。这些修改通常包括新增文件、修改文件、删除文件、安装软件等操作。每一层只记录从上一层的增量变化,因此镜像存储高效且支持分层复用。

以下面这个构建镜像的 Dockerfile 举例

# 基础镜像
FROM ubuntu:20.04
# 设置工作目录
WORKDIR /app
# 安装软件
RUN apt-get update && apt-get install -y python3
# 复制本地文件到镜像
COPY app.py /app/app.py
# 设置默认启动命令
CMD ["python3", "app.py"]

当这个 Dockerfile 被执行时,每条指令(Instruction)都会生成一层layer.

FROM ubuntu:20.04

这一层使用了基础镜像 ubuntu:20.04,其内容包含该镜像的文件系统快照。

例如:

/bin/bash
/etc/apt/
/lib/
...

这是整个镜像的第一个层,所有后续层都基于它。

WORKDIR /app

这一层的内容是工作目录的变更。 实际上,这个层并不会在文件系统中创建新文件或目录,而是仅在元数据中记录 "工作目录为 /app" 的设置。

RUN apt-get update && apt-get install -y python3

这一层包含执行命令后对文件系统的修改: 更新 apt-get 缓存(通常修改 /var/lib/apt/lists/)。 安装 Python3(添加文件到 /usr/bin/python3 和相关依赖到 /usr/lib/ 等)。 例如,新增的文件可能包括:

/usr/bin/python3
/usr/lib/python3/
/var/lib/dpkg/
/etc/apt/sources.list
...
COPY app.py /app/app.py

这一层的内容是将 app.py 文件从构建上下文复制到镜像内的 /app/ 目录。

CMD ["python3", "app.py"]

这一层不改变文件系统,只更新镜像的元数据,记录默认的启动命令。 镜像中的 CMD 命令只是设置运行容器时的默认行为。

整个过程可以用一张图表表示:

层序号

内容描述

示例文件系统变化

Layer 1

基础镜像(ubuntu:20.04)

/bin/, /lib/, /etc/apt/

Layer 2

WORKDIR /app

无实际文件变化,仅元数据更新为:工作目录=/app

Layer 3

RUN apt-get install python3

/usr/bin/python3, /usr/lib/, /var/lib/apt/lists/

Layer 4

COPY app.py /app/app.py

/app/app.py

Layer 5

CMD ["python3", "app.py"]

无实际文件变化,仅元数据更新为:启动命令=["python3", "app.py"]

blob

Blob是存储镜像层和配置的基本单元,每个 Blob 对应镜像的一个层或配置文件。

Blob 存储是无状态的,可以独立存放在不同的后端存储中(如本地文件系统、Amazon S3、Google Cloud Storage 等)。通过配置文件 config.yml 可以切换存储后端。