文档
目标
演示 Docker 多阶段构建(Multi-stage Build)技术,将一个 Go 应用从源码编译到最终运行镜像,压缩镜像体积从 ~800MB 降至 ~10MB。
完整代码
示例应用(main.go)
package main
import (
"fmt"
"net/http"
"os"
)
func handler(w http.ResponseWriter, r *http.Request) {
hostname, _ := os.Hostname()
fmt.Fprintf(w, "Hello from %s (Go %s)\n", hostname, r.URL.Path[1:])
}
func main() {
http.HandleFunc("/", handler)
fmt.Println("Server listening on :8080")
http.ListenAndServe(":8080", nil)
}
Dockerfile(多阶段构建)
# ============ 阶段1:编译 ============
FROM golang:1.22-alpine AS builder
# 设置国内代理(可选)
ENV GOPROXY=https://goproxy.cn,direct
WORKDIR /src
COPY main.go .
# 静态编译,剥离调试信息,减小体积
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app main.go
# ============ 阶段2:运行 ============
FROM scratch
# 从构建阶段复制编译好的二进制
COPY --from=builder /app /app
# scratch 镜像没有时区文件,需要的话可以复制
# COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
EXPOSE 8080
ENTRYPOINT ["/app"]
对比:单阶段构建(不推荐)
# 不推荐 — 镜像体积 ~800MB
FROM golang:1.22
WORKDIR /src
COPY main.go .
RUN go build -o /app main.go
EXPOSE 8080
ENTRYPOINT ["/app"]
Docker Compose 生产级编排
# docker-compose.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
image: my-go-app:latest
ports:
- "8080:8080"
environment:
- APP_ENV=production
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:8080/health"]
interval: 10s
timeout: 3s
retries: 3
deploy:
resources:
limits:
cpus: '0.5'
memory: 128M
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
运行步骤
# 1. 构建镜像
docker build -t my-go-app:multi -f Dockerfile .
# 2. 查看镜像大小
docker images | grep my-go-app
# my-go-app multi 8.5MB ← 多阶段构建
# my-go-app single 823MB ← 单阶段构建
# 3. 运行容器
docker run -d --name go-app -p 8080:8080 my-go-app:multi
# 4. 测试
curl http://localhost:8080/hello
# Hello from abc123def (Go hello)
# 5. 查看层信息
docker history my-go-app:multi
# 6. 使用 dive 分析镜像(需安装)
dive my-go-app:multi
预期输出
镜像大小对比:
单阶段镜像:823 MB
多阶段镜像:8.5 MB ← 减少 ~99%
docker history 输出:
IMAGE CREATED SIZE
<missing> 1 min ago 8.5MB ← 最终只有二进制
关键点
FROM scratch是空镜像,不含任何 OS 层,安全性极高CGO_ENABLED=0强制纯 Go 静态链接,不依赖 libc-ldflags="-s -w"去除符号表和调试信息COPY --from=builder跨阶段复制,构建依赖不进入最终镜像- 多阶段适合所有编译型语言:Go、Rust、C/C++、Java(jlink)