news 2026/7/1 23:23:39

Docker镜像瘦身实战:从1G优化到50M的全过程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Docker镜像瘦身实战:从1G优化到50M的全过程

上次review同事的Dockerfile,一个Go服务的镜像打出来1.2G。

“这也太大了吧?”
“能跑就行呗。”

能跑是能跑,但每次部署拉镜像就要好几分钟,磁盘空间也吃不消。

花了半天时间优化,最后压到47M,记录一下过程。

问题分析

先看看原来的Dockerfile:

FROM golang:1.21 WORKDIR /app COPY . . RUN go mod download RUN go build -o main . EXPOSE 8080 CMD ["./main"]

看起来没毛病,但问题就出在这里。

镜像分析

# 查看镜像大小docker images|grepmyapp myapp latest abc1231.24GB# 用dive分析镜像层dive myapp:latest

分析结果:

  • golang:1.21基础镜像就有800MB
  • 加上源码、依赖、编译产物,妥妥过1G

优化方案

阶段一:多阶段构建

最立竿见影的优化:编译和运行分开。

# 阶段1:编译 FROM golang:1.21 AS builder WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -o main . # 阶段2:运行 FROM alpine:3.18 WORKDIR /app COPY --from=builder /app/main . EXPOSE 8080 CMD ["./main"]

效果:

docker images|grepmyapp myapp latest def456 28MB

直接从1.2G降到28MB,降了97%。

原理很简单:

  • 编译阶段用完整的golang镜像
  • 运行阶段只拷贝编译好的二进制文件
  • 用alpine替代完整系统,本身才5MB

阶段二:进一步压缩

28MB还能更小吗?可以。

# 阶段1:编译 FROM golang:1.21-alpine AS builder WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o main . # 阶段2:运行 FROM scratch COPY --from=builder /app/main /main EXPOSE 8080 ENTRYPOINT ["/main"]

改进点:

  • 编译阶段也用alpine,加快构建
  • 加上-ldflags="-s -w"去掉调试信息
  • scratch空镜像替代alpine
docker images|grepmyapp myapp latest ghi789 12MB

从28MB又降到12MB。

阶段三:UPX压缩(可选)

如果想更极致:

FROM golang:1.21-alpine AS builder # 安装upx RUN apk add --no-cache upx WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o main . RUN upx --best --lzma main FROM scratch COPY --from=builder /app/main /main EXPOSE 8080 ENTRYPOINT ["/main"]
docker images|grepmyapp myapp latest jkl0124.7MB

从12MB降到4.7MB。

但UPX有个问题:程序启动时需要解压,会增加启动时间。适合对镜像大小极度敏感但对启动速度不敏感的场景。

不同语言的优化策略

Java项目

Java比较麻烦,因为需要JVM。

# 阶段1:编译 FROM maven:3.9-eclipse-temurin-17 AS builder WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline COPY src ./src RUN mvn package -DskipTests # 阶段2:运行 FROM eclipse-temurin:17-jre-alpine WORKDIR /app COPY --from=builder /app/target/*.jar app.jar EXPOSE 8080 ENTRYPOINT ["java", "-jar", "app.jar"]

关键点:

  • 用jre-alpine替代完整JDK
  • 分离依赖下载和代码编译(利用缓存)

Java还可以用jlink自定义运行时:

