⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 http://www.iocoder.cn/Fight/Dockerjingxiangshoushencong143Gdao224MB/ 「芋道源码」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

瘦身路线总览:四步压掉 98.5%

镜像大小直接影响 CI/CD 速度、镜像仓库带宽、云部署成本——尤其在 Kubernetes 频繁拉镜像的场景下,每多 100M 都是真金白银。

下面用一个 ReactJS 应用做演示,镜像从 1.43GB 一路压到 22.4MB——压缩率 98.5%。每一步只改一件事,一目了然:

关于镜像版本:本文 Dockerfile 全部用 node:20 / node:20-alpine(2026 年 LTS)。下面的体积截图是当年 demo 录制时用 node:12 实拍——绝对数字会有 5%-15% 差异,但每一步的相对压缩率(-60% / -83% / -77%)几乎不变,思路完全通用。

Step 1:建一个空的 React 项目

用 CRA 脚手架快速起一个项目(一切瘦身的起点都是要有”东西”先跑起来):

npx create-react-app react-demo

进入目录、装依赖、本地跑起来:

cd react-demo
yarn install
yarn start

浏览器访问 http://localhost:3000 看到默认欢迎页就 OK:

Step 2:第一版 Dockerfile(基线 1.43GB)

最直白的写法——直接把项目塞进 node:20 镜像里跑:

FROM node:20
WORKDIR /app
COPY package.json ./
RUN yarn install
COPY . .
EXPOSE 3000
CMD ["yarn", "start"]

构建并查看:

docker build -t react-demo .
docker images

结果:1.43GB——一个静态页面背了 1.4G 的壳。

跑起来验证一下能用:

docker run --rm -it -p 3000:3000/tcp react-demo:latest

为什么这么大? node:20 默认基于 Debian(Bookworm),自带的系统组件 + 全套 npm 依赖 + 项目源代码——三样东西全在最终镜像里。

Step 3:换 Alpine 基础镜像(→ 580MB)

第一刀砍系统层:node:20-alpine 用 Alpine Linux 替代 Ubuntu,体积小一个数量级。

各种 Node 基础镜像大小对比:

只改 FROM 行:

FROM node:20-alpine
WORKDIR /app
COPY package.json ./
RUN yarn install
COPY . .
EXPOSE 3000
CMD ["yarn", "start"]

重新构建:

结果:580MB——直接砍掉 60%。但还能更狠。

Step 4:多阶段构建丢弃源码与依赖(→ 97.5MB)

上一版镜像里还带着完整的源代码和 node_modules——但生产运行只需要 build 产物(/build 目录里的 html/js/css),其它都是负重。

多阶段构建(Multi-stage Build)的核心思想:构建用一个镜像,运行用另一个镜像,构建产物从前者复制到后者。

# STAGE 1: 构建阶段
FROM node:20-alpine AS build
WORKDIR /app
COPY package.json ./
RUN yarn install
COPY . /app
RUN yarn build

# STAGE 2: 运行阶段
FROM node:20-alpine
WORKDIR /app
RUN npm install -g webserver.local
COPY --from=build /app/build ./build
EXPOSE 3000
CMD webserver.local -d ./build

第二阶段是干净的 node:20-alpine——只装一个轻量 webserver,再把第一阶段的 build 产物拷过来,源码和 node_modules 一概不要。

构建:

结果:97.5MB——再砍 83%。漂亮。

Step 5:用 Nginx 做静态资源服务(→ 22.4MB)

最后一步:别再用 Node 跑静态资源了——React 编译产物是纯静态文件,Nginx 是这种场景下绝对的最优解:

  • Nginx 处理静态资源比 Node 高效得多
  • Nginx 镜像比 Node 小一大截
  • 一份 Dockerfile 同时拿到性能和体积

最终版:

# STAGE 1: 构建
FROM node:20-alpine AS build
WORKDIR /app
COPY package.json ./
RUN yarn install
COPY . /app
RUN yarn build

# STAGE 2: 运行
FROM nginx:stable-alpine
COPY --from=build /app/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

构建:

结果:22.4MB——从 1.43GB 一路下来,体积压缩了 98.5%,性能反而更好。

启动验证:

docker run --rm -it -p 3000:80/tcp react-demo:latest

注意端口映射:Nginx 容器内部默认监听 80,所以宿主机 3000 → 容器 80。

总结:三招就能把任何 NodeJS 项目压到几十 MB

回头看 1.43GB → 22.4MB 这条路,真正起决定作用的就三件事——次序也很重要,按这个顺序砍最省力:

招式 解决的问题 砍掉的体积 心智成本
1. Alpine 替 Ubuntu 系统层冗余 1.43GB → 580MB(-60%) 改一行 FROM 即可
2. 多阶段构建 把构建依赖留在最终镜像 580MB → 97.5MB(-83%) 多写一个 STAGE,思路换一下
3. 静态资源换 Nginx 用错了运行时 97.5MB → 22.4MB(-77%) 第二阶段换基础镜像

四条放在哪都不会错的工程原则:

  • 运行时镜像 ≠ 构建时镜像——别把构建工具链带进生产镜像
  • 基础镜像优先 Alpine 系列——除非业务需要 glibc,否则 Alpine 永远是首选
  • 静态资源用 Nginx,不要用 Node——Node 跑 web 服务给静态文件是烧 CPU 烧内存
  • CI 跑 docker images 看体积告警——超过预期值就回头查,别等仓库账单提醒你

这套思路不局限于 React——任何 NodeJS / 前端构建产物 / 单一可执行文件的服务都适用。Java 项目也类似:构建用 maven 镜像,运行用 distroless / alpine + JRE,容易压到 80MB 量级。

最后说一句:CI/CD 时间和镜像仓库账单是真金白银——花一个小时优化 Dockerfile 的 ROI,比加一个月班都高。

文章目录
  1. 1. 瘦身路线总览:四步压掉 98.5%
  2. 2. Step 1:建一个空的 React 项目
  3. 3. Step 2:第一版 Dockerfile(基线 1.43GB)
  4. 4. Step 3:换 Alpine 基础镜像(→ 580MB)
  5. 5. Step 4:多阶段构建丢弃源码与依赖(→ 97.5MB)
  6. 6. Step 5:用 Nginx 做静态资源服务(→ 22.4MB)
  7. 7. 总结:三招就能把任何 NodeJS 项目压到几十 MB