🐳 Docker 镜像瘦身的起点:用 docker history 揭秘其“族谱”

明明只是部署了一个轻量级应用,但查看镜像大小时却发现它轻松超过了1GB。你可能会感到困惑,心想“我到底加了什么东西?”。这时,我们需要做的就是揭开镜像的“族谱”,一探究竟。

Docker 镜像层级优化示意图

“你到底吃了什么,怎么长这么大了?”

Docker 镜像就像层层堆叠的洋葱皮(层)。docker history 命令就像一台X光机,能一层层剥开这些“外衣”,找出导致镜像臃肿的“元凶”。

其基本用法非常简单:

# 基本用法
docker history [选项] <镜像名称:标签>

# 示例:查看我的应用历史
docker history my-app:latest

执行该命令后,会从最新层开始,逆序显示镜像的所有层,直至基础镜像。

IMAGE          CREATED          CREATED BY                                      SIZE
a6215f271958   5 minutes ago    /bin/sh -c #(nop)  CMD ["/bin/sh"]               0B
<missing>      7 weeks ago      /bin/sh -c #(nop) ADD file:f28242cf608f6...     7.81MB

请仔细观察 SIZE 这一项。如果本应是 0B 的地方却显示了几百MB,那么它就是需要“瘦身”的对象。凭经验来看,总会有一些地方“不对劲”。

💡 实用技巧:--no-trunc

默认情况下,如果命令过长,输出内容会被截断。此时,加上 --no-trunc 选项,就能完整显示所有命令,这对于精确分析至关重要。

docker history --no-trunc my-app:latest


实战案例:“罪魁祸首是 chown

我来分享一个亲身经历的真实案例。我构建了一个 150MB 的项目文件,但镜像大小却诡异地超过了 300MB。而“罪魁祸首”就是 Dockerfile 中看似再普通不过的两行命令。

[低效方法:创建两个层]

# 1. 首先复制文件(默认以 root 用户复制)
COPY . .

# 2. 为安全起见更改所有权(作为单独命令执行)
RUN chown -R appuser:appgroup /app

构建之后,用 docker history 查看,结果如下:

IMAGE          CREATED BY                                        SIZE
<layer_id_2>   /bin/sh -c chown -R appuser:appgroup /app         150MB  <-- (问题所在!)
<layer_id_1>   /bin/sh -c #(nop) COPY dir:abc in /app            150MB

Docker 层级的“可怕”之处就在这里。第一步复制 150MB 文件时,会创建一个层。但第二步执行 chown 命令时,Docker 会认为“咦?文件信息(所有权)变了?那我就把更改后的状态再创建一个新层!”它会将原有的 150MB 文件原封不动地复制一份,再堆叠一个新层。

结果,内容完全相同,只是所有权不同的文件却被双重存储,导致镜像大小翻倍。


解决方案:一行命令搞定瘦身!

解决方案很简单。只需在复制文件时直接指定所有权,就能将多个层合并为一个。

[高效方法:创建单个层]

# 复制文件时同时指定所有权!
COPY --chown=appuser:appgroup . .

将 Dockerfile 这样修改后,再次执行 docker history,结果会发生翻天覆地的变化。

IMAGE          CREATED BY                                               SIZE
<layer_id_1>   /bin/sh -c #(nop) COPY --chown=appuser... dir:abc       150MB

看到了吗?只创建了一个层,镜像大小又“苗条”地回到了 150MB。仅仅是将两条命令合并为一行,镜像容量就减少了一半。


结论:构建后务必检查“历史记录”!

请记住,Docker 镜像的特点是每条命令对应一个层

就像编写代码后进行测试一样,在构建镜像后,也请养成习惯性地运行 docker history --no-trunc 命令。当“这个 apt-get 为什么这么大?”、“为什么要特意将这个层分开?”之类的疑问接踵而至时,不知不觉中,您就会构建出非常轻量且高效的系统。

因为不必要的层是系统的敌人!


这篇文章对您有帮助吗? 如果有,请点赞!

也请阅读相关文章。

相关文章

Docker 共享内存(shm_size 和 ipc)完全理解

Docker: 无需网络共享,通过主机端口实现容器间通信

Docker 卷(Volume)复制,‘为什么’要这样做?