Docker 镜像瘦身的四个层次:从 1.2G 到 200M

举报
yd_242458115 发表于 2026/10/27 00:02:05 2026/10/27
【摘要】 镜像臃肿不只是磁盘问题,还拖慢部署、扩大攻击面。这篇按见效顺序讲四个层次:选基础镜像、合并层、多阶段构建、以及清理构建缓存。

一个 Node.js 服务的镜像做到了 1.2G,这在生产里很常见。拉取慢、推送慢、扫描慢,出了漏洞还得把所有层重新打一遍。

镜像瘦身有明确的方法论。按见效快慢排四个层次,做完通常能压到 200M 以内。

第一层:换基础镜像(收益最大,改动最小)

体积的大头几乎总在基础镜像上。

基础镜像 | 典型大小

node:latest | 1.1 GB

node:18 | 1.0 GB

node:18-slim | 220 MB

node:18-alpine | 130 MB

从 node:18 换成 node:18-slim,一行改动,体积掉 80%。

但有代价,要提前想清楚:

  • alpine 用的是 musl libc,不是 glibc。 有些 npm 包带原生扩展(bcrypt、sharp、canvas),在 alpine 上要重新编译,装不上就是一堆报错。
  • alpine 缺一些常见工具(bash、curl、git)。构建脚本里用了就会失败。
  • slim 版本删了文档和部分工具,但保留了 glibc,兼容性风险小得多。

实践建议: 先试 slim,需要极致体积再考虑 alpine,并且在 CI 里跑一遍完整启动测试再合并。

第二层:合并 RUN 指令(减少层数)

Dockerfile 的每条 RUN 都是一层。每层都带着上一层的完整快照,所以:

# 差:三层,且 apt 缓存被固化在镜像里
RUN apt-get update
RUN apt-get install -y curl
RUN rm -rf /var/lib/apt/lists/*
# 好:一层,缓存和安装在同一层里清理掉
RUN apt-get update && \
    apt-get install -y --no-install-recommends curl && \
    rm -rf /var/lib/apt/lists/*

关键在于:安装和清理必须在同一个 RUN 里。分成两条 RUN,即使后面删了文件,被删的内容仍然存在于前一层里,镜像体积不会变小。

--no-install-recommends 也很关键,它跳过推荐依赖,通常能省几十 MB。

第三层:多阶段构建(把构建工具留在构建阶段)

这是最能体现“专业度”的一步。核心思路:构建需要的东西,运行时一个都不需要。

# 构建阶段:带着全套工具
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# 运行阶段:只拷产物
FROM node:18-slim
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package.json ./
CMD ["node", "dist/index.js"]

构建阶段的编译器、源码、测试依赖全部不会进入最终镜像。

进一步:只装生产依赖。

RUN npm ci --omit=dev

--omit=dev 是 npm 7+ 的写法,老版本是 --production。这一条通常能砍掉一半的 node_modules。

如果是 Go、Rust 这类编译型语言,多阶段的效果更夸张——最终镜像可以只有几 MB。

第四层:细节清理(榨最后一点)

做到这里通常已经 200M 上下了。还有几个地方能再挤一挤:

用 .dockerignore。 不加这个,COPY . . 会把 .git、node_modules、日志、测试数据全塞进去。一个 .git 目录几 MB 到几百 MB 不等。

.git
node_modules
npm-debug.log
*.md
测试数据目录

别把包管理器的缓存留在镜像里。 npm 的缓存默认在 ~/.npm,体积可能上百 MB:

RUN npm ci --omit=dev && npm cache clean --force

注意用户权限。 顺手提一句,很多教程的镜像用 root 跑进程,这是安全问题不是体积问题,但既然在改 Dockerfile,一起修掉:

RUN useradd -r -u 10001 appuser
USER appuser

怎么看瘦身效果

先看现状:

docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"

想知道每一层占多少(找出到底哪一层最胖):

docker history --no-trunc <镜像名> | head -20

对比前后:

docker build -t myapp:before .
# 改 Dockerfile
docker build -t myapp:after .

一个真实的顺序建议

不要一次全改,否则出问题不知道是哪一步导致的。按这个顺序,每步验证一次:

1. 换基础镜像(slim)→ 验证启动

2. 加 .dockerignore → 验证构建

3. 合并 RUN、加 --no-install-recommends → 验证

4. 改多阶段构建 → 验证

5. 只留生产依赖 → 验证

每一步都是一次提交,出问题能立刻定位。

小结

记住一句话:镜像里每一层都是不可变的历史,删东西不会让历史变小。

所以瘦身不是“事后删”,而是“一开始就不放进去”。多阶段构建之所以有效,正是因为它让不需要的东西从来没进过最终镜像。

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

评论(0)

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

全部回复

上滑加载中

设置昵称

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

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

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