容器镜像瘦身实录:多阶段构建把 800MB 压到 90MB

举报
yd_252559278 发表于 2026/11/06 10:06:34 2026/11/06
【摘要】 上周把一个 Go 写的内部工具做成容器镜像推到仓库,CI 流水线拉取镜像时每次都要等两分多钟,部署到新节点更是慢得明显。查了一下镜像大小:800MB。一个只有十几兆二进制的服务,镜像却快 1GB,显然哪里不对。最后用多阶段构建把镜像压到了 90MB,拉取时间从两分多钟降到十几秒。这次把排查和改造过程记录下来。一、镜像为什么会胖镜像是一层一层叠上去的,每一层都算体积。最常见的“胖”有三个来源。...

上周把一个 Go 写的内部工具做成容器镜像推到仓库,CI 流水线拉取镜像时每次都要等两分多钟,部署到新节点更是慢得明显。查了一下镜像大小:800MB。一个只有十几兆二进制的服务,镜像却快 1GB,显然哪里不对。最后用多阶段构建把镜像压到了 90MB,拉取时间从两分多钟降到十几秒。这次把排查和改造过程记录下来。

一、镜像为什么会胖

镜像是一层一层叠上去的,每一层都算体积。最常见的“胖”有三个来源。

第一,基础镜像选得太重。为了图省事直接用完整版的语言镜像,里面自带编译器、调试工具和一大堆系统包,光底座就有六七百兆。运行时其实只需要一个能跑二进制的环境。

第二,构建产物和构建工具混在同一层。源码、依赖缓存、编译中间文件都留在镜像里,而运行时根本用不到它们。

第三,历史层没清理。RUN 里下载的安装包、解压的临时文件,即使后面删掉了,也只是又叠了一层“删除标记”,底层那份数据还在镜像里占着地方。

二、多阶段构建的思路

多阶段构建就是把“编译”和“运行”拆成两个阶段:第一个阶段用重型镜像编译出二进制,第二个阶段只把编译好的二进制拷进一个极小的基础镜像。最终镜像只含运行阶段的内容,编译工具、源码和中间文件一个都不带。

原理很简单,但效果直接:编译环境再重也不影响最终体积,因为它根本不会进最终镜像。

三、实操:改造 Dockerfile

改造前是单阶段写法,编译和运行都在同一个镜像里:

FROM golang:1.22
WORKDIR /app
COPY . .
RUN go build -o server ./cmd/server
CMD ["./server"]

改造后拆成两个阶段:

FROM golang:1.22 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o server ./cmd/server

FROM alpine:3.19
WORKDIR /app
COPY --from=builder /app/server ./server
EXPOSE 8080
CMD ["./server"]

有几个细节值得注意。先单独复制 go.mod 和 go.sum 再下载依赖,这样只有依赖变化时才重新下载,改业务代码不会让依赖层失效,构建缓存命中率明显提高。CGO_ENABLED=0 产出静态链接的二进制,放到不带 C 库的精简基础镜像里也能跑。COPY --from=builder 只取编译产物,源码和构建缓存都留在第一个阶段里。

四、效果验证

改造后重新构建并对比:镜像从 800MB 降到 90MB,缩小了将近九成。CI 拉取时间从两分多钟降到十几秒,新节点扩容时镜像拉取不再是瓶颈。安全扫描的漏洞数也少了一大截——镜像里少了编译器和几百个系统包,攻击面自然小了。

验证时别只看大小,还要确认服务真的能跑:进容器看进程是否正常启动、调一次健康检查接口、再看日志有没有缺失动态库之类的报错。静态编译这一步没做对的话,服务会在启动时直接报错,排查起来反而费时间。

五、几个容易踩的坑

一是基础镜像不是越小越好。极简镜像缺时区数据、缺 CA 证书,服务调外部 HTTPS 接口或打带时区的时间戳时会出问题。缺什么就显式装什么,别等线上报错再补。

二是构建缓存失效。COPY . . 写在依赖下载之前,改一行代码就让依赖层失效,构建时间反而变长。先拷依赖文件、下载依赖,再拷源码,这个顺序不能乱。

三是忽略多架构。团队里有人用 ARM 机器本地构建,推上去的镜像在 x86 节点上跑不起来。CI 里固定用 docker buildx 指定目标平台,本地构建和线上构建用同一套参数,避免架构不一致。

六、小结

镜像瘦身不是为了数字好看,而是直接换来更快的部署和更小的攻击面。记住三件事:编译和运行分阶段、只把运行必需品拷进最终镜像、构建顺序先依赖后源码。按这个思路改造,大多数服务的镜像都能瘦掉八成以上,剩下的再按实际缺失补时区和证书这类运行时依赖即可。

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

评论(0)

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

全部回复

上滑加载中

设置昵称

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

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

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