FROM eclipse-temurin:17 AS jre-builder RUN jlink \ --add-modules java.base,java.logging,java.sql,java.naming,java.management \ --strip-debug \ --no-man-pages \ --no-header-files \ --compress=2 \ --output /javaruntime FROM alpine:3.18 COPY --from=jre-builder /javaruntime /opt/java COPY --from=builder /app/target/*.jar /app/app.jar ENV PATH="/opt/java/bin:${PATH}" ENTRYPOINT ["java", "-jar", "/app/app.jar"]

自定义的JRE只有几十MB,比完整JRE小很多。

Node.js项目

# 阶段1:构建 FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci --only=production COPY . . RUN npm run build # 阶段2:运行 FROM node:20-alpine WORKDIR /app COPY --from=builder /app/dist ./dist COPY --from=builder /app/node_modules ./node_modules COPY package*.json ./ USER node EXPOSE 3000 CMD ["node", "dist/index.js"]

Node项目主要是node_modules太大,优化方向:

  • 只装生产依赖
  • npm ci替代npm install
  • 考虑用esbuild打包成单文件

Python项目

# 阶段1:构建 FROM python:3.11-alpine AS builder RUN apk add --no-cache gcc musl-dev WORKDIR /app COPY requirements.txt . RUN pip install --user --no-cache-dir -r requirements.txt # 阶段2:运行 FROM python:3.11-alpine WORKDIR /app COPY --from=builder /root/.local /root/.local COPY . . ENV PATH=/root/.local/bin:$PATH EXPOSE 8000 CMD ["python", "app.py"]

缓存优化

镜像大小优化完了,顺便说说构建速度。

利用层缓存

# 好的写法:先复制依赖文件 COPY go.mod go.sum ./ RUN go mod download # 再复制源码 COPY . . RUN go build -o main .
# 差的写法:一起复制 COPY . . RUN go mod download RUN go build -o main .

好的写法只要依赖不变,go mod download这层就会走缓存。

.dockerignore

别忘了加.dockerignore:

.git .gitignore *.md .idea .vscode node_modules vendor *.log Dockerfile docker-compose.yml

不然COPY .会把一堆没用的东西复制进去。

安全优化

镜像瘦身的同时,顺便做一下安全加固。

非root用户

FROM alpine:3.18 RUN addgroup -S appgroup && adduser -S appuser -G appgroup WORKDIR /app COPY --from=builder /app/main . RUN chown -R appuser:appgroup /app USER appuser EXPOSE 8080 CMD ["./main"]

只读文件系统

# docker-compose.ymlservices:app:image:myappread_only:truetmpfs:-/tmp

扫描漏洞

# 用trivy扫描trivy image myapp:latest

选择维护良好的基础镜像,及时更新。

实际效果对比

优化阶段镜像大小构建时间
原始版本1.24GB45s
多阶段+alpine28MB38s
scratch+ldflags12MB35s
UPX压缩4.7MB52s

推荐停在"scratch+ldflags"这个阶段,性价比最高。

部署效率提升

镜像从1.2G降到12MB后:

  • 推送到仓库:从3分钟变成5秒
  • 拉取镜像:从2分钟变成2秒
  • 磁盘占用:一台机器能放更多版本

特别是跨区域部署的时候,镜像小就是快。我们有几个异地节点,之前用星空组网把节点连起来后,小镜像部署基本感觉不到延迟。

常见问题

Q1:scratch镜像没有shell怎么调试?

# 需要调试就用alpine FROM alpine:3.18 # 或者用busybox FROM busybox:latest

Q2:CGO_ENABLED=0是什么意思?

禁用CGO,编译成纯静态二进制。不依赖glibc,才能在scratch里跑。

如果你的代码用了CGO(比如用了sqlite3),就不能这样玩。

Q3:alpine里程序跑不起来?

可能是glibc的问题。alpine用的是musl。

解决方案:

  • 编译时用alpine对应的golang镜像
  • 或者静态编译

总结

Docker镜像瘦身的核心技巧:

技巧适用场景效果
多阶段构建所有项目立竿见影
小基础镜像大多数项目很明显
ldflags去调试信息Go项目减少30-50%
UPX压缩对大小极端敏感减少60-70%
.dockerignore所有项目加快构建

一句话总结:多阶段构建 + 合适的基础镜像,就能解决90%的问题。


有其他镜像优化技巧欢迎评论区分享~

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/1 5:14:13

Open-AutoGLM到底有多强?实测对比7大AutoML框架后的惊人结论

第一章:Open-AutoGLM开源网址 Open-AutoGLM 是一个面向自动化自然语言处理任务的开源框架,旨在简化大语言模型(LLM)在实际业务场景中的部署与调优流程。该项目由社区驱动,托管于主流代码托管平台,开发者可通…

作者头像 李华
网站建设 2026/6/16 19:39:03

Open-AutoGLM源码哪里下?一文解决99%的克隆与编译难题

第一章:Open-AutoGLM源码下载获取 Open-AutoGLM 的源码是参与其开发与本地部署的第一步。该项目托管在 GitHub 平台上,采用开源协议发布,支持社区协作与二次开发。环境准备 在下载源码前,请确保系统已安装以下基础工具&#xff1a…

作者头像 李华
网站建设 2026/6/26 11:09:02

原产地证明办理:所需材料与模板自动生成

原产地证明办理:所需材料与模板自动生成 在全球化贸易日益紧密的今天,一张薄薄的原产地证明(Certificate of Origin, COO)往往决定着一批货物能否顺利通关、享受关税减免,甚至影响整个订单的利润空间。对于外贸企业而言…

作者头像 李华
网站建设 2026/7/1 12:28:58

太流批了,实用工具,吾爱出品

今天给大家推荐三款工具,一款是Office文档图片导出工具,一款是环境变量添加工具,一款是GitHub下载工具,有需要的小伙伴可以下载收藏。 第一款:Office文档图导出工具 Office文档里图片批量导出其实可以用把文档后缀改成…

作者头像 李华
网站建设 2026/7/1 21:57:12

大模型自动化推理新突破,Open-AutoGLM在阿里云上的7大应用场景全曝光

第一章:大模型自动化推理新突破,Open-AutoGLM的诞生与演进随着大规模语言模型在自然语言处理领域的广泛应用,如何实现高效、自动化的推理流程成为研究热点。Open-AutoGLM应运而生,作为开源社区驱动的自动化推理框架,它…

作者头像 李华
网站建设 2026/6/30 21:56:55

esp32cam结合AI模型实现人形识别操作指南

用 ESP32-CAM 做本地人形识别?不联网也能玩转边缘 AI 你有没有遇到过这样的场景:家里装了摄像头,想看看门口有没有人,结果点开App要等十几秒加载画面——延迟高不说,还总担心视频被上传到云端,隐私“裸奔”…

作者头像 李华