用多阶段构建给 Docker 镜像瘦身:从 1GB 到 100MB 的实战做法

举报
yd_225891185 发表于 2026/11/04 15:08:36 2026/11/04
【摘要】 刚学 Docker 的时候,我写过一个很典型的 Dockerfile:以 Python 官方镜像为底,先装编译工具,再 pip install 一堆依赖,最后把源码复制进去。镜像跑起来没问题,但一查大小傻眼了:接近 1GB。推镜像慢、拉镜像慢,服务器磁盘也吃不消。后来改成多阶段构建,同一个应用直接瘦到 100MB 出头,部署速度肉眼可见地变快。这篇文章就讲清楚多阶段构建到底在干什么,以及新手...

刚学 Docker 的时候,我写过一个很典型的 Dockerfile:以 Python 官方镜像为底,先装编译工具,再 pip install 一堆依赖,最后把源码复制进去。镜像跑起来没问题,但一查大小傻眼了:接近 1GB。推镜像慢、拉镜像慢,服务器磁盘也吃不消。后来改成多阶段构建,同一个应用直接瘦到 100MB 出头,部署速度肉眼可见地变快。这篇文章就讲清楚多阶段构建到底在干什么,以及新手最容易踩的几个坑。

一、镜像为什么会变胖

Docker 镜像是分层叠加的,每一条 RUN、COPY、ADD 都会留下一层。很多人以为最后删掉文件就瘦了,其实不是:你在某一层里下载的编译工具、中间产物,即使在后面一层里 rm 掉了,也只是被新的一层“遮住”,旧层的数据还躺在镜像里,占用的空间一点没少。

典型的发胖组合是这样的:基础镜像本身几百 MB,加上 gcc、make、各种头文件,再加上 pip 缓存和测试依赖。运行期其实只需要解释器、第三方库和自己的代码,编译工具在运行期完全是多余的。把构建期和运行期混在一个镜像里,胖是必然结果。

二、多阶段构建的核心思路

多阶段构建就是在一个 Dockerfile 里写多个 FROM,每个 FROM 开启一个新阶段。前面的阶段负责编译、安装、打包,后面的阶段只从前面挑需要的成品复制过来,最后的镜像里就只有运行期真正需要的东西。

一个 Python 项目的写法大概是这样:第一阶段用带编译工具的镜像,装好依赖并生成 wheel;第二阶段用精简的 slim 镜像,只复制安装好的依赖和源码。构建工具、缓存、源码里的测试文件都留在第一阶段,不会进入最终镜像。最终镜像只包含解释器、依赖和代码,自然就瘦了。

对 Go、Rust 这类编译型语言效果更明显:第一阶段编译出一个静态二进制,第二阶段用 scratch 或者 distroless 这类极小底座,只放一个二进制文件,最终镜像可以只有十几 MB。当然,极小底座调试起来不方便,生产环境用之前要确认日志、时区、证书这些运行时依赖有没有带齐。

三、实战中要注意的四个细节

第一,依赖安装要分层。先复制 requirements.txt 再 pip install,最后才复制源码。这样源码改动时,依赖层可以复用缓存,构建速度快很多。反过来如果先复制全部源码再装依赖,改一行代码就要重新装一遍依赖,又慢又浪费。

第二,记得关掉缓存。pip install 加上 --no-cache-dir,apt-get 安装后顺手清理 /var/lib/apt/lists,能省下不少空间。这些缓存只在构建时有用,进了最终镜像就是纯负担。

第三,.dockerignore 一定要写。把 .git、pycache、测试数据、本地虚拟环境排除在构建上下文之外。否则每次构建都要把一堆无关文件传给 Docker 守护进程,构建慢,还可能一不小心把本地密钥、配置文件打进镜像。

第四,别为了瘦而瘦。alpine 镜像确实小,但 musl 和 glibc 的差异偶尔会让某些依赖出问题;scratch 镜像连 shell 都没有,出问题连进容器排查都做不了。新手建议先用官方 slim 镜像做第二阶段,稳定跑通后再根据需要尝试更小的底座。

四、怎么验证瘦身效果

改完 Dockerfile 之后,用 docker images 对比一下前后大小,再用 docker history 看看每一层占了多少空间。history 会清楚地告诉你哪一层最胖,瘦身有没有做到位。如果某一层还是异常大,顺着这一层对应的指令往回查,多半是缓存没清或者不该进镜像的文件被复制进来了。

另外可以跑一次 dive 这类工具逐层检查,虽然不是必需的,但第一次做瘦身时看一眼会很有帮助:哪些文件进了最终镜像一目了然。

结语

多阶段构建不是什么高深技巧,它只是把“构建”和“运行”分开:构建阶段要什么给什么,运行阶段只拿成品。下次写 Dockerfile 时,先问自己一句:运行期真的需要这个吗?如果答案是否定的,就让它留在构建阶段。镜像瘦下来之后,推送、拉取、扩容都会轻松很多,这对个人服务器和生产环境都是一笔实实在在的收益。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。