Docker 镜像 SIZE、DISK USAGE、Layer、Prune、.dockerignore 全面解析:pull 到底下载多少?

前言

很多人第一次看到 Docker 镜像大小时,都会有同样的困惑:

  • 为什么 CONTENT SIZE 只有 3GB,DISK USAGE 却有 10GB?
  • docker pull 到底要下载多少流量?
  • 改了一个 5GB 的大文件只改了 1KB,为什么镜像像"翻倍"了一样?
  • docker image prunedocker builder prunedocker system prune 到底有什么区别?
  • .dockerignore 能解决什么问题?

如果你也有这些疑问,这篇文章会把 Docker 镜像体积相关的核心概念一次讲透。


一、先记住一个总原则

Docker 的体积问题,核心不是"文件本身大不大",而是下面这三件事:

  1. 镜像是否被分层存储
  2. 这些层是否能复用
  3. 构建上下文是否被正确过滤

Docker 最重要的设计目标不是"极致压缩",而是:

  • 可复现
  • 可缓存
  • 可复用
  • 可增量分发

也就是说,Docker 更像一个"分层快照系统",而不是一个"智能差分压缩系统"。


二、CONTENT SIZE 和 DISK USAGE 到底是什么

1. CONTENT SIZE

CONTENT SIZE 可以理解为:

这个镜像自身"逻辑上"包含的数据量。

它更接近"镜像本体"的大小,偏向于 远程仓库分发视角 或者 镜像内容总量视角

你可以把它理解成:

  • 这个镜像自己需要哪些 layer
  • 这些 layer 独立看有多大
  • 不考虑本机已经有没有这些 layer

2. DISK USAGE

DISK USAGE 指的是:

Docker 在你本地磁盘上实际占用了多少空间。

它往往比 CONTENT SIZE 更大,因为它可能包含:

  • 多个镜像共享但重复保留的层
  • 旧版本镜像
  • stopped container 的可写层
  • build cache
  • dangling image
  • volume 数据
  • 历史残留

所以你会看到:

  • CONTENT SIZE 只有 3GB
  • DISK USAGE 却达到 10GB

这并不奇怪,说明本地 Docker 环境已经"积累了历史包袱"。


三、Docker 镜像为什么是"分层"的

Docker 镜像不是一个完整的大文件,而是由多个 layer 叠加组成的。

每一层都记录了相对于上一层的变化。

例如:

  • 基础系统层:Ubuntu
  • 语言运行时层:Python / Node.js
  • 依赖层:pip install / npm install
  • 业务代码层:COPY . .

这些层会被组合起来,形成最终可运行的镜像。

镜像分层结构示意

flowchart TB A[Base Image\nUbuntu] --> B[Runtime Layer\nPython / Node] B --> C[Dependencies Layer\npip install / npm install] C --> D[App Code Layer\nCOPY source code] D --> E[Final Image]

四、什么是 shared layer,什么是 unique layer

1. Shared layer

Shared layer 是多个镜像都能复用的层。

例如:

  • ubuntu:22.04
  • python:3.12
  • 常见系统库

如果多个镜像都用到了同一层,Docker 只需要保存一份。


2. Unique layer

Unique layer 是某个镜像独有的层。

比如:

  • 你的业务代码
  • 特定版本的依赖
  • 某次构建中新产生的内容
  • 修改后的大文件

Unique layer 不是"只有一层",而是:

这个镜像独有的那些 layer 的集合

所以它可能是 1 层,也可能是 3 层、5 层甚至更多。


五、一个最容易误解的问题:pull 到底下载多少流量

结论先说

docker pull 下载的不是你看到的 DISK USAGE 那么多,而是:

本地没有的 layer

也就是说,下载量大致等于:

  • 这个镜像需要的 layer 中
  • 你本机当前还没有的那些 layer
  • 再加上这些 layer 的压缩传输体积

重要理解

Docker pull 不是"下载一个大包",而是"按 layer 下载"。

如果你本地已经有某些 layer,那么 pull 时会跳过它们,只下载缺失的部分。

pull 流程示意

flowchart TD A[开始 docker pull] --> B{本地是否已有该 layer?} B -- 有 --> C[跳过下载] B -- 没有 --> D[下载该 layer] C --> E[继续检查下一层] D --> E E --> F[合并所有需要的 layer] F --> G[得到镜像]

六、为什么 CONTENT SIZE 是 3GB,DISK USAGE 却可能是 10GB

常见原因有这些:

1. 镜像之间共享层不止一份

你可能有多个镜像版本:

  • app:latest
  • app:v1
  • app:v2

它们共享基础层,但各自又有不同的业务层。

共享层只存一份,但如果你在本机积累了很多版本,总占用会迅速上升。


2. build cache 很大

构建过程中,Docker 会保存中间产物和缓存层。

