☰
Docker 镜像(Image)
2026/9/27 9:48:34 网站建设 项目流程

从 Layer 到 Registry,理解镜像的构建、存储与分发

本文是对 Docker Image 机制的一次系统学习总结。

不只是介绍docker images、docker pull这些命令,而是从一个实际问题出发,逐步理解 Docker Image 为什么存在、为什么要分层、Layer 为什么可以共享、Digest 和 Manifest 又分别解决什么问题,以及 Image 最终是如何被构建、存储和分发的。


一、先从一个问题开始:换一台 Linux,怎么运行同一个项目?

假设我们现在有一个 Go 项目。

在自己的电脑上运行没有问题。

但是如果换一台 Linux 服务器呢?

可能需要重新:

上传代码 ↓ 安装 Go ↓ 安装项目依赖 ↓ 修改配置 ↓ 启动项目

如果服务器环境和开发环境又存在差异,就可能出现:

“在我电脑上明明可以运行,为什么换台机器就不行了?”

Docker 解决这个问题的核心思路之一,就是:

把运行应用所需要的内容打包起来。

而这个“打包后的东西”,就是我们今天要重点讨论的:

Docker Image


二、Docker Image 到底是什么?

可以先把 Image 理解成:

一个用于创建容器的模板,其中包含运行应用所需要的文件和配置。

简单来说:

应用程序 + 运行环境 + 依赖 + 配置 ↓ Docker Image

例如一个 Go 应用,可以构建成一个 Image。

然后把这个 Image 拿到另一台 Linux:

Docker Image ↓ 另一台 Linux ↓ 运行应用

这样就不需要每次都重新从头配置整个运行环境。

不过这里马上会产生一个问题:

Image 是模板,那真正运行起来的东西是什么?

这就要引出 Docker 中另外一个非常重要的概念:

Container


三、Image 和 Container 到底有什么区别?

两者可以简单理解为:

Image │ │ docker run ↓ Container

1. Image:镜像

Image 可以理解成一个只读模板。

里面包含:

应用 依赖 文件系统 配置

它本身并不是一个正在运行的程序。


2. Container:容器

Container 是基于 Image 创建出来的运行实例。

可以简单理解成:

Container = Image 内容 + Writable Layer + 运行时状态 / 配置

所以:

Image 是“模板”,Container 是“运行起来的实例”。

例如:

docker run nginx

可以理解成:

nginx Image ↓ 创建 ↓ nginx Container

3. 一个 Image 可以创建多个 Container

例如:

nginx Image / | \ ↓ ↓ ↓ Container Container Container

所以如果启动 10 个 nginx Container:

并不意味着需要准备 10 份完全独立的 nginx Image。

多个 Container 可以共享 Image 的只读内容。

这就产生了一个新的问题:

如果多个 Container 可以共享 Image,那么 Image 本身到底是怎么保存的?


四、Image 为什么要分层?

如果我们只修改其中一个文件:

修改一个配置文件 ↓ 整个 Image 都重新处理?

显然会比较浪费。

所以 Docker 使用了:

Layer(镜像层)

一个 Image 不再简单理解成一个整体,而是由多个 Layer 组成:


五、Layer 到底是什么?

Layer 可以理解成:

一组文件系统变化。

例如:

Layer 1 → 基础文件 Layer 2 → 新增 / 修改文件 Layer 3 → 添加配置 Layer 4 → 添加应用文件

这里需要特别注意:

Layer 本质上不是“应用层”“依赖层”“运行时层”。

这些只是为了方便理解而进行的示意。

更准确地说:

Layer ↓ 文件系统变化 ↓ 新增 / 修改 / 删除文件

也就是说,每一层描述的是:

在原来的文件系统基础上,又发生了什么变化。


六、Layer 为什么可以共享?

既然 Image 是由 Layer 组成的,那么就会产生一个非常有意思的问题:

如果两个 Image 中存在相同的 Layer,需要保存两份吗?

