Docker 镜像构建为什么慢:一次 .dockerignore 排查
上周把一个 Python 小服务从本地搬到云主机,docker build 的第一行日志永远是:
改一行 print 也要等 70 秒。查下来既不是网络问题,也不是机器太弱(2C4G),而是构建上下文被塞满了不该进去的东西:项目根目录下躺着 .venv/(三百多 MB 的虚拟环境)、node_modules/、.git/,还有几份训练用的数据集。
默认情况下,Docker 会把构建目录里所有文件打包成 build context 发给 daemon,再逐层计算缓存。文件越多,发送越慢,缓存也越容易被无关文件的变化冲掉。
更要紧的是缓存判定:COPY . . 这一层的输入是「整个上下文的内容签名」,只要上下文中任意文件变动——包括 .git 里 ref 的变化——这一层就失效,后面的 RUN pip install 全部重跑。于是就出现「我明明没动依赖,它又把依赖装了一遍」。
解法是在项目根目录加一个 .dockerignore:
改完再看一眼上下文大小:
同一台机器、同一个项目的对比:
•上下文:412.6MB → 1.8MB
•首次构建:3 分 12 秒 → 48 秒
•只改一行业务代码:70 秒 → 6 秒(依赖层命中缓存)
顺手把 Dockerfile 里 COPY 的顺序也理了一下:
先 COPY 依赖清单再装依赖,最后 COPY 源码。改代码不碰依赖层,缓存能一直命中。
两点踩坑提醒:
1).dockerignore 里写 data/ 只忽略该目录及其子项;想递归匹配扩展名要用 **/*.log 这种写法,写 *.log 不保证覆盖子目录。
2).git 一定要排除,不只是为了体积——CI 里 checkout 之后 .git 的 index、远端 ref 每次都可能变,会让 COPY . . 的缓存频繁失效,这是「本地快、流水线慢」最常见的原因之一。
另外别把 requirements.txt、src/ 这类构建必需路径写进 .dockerignore,一旦写错,报错发生在 COPY 阶段,很容易误判成路径写错,排查方向会跑偏。
改完之后这个镜像再没出现过构建超时,机器还是那台 2C4G。
- 点赞
- 收藏
- 关注作者
评论(0)