多阶段构建把服务镜像从 1.4G 压到 86M
CI 机器扩了两轮还是慢,一看产物:一个 Go 服务镜像 1.4G。打开 Dockerfile,原因很直白:把整个构建镜像连同源码、工具链一起打包进了最终镜像。多阶段构建改造后 86M。
原始写法的问题:基础镜像用 ubuntu:22.04,COPY 全部源码进去,在容器里 go build,最后也没清理任何中间产物。改造后:
# ---- 构建阶段 ----
FROM golang:1.22-alpine AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download # 依赖层单独缓存
COPY . .
RUN CGO_ENABLED=0 go build -ldflags '-s -w' -o /out/app ./cmd/api
# ---- 运行阶段 ----
FROM alpine:3.20
RUN adduser -D -u 10001 app
COPY --from=builder /out/app /usr/local/bin/app
USER 10001
ENTRYPOINT /usr/local/bin/app
三处关键:依赖先 COPY go.mod 再下载,代码变动不破坏缓存层;-ldflags -s -w 去掉调试符号,二进制小 30%;运行阶段不带编译器和源码。
再进一步把 alpine 换 distroless 或 scratch,能到 30M 以内,但要接受没有 shell 进容器排查的不便,我们保守用了 alpine。
前端 Node 项目同理:构建阶段跑 npm ci 和 npm run build,运行阶段只把 dist 拷进 nginx 静态目录,从 1.1G 降到 25M。
镜像瘦身的收益不只是存储:拉取快、攻击面小、启动快。改造完,发布单次镜像传输时间从 90 秒降到 12 秒,几百个服务的累积效应非常可观。顺手在 CI 里加了卡点:镜像超过 300M 直接失败,防止它慢慢胖回去。
补充一个容易被忽略的收益维度:镜像变小后,仓库的存储和带宽费用也降了,我们 Harbor 集群的月度出口流量降了四成,这笔账多数团队从没算过。镜像优化是那种每个环节都受益的改造,值得作为新人练手项目,产出立竿见影。
镜像治理的最后一步是基础镜像的统一维护:团队公共基础镜像每月定期重建,安全补丁自动合入,业务镜像跟着触发重建,漏洞存量长期压在低位。有同学担心重建影响稳定性,实践下来只要基础镜像版本号固定、变更内容只有补丁,风险完全可控。镜像这件事,管住入口比事后清理便宜太多,一个卡点加一条保留策略,就够大多数团队用好几年。
- 点赞
- 收藏
- 关注作者
评论(0)