例如:

两个 Image 都使用了:

Layer A1 Layer A2

那么本地并不需要简单地理解成:

保存 A1 保存 A2 再保存一份 A1 再保存一份 A2

而是可以对相同 Layer 进行复用。

这就是 Docker Image 分层非常重要的一个价值:

Layer 可以共享和复用

带来的好处包括:

  • 减少本地存储

  • 减少重复下载

  • 提高镜像分发效率

  • 支持 Docker Build Cache

例如:

那么:

Layer 1 Layer 2

可以被两个 Image 共同使用。

但是问题又来了:

Docker 怎么知道两个 Layer 是不是相同的?


七、Digest:给内容一个“指纹”

Docker 使用 Digest 来帮助进行内容寻址。

可以简单理解成:

Layer 内容 ↓ SHA256 ↓ Digest

例如:

Layer A ↓ sha256:abc123...

另一个 Image 中如果存在相同内容:

Layer B ↓ sha256:abc123...

那么就可以通过 Digest 判断它们对应的是相同内容。

简单理解:

内容相同 ↓ Digest 相同 ↓ 可以识别 / 复用

如果内容发生变化:

原始内容 ↓ sha256:abc123 内容发生变化 ↓ sha256:def456

Digest 也会发生变化。

因此可以把 Digest 理解成:

内容的“指纹”。


八、Tag 和 Digest 有什么区别?

我们平时经常看到:

nginx:latest

这里的:

latest

就是 Tag。

而另外一种引用形式是:

nginx@sha256:xxxx

这里使用的是 Digest。

两者的核心区别可以简单理解为:

TagDigest
本质名字 / 引用内容摘要
是否可以变化可以内容变化时改变
示例nginx:latestsha256:xxxx

例如:

nginx:latest

今天可能指向 Image A:

latest → Image A

以后 Registry 中的latest可能指向新的 Image B:

latest → Image B

所以 Tag 更像:

“这个镜像现在叫什么。”

而 Digest 更接近:

“这个具体内容是谁。”


九、一个 Image 到底由哪些东西组成?

到这里,我们已经知道:

Image ↓ 很多 Layer

同时又知道:

Layer ↓ Digest

但是又出现了一个问题:

Docker 怎么知道一个 Image 到底应该使用哪些 Layer?

这时候就需要理解:

Manifest

可以把 Manifest 理解成:

Image 的“组成清单”。

例如:

Manifest 会描述 / 引用:

  • Config

  • Layer

  • Digest

  • Size

  • Media Type

因此:

Manifest 不是用来保存文件内容的,而是用来描述 Image 由什么组成。


十、现在回头看,一个 Image 到底是什么?

把前面的知识串起来

再展开:

所以可以形成一个比较完整的认识:

Tag / Digest ↓ 找到 Image ↓ Manifest ↓ 知道有哪些 Config + Layers ↓ Config + Layer 内容

一句话总结:

引用找到镜像,Manifest 描述组成,Layers 保存文件系统内容。


十一、这些 Layer 是怎么来的?

前面解决了:

Image 是什么?

又解决了:

Image 为什么分 Layer?

接下来就要问:

这些 Layer 到底是谁创建出来的?

答案就是:

Dockerfile + Docker Build

基本过程:

Dockerfile ↓ docker build ↓ Layers + Config ↓ Manifest ↓ Image

例如一个简单的 Go Dockerfile:

FROM golang:1.25 WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN go build -o app

其中一些指令会产生文件系统变化。

例如:

RUN / COPY / ADD ↓ 文件系统变化 ↓ Layer

而一些指令主要影响 Image 的配置或元数据,例如:

ENV CMD EXPOSE ↓ Config / Metadata

因此可以建立一个非常重要的认识:

Dockerfile 描述构建过程,Build 最终产生 Image 所需要的 Layers 和 Config。


十二、为什么 Dockerfile 要分开写?

