Dockerfile — 完整编写指南
Dockerfile — 完整编写指南
Expert reference for writing production-grade Dockerfiles. Every instruction, every pattern, every optimization.
编写生产级Dockerfile的专业参考资料,涵盖所有指令、模式与优化方案。
ALWAYS use this skill when the user mentions:
- "Dockerfile", "怎么写 Dockerfile", "Dockerfile 指令"
- "多阶段构建", "multi-stage build"
- "Dockerfile 层优化", "layer caching"
- "Dockerfile 模板", language-specific: "Go Dockerfile", "Java Dockerfile", "Python Dockerfile"
- Need to create or optimize a Dockerfile
- "Dockerfile best practices"
当用户提及以下内容时,务必使用本技能:
- "Dockerfile"、"怎么写 Dockerfile"、"Dockerfile 指令"
- "多阶段构建"、"multi-stage build"
- "Dockerfile 层优化"、"layer caching"
- "Dockerfile 模板"、特定语言相关:"Go Dockerfile"、"Java Dockerfile"、"Python Dockerfile"
- 需要创建或优化Dockerfile
- "Dockerfile best practices"
Instruction Reference
指令参考
FROM — Base Image
FROM — 基础镜像
dockerfile
FROM <image>[:<tag>] [AS <stage-name>]
FROM alpine:3.20 # Tag
FROM alpine:3.20@sha256:abc123...def456 # Digest (production!)
FROM golang:1.22-alpine AS builder # Named stage
FROM scratch # Empty image (for static binaries)
| Best Practice | Why |
|---|
| Pin digest for production | Tags are mutable; digest is immutable |
| Use Alpine/slim variants | Smaller attack surface, smaller image |
| for Go/Rust | Static binaries need nothing else |
dockerfile
FROM <image>[:<tag>] [AS <stage-name>]
FROM alpine:3.20 # 指定标签
FROM alpine:3.20@sha256:abc123...def456 # 指定摘要(生产环境推荐!)
FROM golang:1.22-alpine AS builder # 命名构建阶段
FROM scratch # 空镜像(适用于静态二进制文件)
| 最佳实践 | 原因 |
|---|
| 生产环境固定镜像摘要 | 标签可修改,摘要不可变 |
| 使用Alpine/slim变体镜像 | 更小的攻击面,镜像体积更小 |
| Go/Rust使用 | 静态二进制文件无需其他依赖 |
RUN — Execute Commands
RUN — 执行命令
✅ Chain commands, clean in same layer
✅ 链式执行命令,在同一层清理
❌ Each RUN = new layer (bloat)
❌ 每个RUN创建新层(导致镜像臃肿)
RUN apk add curl
RUN curl ... -o /usr/local/bin/script
RUN chmod +x /usr/local/bin/script
RUN apk add curl
RUN curl ... -o /usr/local/bin/script
RUN chmod +x /usr/local/bin/script
Multi-line readability
多行写法提升可读性
RUN set -eux;
apk add --no-cache
curl
ca-certificates
tzdata;
curl -fsSL ... | tar xz -C /usr/local
RUN set -eux;
apk add --no-cache
curl
ca-certificates
tzdata;
curl -fsSL ... | tar xz -C /usr/local
COPY — Copy Files
COPY — 复制文件
dockerfile
COPY [--chown=<user>:<group>] <src>... <dest>
COPY . /app
COPY --chown=app:app ./binary /usr/local/bin/
COPY --from=builder /app/build /app # From another stage (multi-stage)
dockerfile
COPY [--chown=<user>:<group>] <src>... <dest>
COPY . /app
COPY --chown=app:app ./binary /usr/local/bin/
COPY --from=builder /app/build /app # 从其他构建阶段复制(多阶段构建)
✅ Layer-friendly: copy deps first, then source
✅ 友好利用镜像层:先复制依赖文件,再复制源码
COPY go.mod go.sum ./
RUN go mod download
COPY . .
COPY go.mod go.sum ./
RUN go mod download
COPY . .
❌ Source change invalidates dependency cache
❌ 源码变更会使依赖缓存失效
COPY . .
RUN go mod download
COPY . .
RUN go mod download
ADD — Copy + Auto-extract
ADD — 复制+自动解压
ADD auto-extracts tar archives
ADD会自动解压tar归档文件
Prefer COPY unless you need tar extraction
除非需要自动解压,否则优先使用COPY
COPY archive.tar.gz /app/
RUN tar xzf /app/archive.tar.gz -C /app
COPY archive.tar.gz /app/
RUN tar xzf /app/archive.tar.gz -C /app
WORKDIR — Set Working Directory
WORKDIR — 设置工作目录
All subsequent RUN/COPY/CMD use /app as base
后续所有RUN/COPY/CMD命令均以/app为基础目录
RUN cd /app && npm install # ❌ cd doesn't persist
RUN cd /app && npm install # ❌ cd命令不会持久生效
ARG: build-time only (not in final image)
ARG:仅构建阶段有效(不会保留在最终镜像中)
ARG VERSION=1.0.0
FROM myapp:${VERSION}
ARG VERSION=1.0.0
FROM myapp:${VERSION}
ENV: runtime (persists in image)
ENV:运行时有效(会保留在镜像中)
ENV NODE_ENV=production
PORT=8080
ENV NODE_ENV=production
PORT=8080
Combine: pass ARG to ENV
组合使用:将ARG传递给ENV
ARG APP_VERSION
ENV APP_VERSION=${APP_VERSION}
ARG APP_VERSION
ENV APP_VERSION=${APP_VERSION}
EXPOSE — Document Ports
EXPOSE — 声明端口
dockerfile
EXPOSE 8080
EXPOSE 8080/tcp # Protocol-specific
EXPOSE 8080/udp
dockerfile
EXPOSE 8080
EXPOSE 8080/tcp # 指定协议
EXPOSE 8080/udp
Note: EXPOSE does NOT publish ports. Use -p at runtime:
注意:EXPOSE不会自动发布端口,需在运行时使用-p参数:
docker run -p 8080:8080 myapp
docker run -p 8080:8080 myapp
CMD vs ENTRYPOINT
CMD vs ENTRYPOINT
CMD: default command (overridable)
CMD:默认命令(可被覆盖)
CMD ["nginx", "-g", "daemon off;"]
CMD ["nginx", "-g", "daemon off;"]
docker run myimage echo hello → overrides CMD
docker run myimage echo hello → 会覆盖CMD
ENTRYPOINT: fixed entry (not overridable)
ENTRYPOINT:固定入口(不可被直接覆盖)
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]
docker run myimage → runs: docker-entrypoint.sh nginx -g 'daemon off;'
docker run myimage → 执行:docker-entrypoint.sh nginx -g 'daemon off;'
docker run myimage echo hello → runs: docker-entrypoint.sh echo hello
docker run myimage echo hello → 执行:docker-entrypoint.sh echo hello
Common pattern: script + default args
常见模式:脚本+默认参数
ENTRYPOINT ["/entrypoint.sh"]
CMD ["start"]
ENTRYPOINT ["/entrypoint.sh"]
CMD ["start"]
USER — Switch to Non-Root
USER — 切换至非root用户
Create user and group
创建用户和用户组
RUN addgroup --system app && adduser --system --ingroup app app
USER app
RUN addgroup --system app && adduser --system --ingroup app app
USER app
dockerfile
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD wget -qO- http://localhost:8080/health || exit 1
dockerfile
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD wget -qO- http://localhost:8080/health || exit 1
Dockerfile without shell (scratch):
无shell环境的Dockerfile(如scratch):
HEALTHCHECK --interval=30s CMD /app/healthcheck || exit 1
HEALTHCHECK --interval=30s CMD /app/healthcheck || exit 1
SHELL — Change Default Shell
SHELL — 修改默认Shell
dockerfile
SHELL ["/bin/bash", "-euxo", "pipefail", "-c"]
RUN echo "Now using bash with strict mode"
dockerfile
SHELL ["/bin/bash", "-euxo", "pipefail", "-c"]
RUN echo "现在使用开启严格模式的bash"
Multi-Stage Build Patterns
多阶段构建模式
Pattern 1: Build Binary + Scratch (Go/Rust)
模式1:构建二进制文件 + Scratch(Go/Rust)
dockerfile
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="-w -s" -o server .
FROM scratch
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /app/server /server
USER 1000:1000
CMD ["/server"]
dockerfile
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="-w -s" -o server .
FROM scratch
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /app/server /server
USER 1000:1000
CMD ["/server"]
Pattern 2: Build + Minimal Runtime (Java)
模式2:构建 + 轻量运行时(Java)
dockerfile
FROM maven:3.9-eclipse-temurin-21 AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package -DskipTests
FROM eclipse-temurin:21-jre-alpine
RUN addgroup --system app && adduser -S -G app app
COPY --from=builder /app/target/*.jar /app.jar
USER app
CMD ["java", "-jar", "/app.jar"]
dockerfile
FROM maven:3.9-eclipse-temurin-21 AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package -DskipTests
FROM eclipse-temurin:21-jre-alpine
RUN addgroup --system app && adduser -S -G app app
COPY --from=builder /app/target/*.jar /app.jar
USER app
CMD ["java", "-jar", "/app.jar"]
Pattern 3: Layer-Optimized (Spring Boot)
模式3:镜像层优化(Spring Boot)
dockerfile
FROM eclipse-temurin:21-jre-alpine AS builder
WORKDIR /app
COPY build/libs/*.jar app.jar
RUN java -Djarmode=layertools -jar app.jar extract
FROM eclipse-temurin:21-jre-alpine
RUN addgroup --system app && adduser -S -G app app
dockerfile
FROM eclipse-temurin:21-jre-alpine AS builder
WORKDIR /app
COPY build/libs/*.jar app.jar
RUN java -Djarmode=layertools -jar app.jar extract
FROM eclipse-temurin:21-jre-alpine
RUN addgroup --system app && adduser -S -G app app
Layers in dependency order (max cache)
按依赖顺序分层(最大化缓存利用率)
COPY --from=builder /app/dependencies/ ./
COPY --from=builder /app/spring-boot-loader/ ./
COPY --from=builder /app/snapshot-dependencies/ ./
COPY --from=builder /app/application/ ./
USER app
ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]
COPY --from=builder /app/dependencies/ ./
COPY --from=builder /app/spring-boot-loader/ ./
COPY --from=builder /app/snapshot-dependencies/ ./
COPY --from=builder /app/application/ ./
USER app
ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]
Pattern 4: Build + Alpline (Node.js)
模式4:构建 + Alpine(Node.js)
dockerfile
FROM node:22-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:22-alpine
RUN addgroup --system app && adduser -S -G app app
COPY --from=builder /app/dist /app
COPY --from=builder /app/node_modules /app/node_modules
USER app
CMD ["node", "/app/index.js"]
dockerfile
FROM node:22-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:22-alpine
RUN addgroup --system app && adduser -S -G app app
COPY --from=builder /app/dist /app
COPY --from=builder /app/node_modules /app/node_modules
USER app
CMD ["node", "/app/index.js"]
Pattern 5: Python Dependencies + Slim Runtime
模式5:Python依赖 + 轻量运行时
dockerfile
FROM python:3.12 AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
FROM python:3.12-slim
RUN groupadd --system app && useradd --system -g app app
COPY --from=builder /root/.local /home/app/.local
COPY . /app
ENV PATH=/home/app/.local/bin:$PATH
USER app
CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0"]
dockerfile
FROM python:3.12 AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
FROM python:3.12-slim
RUN groupadd --system app && useradd --system -g app app
COPY --from=builder /root/.local /home/app/.local
COPY . /app
ENV PATH=/home/app/.local/bin:$PATH
USER app
CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0"]
Layer Caching Strategy
镜像层缓存策略
Docker builds layers from top to bottom.
A changed layer invalidates ALL layers below it.
✅ CORRECT order:
FROM base ← rarely changes
RUN install-system-deps ← changes with system updates
COPY go.mod go.sum ./ ← changes with dependency changes
RUN go mod download ← changes with dependency changes
COPY . . ← changes every commit ← MUST BE LAST
❌ WRONG order (slow builds):
COPY . . ← changes every commit
RUN go mod download ← reruns every time!
Docker从上到下构建镜像层。
某一层变更会使其下方所有层的缓存失效。
✅ 正确顺序:
FROM base ← 极少变更
RUN install-system-deps ← 随系统更新变更
COPY go.mod go.sum ./ ← 随依赖变更变更
RUN go mod download ← 随依赖变更变更
COPY . . ← 每次提交都会变更 ← 必须放在最后
❌ 错误顺序(构建缓慢):
COPY . . ← 每次提交都会变更
RUN go mod download ← 每次都会重新执行!
.dockerignore
.dockerignore
.dockerignore
.dockerignore
.git
.gitignore
.md
.env
.env.
Dockerfile
docker-compose*.yml
node_modules
pycache
*.pyc
.git
.idea
.vscode
*.log
tmp/
.git
.gitignore
.md
.env
.env.
Dockerfile
docker-compose*.yml
node_modules
pycache
*.pyc
.git
.idea
.vscode
*.log
tmp/
Workflow — 推荐编写流程
工作流程 — 推荐编写流程
Step 1:
确定语言和运行时: Go/Java/Node.js/Python → 选择基础镜像
Step 2:
选择构建模式: 单阶段/多阶段/Spring Boot 分层 → 从 templates 选模板
Step 3:
编写 Dockerfile: 先 COPY 依赖 → RUN install → COPY 源码 → CMD
Step 4:
验证:
+
+ dive 分析镜像大小
Step 5:
生产加固: USER 非 root、HEALTHCHECK、固定 digest、Security 检查
Step 1:
确定语言和运行时: Go/Java/Node.js/Python → 选择基础镜像
Step 2:
选择构建模式: 单阶段/多阶段/Spring Boot分层 → 从模板中选择
Step 3:
编写Dockerfile: 先COPY依赖文件 → RUN安装依赖 → COPY源码 → CMD
Step 4:
验证:
+
+ dive分析镜像大小
Step 5:
生产加固: 使用非root用户USER、添加HEALTHCHECK、固定镜像摘要、安全检查
Gotchas — Common Pitfalls
常见陷阱
- before : Every code change invalidates the dependency layer — rebuilds from scratch. → Recovery: Always copy deps first:
COPY package.json . → RUN install → COPY src/ .
.
- Root user by default: Always at the end of Dockerfile. Escaping the container as root = host root. → Recovery: Add
RUN addgroup -S app && adduser -S app -G app
+ ; verify with .
- in Dockerfile: Baked into image layers forever. → Recovery: Use BuildKit or runtime injection
docker run -e SECRET=$VAL
.
- No : Sends entire project to build context — slow and leaks sensitive files. → Recovery: Create with at minimum ; verify with
docker build --no-cache . 2>&1 | head -1
.
RUN apt update && apt install
without cleanup: Leaves package lists in layer. → Recovery: Chain with && rm -rf /var/lib/apt/lists/*
; for apk: flag.
- Heavy base image: (77 MB) vs (7 MB). → Recovery: Prefer alpine; if glibc needed, use ; check size with .
- 在 之前: 每次代码变更都会使依赖层缓存失效 — 导致从头构建。→ 解决方法: 始终先复制依赖文件:
COPY package.json . → RUN install → COPY src/ .
。
- 默认使用root用户: 务必在Dockerfile末尾添加。以root身份逃逸容器会获得主机root权限。→ 解决方法: 添加
RUN addgroup -S app && adduser -S app -G app
+ ;使用验证。
- 在Dockerfile中使用: 会永久嵌入镜像层中。→ 解决方法: 使用BuildKit的或运行时注入
docker run -e SECRET=$VAL
。
- 未配置: 会将整个项目发送到构建上下文 — 构建缓慢且可能泄露敏感文件。→ 解决方法: 创建,至少包含;使用
docker build --no-cache . 2>&1 | head -1
验证。
RUN apt update && apt install
未清理: 会在镜像层中留下包列表。→ 解决方法: 链式添加&& rm -rf /var/lib/apt/lists/*
;对于apk,使用参数。
- 使用重型基础镜像: (77 MB)对比(7 MB)。→ 解决方法: 优先选择alpine;若需要glibc,使用;使用查看镜像大小。
Boundary — 能力边界(适用与不适用场景)
能力边界(适用与不适用场景)
| 分类 | 场景 | 说明 |
|---|
| ✅ 能做 | 编写生产级 Dockerfile | 14 条指令完整参考 + 最佳实践 |
| ✅ 能做 | 多阶段构建(Go/Java/Node/Python) | 5 种语言专属模板 |
| ✅ 能做 | 层缓存优化 | COPY 依赖优先 + RUN 合并 + BuildKit cache mount |
| ✅ 能做 | 镜像瘦身 | 5 步法路线图:多阶段→Alpine→distroless→清理→dive |
| ⚠️ 需条件 | 私有依赖安装 | 需配合 BuildKit --secret 或 SSH forwarding |
| ⚠️ 需条件 | CMD vs ENTRYPOINT 选择 | 见指令参考中的决策树(工具用 ENTRYPOINT,服务用 CMD) |
| ❌ 超范围 | docker build 命令执行 | 使用 |
| ❌ 超范围 | 多平台构建(arm64/amd64) | 使用 |
| ❌ 超范围 | 容器编排(多容器) | 使用 |
| 分类 | 场景 | 说明 |
|---|
| ✅ 能做 | 编写生产级Dockerfile | 14条指令完整参考 + 最佳实践 |
| ✅ 能做 | 多阶段构建(Go/Java/Node/Python) | 5种语言专属模板 |
| ✅ 能做 | 镜像层缓存优化 | COPY依赖优先 + RUN命令合并 + BuildKit缓存挂载 |
| ✅ 能做 | 镜像瘦身 | 5步法路线图:多阶段→Alpine→distroless→清理→dive |
| ⚠️ 需条件 | 私有依赖安装 | 需配合BuildKit --secret或SSH转发 |
| ⚠️ 需条件 | CMD与ENTRYPOINT选择 | 参考指令部分的决策树(工具类用ENTRYPOINT,服务类用CMD) |
| ❌ 超范围 | docker build命令执行 | 使用技能 |
| ❌ 超范围 | 多平台构建(arm64/amd64) | 使用技能 |
| ❌ 超范围 | 容器编排(多容器) | 使用技能 |
When NOT to Use This Skill
不适用本技能的场景
| ❌ Skip | ✅ Use Instead |
|---|
| Building images () | |
| Multi-platform builds | |
| Compose file authoring | |
| Docker basics | |
| Running containers | |
| ❌ 跳过本技能 | ✅ 使用以下技能 |
|---|
| 构建镜像() | |
| 多平台构建 | |
| Compose文件编写 | |
| Docker基础 | |
| 运行容器 | |
Security & Stability
安全与稳定性
- All Dockerfile templates are educational. Review and harden before production use.
- Never embed secrets in Dockerfile. Use BuildKit or runtime injection.
- Always for production. Use when copying files for that user.
- Pin base image digests for production reproducibility.
- 所有Dockerfile模板仅作学习参考,生产使用前需审核并加固。
- 切勿在Dockerfile中嵌入密钥。使用BuildKit的或运行时注入。
- 生产环境务必使用。为该用户复制文件时使用。
- 生产环境固定基础镜像摘要以保证可复现性。
🧭 Docker Skills Journey
🧭 Docker技能学习路径
📍
You are here: — Dockerfile 编写
basics → dockerfile → build → buildx → run → compose → ...
→ Next:
— Build images with
basics → dockerfile → build → buildx → run → compose → ...