这些缓存层:

  • 不是最终镜像内容
  • 但会占用磁盘
  • 常常是"DISK USAGE 大户"

尤其在频繁 build 的项目里,build cache 很容易堆积到好几 GB。


3. stopped containers 也占空间

容器运行时有可写层,如果容器停止后没有清理,它也会保留数据。

里面可能包括:

  • 日志
  • 临时文件
  • 运行时生成的数据

4. volume 数据

如果你把数据库、缓存、附件挂到了 volume,上面的数据不会算在某个镜像里,但会占 Docker 的磁盘空间。


5. dangling image / 历史残留

一些中间镜像没有标签,但还在本地保留着。

它们往往看不见、摸不着,但就是在占空间。


七、改了一个大文件一小部分,为什么镜像会"翻倍"

这是很多人最困惑的一点。

关键答案

Docker 的 layer 不是"块级差分",更接近"文件级快照"。

也就是说:

  • 你改了一个大文件的一小部分
  • Docker 往往会把这个文件重新写入新 layer
  • 新 layer 里会出现该文件的新版本

如果这个文件本身很大,比如 5GB,那么即使你只改了 1KB,新的 layer 也可能接近 5GB。

结果

  • 旧 layer:5GB
  • 新 layer:5GB
  • 总计:接近 10GB

这就是"镜像翻倍感"的来源。


这和 git 一样吗?

不一样。

git 更偏向"内容差异和历史版本管理",而 Docker layer 更偏向"文件系统快照和分层复用"。

Docker 的目标不是做最强增量压缩,而是保证:

  • 构建简单
  • 缓存可复用
  • 镜像可重现
  • 分发可校验

八、Docker 有没有压缩机制

有,但要分清楚"压缩发生在哪"。

1. 传输时有压缩

docker pull / docker push 时,layer 通常会以压缩格式传输。

这意味着网络流量一般会比"原始内容大小"更小。

2. 存储层不是"把两个 layer 合起来智能压缩"

Docker 不会为了更高压缩率,把多个 layer 合成一个超大压缩包再分发。

因为这样会失去:

  • 层复用
  • 增量下载
  • 缓存命中
  • 内容寻址

所以 Docker 选择的是"分层复用",而不是"极致整体压缩"。


九、为什么不能把两个 layer 一起压缩

从压缩算法角度看,两个 layer 一起压缩,也许能压得更小一点。

但是 Docker 的核心价值不在这里,而在于:

  • 同一层能被多个镜像共享
  • 本地已有的 layer 可以跳过下载
  • 某层没有变,就不必重新传输

如果把多个 layer 合并压缩:

  • 共享性会下降
  • cache 命中率会变差
  • 拉取时常常要重新下载整个大包

所以 Docker 不这么设计。


十、prune 系列命令到底有什么区别

Docker 清理命令很多,但其实可以分成几类。

1. docker image prune

清理 dangling images,也就是没有标签、没有引用的镜像。

适合:

  • 清理无名中间镜像
  • 回收少量空间

2. docker image prune -a

清理所有未被容器使用的镜像。

docker image prune 激进得多。

适合:

  • 你明确不需要旧镜像
  • 想快速释放空间

3. docker container prune

清理停止状态的容器。

不会删正在运行的容器。


4. docker volume prune

清理未被使用的数据卷。

这个最危险,因为 volume 里经常是:

  • 数据库数据
  • 附件
  • 缓存
  • 用户持久化内容

执行前要非常小心。


5. docker builder prune

清理 build cache。

这通常是最值得先清理的对象,因为它往往占空间很大,而且相对安全。


6. docker system prune

综合清理:

  • 停止容器
  • dangling images
  • 未使用网络
  • 部分缓存

这是"常用的一键清理"。


7. docker system prune -a

最激进的清理方式。

会删除大量未使用对象,适合:

  • 环境已经很乱
  • 你准备大清场

但使用时要先确认是否会影响当前开发。


prune 关系图

flowchart TB A[Docker 清理命令] --> B[image prune] A --> C[image prune -a] A --> D[container prune] A --> E[volume prune] A --> F[builder prune] A --> G[system prune] A --> H[system prune -a] B --> B1[dangling images] C --> C1[未被容器使用的镜像] D --> D1[stopped containers] E --> E1[未使用 volumes] F --> F1[build cache] G --> G1[综合清理] H --> H1[最激进清理]

十一、.dockerignore 的作用是什么

.dockerignore 的作用非常关键:

控制哪些文件不会被发送到 Docker build 上下文里。

这是很多人减小镜像体积和加快 build 的第一步。

你可以理解为

docker build . 的时候,当前目录会作为构建上下文发送给 Docker。

如果不写 .dockerignore,很可能:

  • .git 传进去
  • node_modules 传进去
  • .env 传进去
  • 把模型文件传进去
  • 把临时文件传进去

