跳到主要内容

Docker 与容器化

问题

Go 项目如何进行 Docker 容器化?为什么 Go 的 Docker 镜像特别小?

答案

多阶段构建(标准方案)

# 阶段 1:编译
FROM golang:1.22-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 /app/server ./cmd/server

# 阶段 2:运行(最小镜像)
FROM alpine:3.19
RUN apk --no-cache add ca-certificates tzdata
COPY --from=builder /app/server /server
EXPOSE 8080
CMD ["/server"]
镜像大小
golang:1.22~800MB
alpine + 二进制~15MB
scratch + 二进制~8MB
distroless~10MB
CGO_ENABLED=0

设为 0 产生纯静态二进制,不依赖 libc,可以在 scratch 空镜像上运行。如果用了 CGO(如 SQLite),需要用 alpine 镜像。

编译优化

# -s 去掉符号表,-w 去掉调试信息 → 减小约 30% 体积
go build -ldflags="-s -w" -o server

# 注入版本信息
go build -ldflags="-s -w -X main.version=1.0.0 -X main.buildTime=$(date -u +%Y%m%d%H%M%S)"

Docker Compose 开发环境

# docker-compose.yml
services:
app:
build: .
ports: ["8080:8080"]
depends_on: [mysql, redis]
environment:
- DB_HOST=mysql
- REDIS_ADDR=redis:6379

mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: app
ports: ["3306:3306"]

redis:
image: redis:7-alpine
ports: ["6379:6379"]

常见面试问题

Q1: 为什么 Go 的 Docker 镜像可以那么小?

答案:Go 编译为单个静态二进制,不需要 JVM、Node.js 等运行时。设 CGO_ENABLED=0 后甚至不依赖 libc,可以直接放在空镜像(scratch)上运行。

Q2: scratch vs alpine vs distroless?

答案

基础镜像大小特点
scratch0无壳、无工具,最小
alpine~5MB有 Shell,可 debug
distroless~2MBGoogle 出品,无 Shell 但有 CA 证书

生产推荐 distrolessalpine,方便排查问题。纯安全考虑用 scratch

相关链接