镜像从 1.2G 瘦到 120M:多阶段构建与层缓存

Docker · 3 分钟阅读· 266 次阅读

一个 Node 项目直接 FROM node 打完包 1.2G,其中 800M 是构建工具和缓存,运行时根本用不到。

多阶段构建把「构建环境」和「运行环境」分开,最终镜像只保留产物和运行时依赖。

先说结论

构建与运行分两阶段,运行阶段用精简基础镜像,层按变更频率排序。

  • builder 阶段装构建工具,运行阶段只拷产物
  • 优先 slim/distroless,注意 glibc 依赖
  • RUN 合并并用 && 清理缓存
  • .dockerignore 排除 node_modules 与 .git

先把问题说清楚

镜像层是叠加的:这一层删掉的文件,在上一层仍然占空间。所以 apt install 与 rm 必须写在同一个 RUN 里。

多阶段构建的另一个好处是:运行镜像里不含编译器和源码,攻击面显著变小。

具体怎么做

按顺序执行,每步都验证完再进下一步:

  • 拆分 builder 与 runtime 两个阶段
  • builder 里完成依赖安装与编译
  • runtime 只 COPY --from=builder 产物
  • 选择合适基础镜像:alpine(musl)或 debian-slim(glibc)
  • 按「最不常变」到「最常变」排序 COPY 与 RUN,利用缓存

参数与成本对照

把关键数值摊开,方便判断该选哪条路:

做法 体积影响 风险
多阶段构建 通常减 60%-90% 需确认运行依赖
alpine 基础镜像 减 100M 以上 musl 兼容性
合并 RUN + 清理 减缓存层 可读性下降
distroless 最小 无 shell,排障难

镜像从 1.2G 瘦到 120M

几个容易踩的坑

下面这些是最常见的错误做法:

  • alpine 里跑 glibc 编译的二进制:报 not found(其实缺动态库)
  • 先 COPY . 再 npm install:每次改代码都重装依赖
  • 忘记 .dockerignore:把 node_modules 和 .git 打进构建上下文
  • 在运行镜像里留 gcc 与源码

最后补两句

镜像瘦身是长期收益:拉取更快、漏洞更少、回滚更迅速。

工具是手段,理解机制才是能力。命令记不住没关系,知道「该看什么」就够了。

评论 (3)

阿
阿哲

照着做了一遍,确实少走弯路。第二步最容易漏,建议顺手加个检查清单。

南
南风

比「一键脚本」有用多了,知道为什么比知道怎么做重要。

K
Kevin

补充一句:不同版本行为会变,动手前先确认自己的版本号。

发表评论

验证码

提交后需站长审核,通过后才会显示。