Go 语言完全指南 / 27 - 容器化:多阶段构建、scratch 镜像、CGO 交叉编译
27 - 容器化
27.1 基本 Dockerfile
# 最简单的 Dockerfile
FROM golang:1.24-alpine
WORKDIR /app
# 先复制依赖文件(利用 Docker 缓存)
COPY go.mod go.sum ./
RUN go mod download
# 复制源代码
COPY . .
# 编译
RUN go build -o server ./cmd/server
# 暴露端口
EXPOSE 8080
# 运行
CMD ["./server"]
⚠️ 问题:这个镜像包含完整的 Go 工具链,体积约 300MB+。
27.2 多阶段构建(Multi-stage Build)
# ===== 阶段 1: 编译 =====
FROM golang:1.24-alpine AS builder
WORKDIR /app
# 安装 git(go mod 需要)
RUN apk add --no-cache git
COPY go.mod go.sum ./
RUN go mod download
COPY . .
# 编译优化
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build -ldflags="-s -w" -o server ./cmd/server
# ===== 阶段 2: 运行 =====
FROM alpine:3.19
# 安装必要的系统依赖
RUN apk add --no-cache ca-certificates tzdata
# 设置时区
ENV TZ=Asia/Shanghai
WORKDIR /app
# 从编译阶段复制二进制
COPY --from=builder /app/server .
COPY --from=builder /app/config ./config
# 非 root 用户运行
RUN adduser -D -s /bin/sh appuser
USER appuser
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget -qO- http://localhost:8080/health || exit 1
ENTRYPOINT ["./server"]
27.3 scratch 镜像
scratch 是空镜像,最终镜像只包含你的二进制文件。
FROM golang:1.24-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build -ldflags="-s -w" -o server ./cmd/server
# 使用 scratch(空镜像)
FROM scratch
# 复制 SSL 证书(HTTPS 需要)
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
# 复制时区数据
COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
# 复制二进制
COPY --from=builder /app/server /server
EXPOSE 8080
ENTRYPOINT ["/server"]
| 基础镜像 | 大小 | Shell | 包管理器 | 适用场景 |
|---|---|---|---|---|
golang:1.24 | ~800MB | ✅ | ✅ | 仅编译阶段 |
alpine:3.19 | ~7MB | ✅ | ✅ | 需要调试 |
distroless | ~20MB | ❌ | ❌ | 安全生产 |
scratch | 0MB | ❌ | ❌ | 极简生产 |
27.4 CGO 交叉编译
# 禁用 CGO(纯 Go 编译,支持交叉编译)
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o server .
# 多平台编译
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o server-arm64 .
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -o server-mac .
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o server.exe .
# 需要 CGO 时(如使用 SQLite)
# 需要安装交叉编译工具链
# apt install gcc-aarch64-linux-gnu
CC=aarch64-linux-gnu-gcc CGO_ENABLED=1 GOOS=linux GOARCH=arm64 go build -o server-arm64 .
Dockerfile 多平台构建
FROM --platform=$BUILDPLATFORM golang:1.24-alpine AS builder
ARG TARGETOS
ARG TARGETARCH
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
go build -ldflags="-s -w" -o server ./cmd/server
FROM alpine:3.19
COPY --from=builder /app/server /server
ENTRYPOINT ["/server"]
# 构建多平台镜像
docker buildx build --platform linux/amd64,linux/arm64 -t myapp:latest .
27.5 Docker Compose
# docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "8080:8080"
environment:
- DATABASE_URL=postgres://user:pass@db:5432/mydb?sslmode=disable
- REDIS_URL=redis://redis:6379
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
restart: unless-stopped
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: mydb
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user"]
interval: 5s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
volumes:
- redisdata:/data
volumes:
pgdata:
redisdata:
27.6 .dockerignore
# .dockerignore
.git
.github
.vscode
*.md
Makefile
docker-compose.yml
Dockerfile
.env
tmp/
vendor/
public/
coverage.out
27.7 优化技巧
# Makefile
APP_NAME := myapp
VERSION := $(shell git describe --tags --always)
BUILD_TIME := $(shell date -u '+%Y-%m-%dT%H:%M:%SZ')
LDFLAGS := -s -w \
-X main.version=$(VERSION) \
-X main.buildTime=$(BUILD_TIME)
.PHONY: build docker
build:
CGO_ENABLED=0 go build -ldflags="$(LDFLAGS)" -o bin/$(APP_NAME) ./cmd/server
docker:
docker build \
--build-arg VERSION=$(VERSION) \
--build-arg BUILD_TIME=$(BUILD_TIME) \
-t $(APP_NAME):$(VERSION) .
docker-run:
docker-compose up -d
docker-stop:
docker-compose down
🏢 业务场景
- 微服务部署:每个服务一个 Docker 镜像
- Kubernetes:Docker 镜像部署到 K8s
- CI/CD:Docker 构建自动化
- 本地开发:docker-compose 一键启动所有依赖