既然 Layer 可以复用,那么它不仅可以帮助节省存储。

还可以帮助:

Docker Build Cache

例如:

COPY go.mod go.sum ./ RUN go mod download COPY . . RUN go build -o app

假设我们只是修改了业务代码:

go.mod 没变 ↓ 依赖没有变化 ↓ 之前的依赖相关结果可以复用

但是:

源代码变化 ↓ COPY . . 发生变化 ↓ 后面的步骤需要重新执行

所以:

Layer ↓ 缓存 ↓ 复用构建结果 ↓ 减少重复构建

这也是为什么 Dockerfile 中经常会看到:

COPY go.mod go.sum ./ RUN go mod download COPY . .

而不是一开始就:

COPY . . RUN go mod download

前一种写法更有利于利用缓存。


十三、Multi-stage Build:构建环境和运行环境分开

再思考一个问题:

如果一个 Go 程序已经编译好了,运行的时候还需要 Go 编译器吗?

答案显然是不需要。

如果把所有东西都放进最终 Image:

最终 Image Go Compiler Source Code Dependencies Build Tools Application

最终镜像可能包含很多运行时根本不需要的内容。

因此可以使用:

Multi-stage Build

基本思路:

Build Stage ┌──────────────────┐ │ Compiler │ │ Source Code │ │ Dependencies │ └────────┬─────────┘ │ Build ↓ Application Binary │ ↓ Runtime Stage ┌──────────────────┐ │ Minimal Runtime │ │ Application │ │ Binary │ └──────────────────┘

也就是:

Build Stage 负责构建,Runtime Stage 负责运行。

这样可以:

  • 减小最终 Image

  • 不携带编译器

  • 不携带源码

  • 减少运行环境中的无关内容


十四、一个 Tag,为什么可以支持不同平台?

现在又有一个新的问题:

如果我的电脑是 amd64,另一台机器是 arm64,能不能使用同一个镜像 Tag?

这就涉及:

Multi-platform Image

例如:

nginx:latest ↓ Image Index ┌──┴──┐ ↓ ↓ amd64 arm64 ↓ ↓ Manifest Manifest ↓ ↓ Config Config +Layers +Layers

当执行:

docker pull nginx

Docker 会根据当前平台选择对应的镜像变体。

可以简单理解成:

当前平台 ↓ Image Index ↓ 选择对应 Manifest ↓ 下载对应 Config + Layers

这里需要区分:

Multi-stageMulti-platform
解决构建问题平台问题
结构Build → Runtimeamd64 → arm64
关注怎么构建给谁运行

一句话:

Multi-stage 解决“怎么构建”,Multi-platform 解决“给谁运行”。


十五、Image 做好了,怎么把它带到另一台机器?

现在回到文章最开始的问题:

换一台 Linux,怎么运行同一个项目?

前面我们已经有了:

Dockerfile ↓ Build ↓ Image

那么接下来就是:

怎么把 Image 从一台机器带到另一台机器?

这时候就需要:

Registry

可以把 Registry 理解成:

Docker Image 的存储和分发中心。

基本流程:

我的电脑 │ │ docker push ↓ Registry │ │ docker pull ↓ 另一台 Linux

十六、Docker Image Reference 怎么看?

例如:

docker.io / library / nginx : latest

可以拆成:

docker.io ↓ Registry library ↓ Namespace nginx ↓ Repository latest ↓ Tag

所以:

Registry / Namespace / Repository / Tag

例如:

Repository: nginx Tags: ├── latest ├── 1.29 └── 1.28

这里要注意:

Repository 是nginx,不同的 Tag 可以指向不同版本的镜像内容。


十七、Docker CLI 拉取规则

最常见:

docker pull nginx

可以理解成默认使用:

docker.io/library/nginx:latest

如果指定 Tag:

docker pull nginx:1.29

表示拉取:

nginx └── Tag: 1.29

如果指定 Digest:

