Py学习  »  docker

Docker 镜像 1.43G 拉到怀疑人生?这 4 步压到 22MB,部署时间起飞

Java基基 • 3 月前 • 228 次点击  

👉 这是一个或许对你有用的社群

🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入芋道快速开发平台知识星球。下面是星球提供的部分资料: 

👉这是一个或许对你有用的开源项目

国产Star破10w的开源项目,前端包括管理后台、微信小程序,后端支持单体、微服务架构

RBAC权限、数据权限、SaaS多租户、商城、支付、工作流、大屏报表、 ERPCRMAI大模型、IoT物联网等功能:

  • 多模块:https://gitee.com/zhijiantianya/ruoyi-vue-pro
  • 微服务:https://gitee.com/zhijiantianya/yudao-cloud
  • 视频教程:https://doc.iocoder.cn
【国内首批】支持 JDK17/21+SpringBoot3、JDK8/11+Spring Boot2双版本 

瘦身路线总览:四步压掉 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%)几乎不变 ,思路完全通用。

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 视频教程:https://doc.iocoder.cn/video/

Step 1:建一个空的 React 项目

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

npx create-react-app react-demo

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

cd react-demo
yarn install
yarn start

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

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/yudao-cloud
  • 视频教程:https://doc.iocoder.cn/video/

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
EXPOSE3000
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,比加一个月班都高。



欢迎加入我的知识星球,全面提升技术能力。

👉 加入方式,长按”或“扫描”下方二维码噢

星球的内容包括:项目实战、面试招聘、源码解析、学习路线。

文章有帮助的话,在看,转发吧。

谢谢支持哟 (*^__^*)

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/195979