Files
cs-note/hhs/MS/05-部署运维/01-容器化/01-Dockerfile最佳实践.md
T
2026-05-24 11:42:38 +08:00

4.7 KiB
Raw Blame History

tags, create time
tags create time
docker
container
dockerfile
buildkit
image-optimization
2026-05-18 00:45

Dockerfile 最佳实践 — 教程

概述

Dockerfile 是微服务交付的标准起点。本文档从多阶段构建出发,详解层缓存技巧、BuildKit 高级特性和常见问题排查。更多话题:多架构构建见 ../01-容器化/02-多架构构建,镜像安全见 ../01-容器化/03-镜像安全。

Go 多阶段构建(推荐)

# ========== 阶段 1: 构建 ==========
FROM golang:1.22-alpine AS builder

RUN apk --no-cache add git ca-certificates

WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download

COPY . .
ARG LDFLAGS="-s -w -extldflags '-static'"
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags "$LDFLAGS" -o server .

# ========== 阶段 2: 运行时 ==========
FROM alpine:latest

RUN apk --no-cache add ca-certificates tzdata && \
    cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
    echo "Asia/Shanghai" > /etc/timezone

RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

WORKDIR /app
COPY --from=builder /app/server .

EXPOSE 8080

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD wget -qO- http://localhost:8080/healthz || exit 1

CMD ["./server"]

[!question] 为什么要用多阶段构建?

单阶段构建中,编译工具和源码都在最终镜像里——一个 Go 项目的镜像轻松超过 800MB。多阶段构建把构建和运行拆成两个独立的镜像层,第二阶段只 COPY 二进制文件,最终镜像缩小到十几 MB。

关键优化点速查

优化项 方法 效果
多阶段构建 编译和运行分离 镜像从 800MB → 15MB
alpine 基础镜像 替代 debian/ubuntu 减小体积
非 root 运行 USER appuser 安全合规
静态链接 CGO_ENABLED=0 不依赖系统库
layer cache go.mod 先 COPY CI 加速构建
健康检查 HEALTHCHECK 指令 K8s 原生支持

.dockerignore — 别忽略的文件

.git
.gitignore
*.md
vendor/
tests/
*.log
.DS_Store
.idea/
.vscode/

[!tip] 为什么 .dockerignore 很重要?

如果不排除 .git 目录,整个版本历史都会被打包进镜像(增加数百 MB)。如果排除不当,可能遗漏必要的配置文件。建议每个项目根目录都包含此文件。

层缓存技巧

❌ 差的缓存策略

# 差:任何文件改动都会让后续所有 layer 失效
COPY . .
RUN go build

✅ 好的缓存策略

# 好:只有 go.mod/go.sum 变化时才重新下载依赖
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build

核心原则:经常变动的内容靠近 COPY,少变的放前面。这样当代码频繁变更时,依赖下载和构建步骤仍能命中缓存。

BuildKit 特性

BuildKit 是新一代构建引擎,默认在 Docker 18.09+ 和所有 Docker Desktop 中启用:

# 启用 BuildKit 加速
export DOCKER_BUILDKIT=1

# 利用远程缓存 (需配合 registry)
docker build --cache-from=harbor.example.com/app/cache:latest .

# SSH agent forwarding(拉取私有依赖用)
docker build --ssh default .

# 秘密变量注入(不进 Docker history)
docker build --secret id=token,env=GITHUB_TOKEN .
# syntax=docker/dockerfile:1
# ^^^ 必须声明语法版本以启用 BuildKit

FROM golang:1.22-alpine AS builder
RUN --mount=type=cache,target=/go/pkg/mod \
    --mount=type=cache,target=/root/.cache/go-build \
    go mod download && go build -o server .

[!info] --mount=type=cache vs RUN 缓存

Docker 默认的层缓存会在源文件变化时跳过整层;而 type=cache 是增量更新本地缓存目录,不受构建上下文变化影响,适合依赖下载和编译缓存。

常见问题排查

症状 原因 解决
exec: "app": not found 多阶段 COPY 路径错误 检查绝对路径和文件名拼写
standard_init_linux.go: exec user process caused: permission denied 没有执行权限或缺少换行符 chmod +x, 确保 Linux 换行
service unavailable (K8s) HEALTHCHECK 未就绪 增大 --start-period
镜像体积异常大 .git 未排除或多层 RUN 未清理 检查 .dockerignore,合并 RUN 并 rm -rf
cannot execute: executable file not found 架构不匹配(arm64 → amd64) 确认 TARGETARCH 或手动指定 --platform

关联笔记