docker pull nginx@sha256:xxxx

则是根据确定的 Digest 指定内容。

因此可以记住三个简单规则:

不写 Registry → 使用默认 Registry 不写 Tag → 默认 latest 使用 @Digest → 指定确定内容

十八、一次docker pull到底发生了什么?

执行:

docker pull nginx

背后并不是简单地:

服务器 ↓ 把一个 Image 文件下载下来

而是一个逐步解析和获取的过程。

可以简化为:

docker pull nginx ↓ 解析 Image Reference ↓ 请求 Registry ↓ 获取 Image Index / Manifest ↓ 选择当前平台 ↓ 找到 Config + Layers ↓ 检查本地 Layer ┌────┴────┐ 已存在 不存在 ↓ ↓ 复用 下载 └────┬────┘ ↓ Local Image

这里非常重要的一点就是:

Pull 不一定会重新下载所有 Layer。

如果本地已经存在相同的 Layer:

Layer Digest ↓ 本地已经存在 ↓ 直接复用

这就是前面讲的:

Layer 共享 + Digest 内容寻址

最终这些内容组成了本地 Image。

然后:

Local Image ↓ docker run ↓ Container

十九、Image 用完了,怎么清理?

随着学习 Docker,我们很容易遇到:

docker images

看到:

REPOSITORY TAG IMAGE ID nginx latest abc123 <none> <none> def456

这时候可能会看到很多:

<none>

但需要注意:

<none>不一定等于 Dangling Image。

可以专门查看 Dangling Image:

docker images -f dangling=true

清理 Dangling Image

使用:

docker image prune

默认主要清理:

Dangling Images


更彻底的清理

docker image prune -a

它的范围更大:

删除没有被任何 Container 使用的 Image,包括有 Tag 但没有被使用的 Image。

所以:

docker image prune ↓ 范围较小 docker image prune -a ↓ 范围更大

执行prune -a前要确认是否真的不再需要这些 Image。


二十、最后把整个 Image 机制串起来

学习到这里,可以把 Docker Image 的完整链路串起来:

Dockerfile ↓ Build ↓ Layers + Config ↓ Manifest ↓ Image ↓ push ↓ Registry ↓ pull ↓ Local Image ↓ docker run ↓ Container ↓ Writable Layer

而 Image 内部又可以理解为:

Image Reference (Tag / Digest) ↓ Image Index(多平台时) ↓ Image Manifest ↓ ┌──────────────────┐ │ Config │ │ │ │ Layer 1 │ │ Layer 2 │ │ Layer 3 │ └──────────────────┘

其中:

Image

用于创建 Container 的只读模板。

Layer

文件系统变化的集合,也是镜像共享和构建缓存的重要基础。

Digest

基于内容计算得到的摘要,可用于内容寻址和识别相同内容。

Manifest

描述 Image 所使用的 Config 和 Layers。

Registry

Image 的存储和分发中心。

Container

Image 创建出来的运行实例,并拥有自己的可写层。


二十一、总结

如果只记住这几个关键词,可以记成:

Image ↓ Layer ↓ 共享 ↓ Digest ↓ Manifest ↓ Build ↓ Registry ↓ Pull ↓ Container

更完整地说:

Docker Image 并不是一个简单的大文件,而是由 Config 和多个 Layer 组成,并通过 Manifest 描述它们之间的关系。

Layer 带来了:

  • 内容复用

  • 镜像共享

  • 存储优化

  • 构建缓存

  • 增量分发

Digest 则帮助 Docker 根据内容进行识别和寻址。

而 Registry 则负责让这些 Image 可以在不同机器之间进行存储和分发。

最终形成:

Dockerfile ↓ Build ↓ Layers + Config ↓ Manifest ↓ Image ↓ Registry ↓ Pull ↓ Local Image ↓ Container

理解 Docker Image 的关键,就是理解“分层、内容寻址、Manifest 描述、Registry 分发”这四件事。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询