Go 多阶段构建
Go 的典型流水线:构建阶段带完整工具链,运行阶段用 distroless 或 scratch 这类极小基础镜像,只放静态编译出的二进制。配合目标平台参数做交叉编译,一份文件就能产出目标架构的产物。
基本信息
| 阶段划分 | 构建阶段用 golang 镜像,运行阶段用 distroless 或 scratch |
|---|---|
| 静态编译 | CGO_ENABLED=0 与目标平台参数一起传入 |
| 交叉编译 | TARGETOS 与 TARGETARCH 由构建平台自动注入 |
| 体积优化 | -trimpath 配合链接参数去掉路径与符号信息 |
| 依赖缓存 | 先复制 go.mod 与 go.sum 再下载模块 |
典型使用场景
- 服务镜像精简:只带二进制与证书,运行阶段不含工具链
- 多架构发布:构建阶段按目标平台交叉编译,运行阶段对应平台基础镜像
- 排查镜像内报找不到动态库:确认是否真的完成了静态编译
- 依赖层缓存优化:改业务代码不触发模块重新下载
- 排查启动缺少时区或证书:运行阶段按需补上相应文件
- 统一构建产物路径,方便 CI 归档与后续校验
示例
示例
FROM --platform=$BUILDPLATFORM golang:1.23 AS build
ARG TARGETOS TARGETARCH
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download # 依赖层单独缓存
COPY . .
RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH \
go build -trimpath -ldflags='-s -w' -o /out/app .
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=build /out/app /app
ENTRYPOINT ["/app"]两个阶段的分工
| 内容 | 构建阶段 | 运行阶段 |
|---|---|---|
| Go 工具链 | 有 | 无 |
| 模块缓存 | 有 | 无 |
| 静态二进制 | 产出 | 运行 |
| 证书与时区文件 | 系统自带 | 需要按需补齐 |
| 镜像体积 | 数百 MB 级 | 数十 MB 级或更小 |
先复制 go.mod 与 go.sum 再执行模块下载,改业务代码不会触发依赖重新下载,构建时间差别很明显。
运行在 distroless 或 scratch 上时容器里没有 shell,exec 进容器排查不可用,需要调试时临时换用带工具的基础镜像。
CGO_ENABLED=0 时域名解析走纯 Go 实现,个别依赖系统解析库的行为会变化,涉及解析的场景要在目标环境验证。
运行阶段用 nonroot 变体时启动身份已经是普通用户,注意二进制的可执行权限,以及日志、临时目录是否可写。