这会导致:

  • build 慢
  • 上下文大
  • 镜像容易膨胀
  • 敏感信息可能被带进构建流程

常见 .dockerignore 示例

Node / 前端项目

dockerignore 复制代码
node_modules
dist
build
.git
.env
*.log

Python / AI 项目

dockerignore 复制代码
__pycache__/
*.pyc
.venv
venv/
.git
.env
logs/
tmp/
models/
*.pt
*.bin
*.onnx

.dockerignore 生效位置

flowchart LR A[本地项目目录] --> B[docker build 上下文打包] B --> C{.dockerignore 过滤} C -->|保留| D[发送给 Docker] C -->|忽略| E[不发送] D --> F[COPY 到镜像]

十二、为什么 .dockerignore 很适合 AI 项目

如果你做的是 AI 项目,特别容易遇到大体积文件:

  • embedding 模型
  • reranker 模型
  • ONNX 文件
  • tokenizer 缓存
  • 实验产物
  • 日志

这些东西如果被错误地放进镜像里,镜像会非常大。

很多 AI 项目镜像体积膨胀,不是因为代码多,而是因为:

大模型文件被一并打包进了 build context 或镜像层。

所以 AI 项目一定要认真写 .dockerignore


十三、一个推荐的镜像优化思路

如果你希望镜像更小,可以按这个思路来:

1. 基础层尽量小

  • 选轻量镜像
  • 避免不必要的系统包

2. 依赖安装分层优化

先复制依赖清单,再安装依赖,最后复制代码。

这样能最大化缓存复用。

3. 大文件不要直接打入镜像

  • 模型文件用挂载
  • 数据用 volume
  • 外部下载

4. 配好 .dockerignore

把不需要的内容挡在 build 之外。

5. 定期 prune

避免 build cache 堆积。


十四、实战建议:如何判断你的 Docker "脏不脏"

你可以先执行:

bash 复制代码
docker system df -v

重点看这些项:

  • Images
  • Containers
  • Local Volumes
  • Build Cache

如果 Build Cache 很大,通常先清它。

如果 Volume 很大,先确认里面有没有业务数据再动。

如果 Images 很大,看看是不是有多个旧版本镜像堆积。


十五、推荐的清理顺序

一个比较稳妥的顺序是:

  1. docker builder prune
  2. docker container prune
  3. docker image prune
  4. 视情况使用 docker image prune -a
  5. 最后再考虑 docker volume prune

这套思路通常能先回收最多空间,同时降低误删风险。


十六、总结:把这几个概念记住就够了

1. CONTENT SIZE

镜像自身逻辑大小,更接近"镜像内容总量"。

2. DISK USAGE

本地 Docker 实际占用空间,包含镜像、缓存、容器、卷、历史残留等。

3. UNIQUE layer

该镜像独有的层,不一定只有一层,而是一组独有层。

4. docker pull

只下载你本地没有的 layer,不是盲目下载全部。

5. 修改大文件会膨胀

Docker 更接近文件级快照,改一个大文件的一小部分,可能导致一个新大层。

6. .dockerignore

控制构建上下文,防止无关文件和大文件进入镜像。

7. prune

清理不同类型的垃圾:镜像、容器、卷、缓存,各有各的风险。


十七、可以直接拿走的结论

Docker 镜像体积不是单纯看一个数字,而是看层复用、缓存、构建上下文和本地残留。
pull 的流量通常接近"缺失 layer 的压缩后体积",而不是本机 DISK USAGE。
AI 项目要特别注意大模型文件、缓存和 .dockerignore,否则体积会非常夸张。

相关推荐
卷无止境1 小时前
Windows 上丝滑开发 Python,并稳定构建 Docker 镜像
后端·python·docker
吕海洋5 小时前
目前可用的docker源2026.08
运维·docker·容器
gsls2008088 小时前
Penpot Docker 部署与 MCP 服务配置文档
运维·docker·容器·原型·penpot
xrlfreedom9 小时前
大厂 MCP 面试实录:本地 Server 远程化改造中的技术选型与落地实践
docker·json schema·mcp
艾伦_耶格宇12 小时前
【DOCKER容器实战】-2MySQL
运维·docker·容器
禅口魔心14 小时前
RK3576开发——Docker部署
docker·嵌入式linux
随便做点啥15 小时前
32卡×4090 24GB,Qwen3.8-27B-FP8 集群部署报告
服务器·经验分享·docker·vllm
To_OC1 天前
跑了3个Docker容器后,我终于搞懂镜像和容器到底啥关系
后端·docker·容器
tryCbest1 天前
Docker 从零到精通:知识与跨平台使用指南
docker·容器
开始学AI1 天前
Codex2API Docker 使用宿主机代理:OAuth Token 兑换 403 问题排查与解决方案
运维·docker·容器