Docker 构建速度总是上不来?先看看镜像层和上下文
很多人第一次写 Dockerfile 时,只关心“能不能构建成功”。等到项目稍微变大一点,就会开始抱怨镜像构建太慢。这个时候常见的反应是去找加速源、换基础镜像、甚至怀疑 Docker 本身不稳定。但经验告诉我,真正拖慢本地构建速度的,很多时候不是网络,而是你自己把缓存层次和构建上下文写得太随意了。
Docker 的缓存不是玄学,它很讲顺序
Dockerfile 里每一步都会形成一层,而缓存是否命中,很大程度取决于前面步骤有没有变化。也就是说,越稳定、越少改动的步骤,越应该放在前面;越频繁改动的业务代码,越应该尽量往后放。
这个原则听起来简单,但真正落地时很多人会把整个项目先 COPY . .,然后再安装依赖。结果任何一个源码小改动,都会把依赖安装层一起打废,构建时间自然越来越长。
依赖和源码,最好分开处理
无论是 Node、Go 还是其他语言,构建思路都差不多:先复制依赖描述文件,安装依赖,等依赖层稳定后,再复制真正经常改动的源码。这样你改一个业务文件,不会连带把最耗时的安装步骤一起重跑。
这不是“高级优化”,而是本地开发中非常朴素的时间管理。一天构建十几次的时候,这种差别会非常明显。
构建上下文太大,会让你在无形中浪费很多时间
另一个经常被忽略的点是构建上下文。你执行 docker build 时,当前目录里的内容会被打包发给 Docker 守护进程。如果日志目录、临时文件、依赖目录、编译产物全都跟着进去,即使 Dockerfile 没有直接使用它们,也会增加构建准备时间。
所以 .dockerignore 不是可有可无的配件,而是构建性能的一部分。哪些目录本来就不该参与镜像生成,应该尽早排除掉。
快速构建的核心,不是“写得巧”,而是“变化面小”
我后来越来越少追求那种看上去很炫的 Dockerfile 技巧,反而更关注一个很朴素的问题:这次代码改动,到底会让哪些层失效?如果答案是“几乎全部重来”,那问题就已经很明显了。
把变动频率低的部分稳定住,把真正常改的部分后置,再配合合理的 .dockerignore,本地构建体验通常就会改善很多。
理解层缓存之后,Dockerfile 会写得更像工程代码
镜像构建本来就不是一次性的动作,本地开发更不是只 build 一次就结束。只要你把缓存层和上下文这两个点想明白,Dockerfile 就不会再只是“能跑起来的脚本”,而会更像一份可维护的工程配置。
很多构建慢的问题,真不需要太复杂的解决方案,先把这两个基础点整理好,收益往往就已经很可观了。
