Dockerfile 详解

5.1 指令详解

FROM —— 指定基础镜像

FROM ubuntu:22.04
FROM python:3.12-slim
FROM golang:1.22-alpine AS builder  # 命名阶段
FROM scratch                        # 空镜像(用于静态二进制)

RUN —— 构建时执行命令

# shell 格式(通过 /bin/sh -c 执行)
RUN apt-get update && apt-get install -y curl
 
# exec 格式(直接执行,不经过 shell)
RUN ["/usr/bin/apt-get", "update"]
 
# 最佳实践:合并命令减少层数
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
        curl \
        ca-certificates && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

COPY —— 复制文件

COPY . .                           # 复制整个上下文
COPY requirements.txt /app/        # 复制单个文件
COPY --chown=appuser:appuser . .   # 设置所有者
COPY --chmod=755 script.sh /app/   # 设置权限(BuildKit)

ADD —— 复制文件(特殊功能)

# 自动解压 tar 文件
ADD app.tar.gz /app/
 
# 从 URL 下载
ADD https://example.com/file.zip /tmp/
 
# 注意:一般推荐用 COPY,只有需要自动解压时才用 ADD

WORKDIR —— 设置工作目录

WORKDIR /app
# 后续指令都在 /app 下执行
# 如果目录不存在会自动创建
WORKDIR /app/src

ENV —— 设置环境变量

ENV APP_HOME=/app
ENV APP_PORT=8080
ENV PYTHONUNBUFFERED=1
# 环境变量在构建和运行时都有效

ARG —— 构建参数

ARG VERSION=1.0
ARG DEBIAN_FRONTEND=noninteractive
# 只在构建时有效,运行时不存在
# 可通过 docker build --build-arg VERSION=2.0 覆盖

EXPOSE —— 声明端口

EXPOSE 80
EXPOSE 443
# 只是文档性质,实际端口映射靠 docker run -p

USER —— 切换用户

RUN useradd -r -s /usr/bin/nologin appuser
USER appuser
# 后续指令都以 appuser 身份执行

ENTRYPOINT —— 入口命令

# exec 格式(推荐)
ENTRYPOINT ["python", "app.py"]
 
# shell 格式(不推荐,信号传递有问题)
ENTRYPOINT python app.py
 
# ENTRYPOINT 不可被 docker run 参数覆盖
# 但可以通过 --entrypoint 覆盖

CMD —— 默认参数

# exec 格式
CMD ["python", "app.py"]
 
# shell 格式
CMD python app.py
 
# CMD 可被 docker run 参数覆盖
# docker run myimage other_command  → 执行 other_command

ENTRYPOINT vs CMD 组合

ENTRYPOINT ["python"]
CMD ["app.py"]
# docker run myimage           → python app.py
# docker run myimage test.py   → python test.py(CMD 被覆盖)
 
ENTRYPOINT ["python"]
# 无 CMD 时
# docker run myimage           → python(无参数,报错)
# docker run myimage app.py    → python app.py

VOLUME —— 声明卷挂载点

VOLUME /data
VOLUME ["/var/log", "/var/data"]
# 运行时自动创建匿名卷

HEALTHCHECK —— 健康检查

HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
  CMD curl -f http://localhost:8080/health || exit 1
 
HEALTHCHECK NONE  # 禁用健康检查

LABEL —— 元数据

LABEL maintainer="user@example.com"
LABEL version="1.0" \
      description="My application"

SHELL —— 更改默认 shell

SHELL ["/bin/bash", "-c"]
RUN echo "Using bash" && ls -la

STOPSIGNAL —— 停止信号

STOPSIGNAL SIGQUIT

5.2 ENTRYPOINT vs CMD 深入

特性ENTRYPOINTCMD
可被 docker run 覆盖否(需 —entrypoint)
用途固定执行命令默认参数
组合使用ENTRYPOINT 是命令,CMD 是参数
# 示例
# Dockerfile:
# ENTRYPOINT ["python"]
# CMD ["app.py"]
 
docker run myimage              # → python app.py
docker run myimage test.py      # → python test.py(CMD 被覆盖)
docker run --entrypoint bash myimage  # → bash(ENTRYPOINT 被覆盖)

5.3 COPY vs ADD

特性COPYADD
基本复制
自动解压 tar
从 URL 下载
推荐使用✅ 一般情况仅需自动解压时

5.4 最小基础镜像选择

基础镜像大小适用场景
scratch0MB静态二进制(Go/Rust)
alpine~5MB需要包管理器的轻量场景
distroless~20MB生产环境(无 shell)
slim/debian-slim~80MB需要 Debian 兼容
ubuntu~77MB需要完整 Ubuntu
ubuntu-full~200MB+开发环境
# Go 静态二进制:用 scratch
FROM scratch
COPY --from=builder /app/server /server
ENTRYPOINT ["/server"]
 
# Python 轻量:用 alpine
FROM python:3.12-alpine
 
# 生产环境:用 distroless
FROM gcr.io/distroless/python3-debian12

5.5 多阶段构建最佳实践

# === 阶段 1:构建 ===
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
 
# === 阶段 2:生产运行 ===
FROM node:20-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --from=builder --chown=appuser:appgroup /app/dist ./dist
COPY --from=builder --chown=appuser:appgroup /app/node_modules ./node_modules
USER appuser
EXPOSE 3000
CMD ["node", "dist/server.js"]

5.6 最佳实践总结

安全

  • 不要用 root 运行容器(USER 指令)
  • 不要在镜像中存储密钥(使用 BuildKit secret mount)
  • 扫描漏洞(docker scout / trivy)

体积

  • 用小基础镜像(alpine / distroless / scratch)
  • 多阶段构建
  • .dockerignore 排除无关文件
  • 合并 RUN 命令减少层数
  • 清理包管理器缓存

构建速度

  • 依赖文件先 COPY,代码后 COPY(利用缓存)
  • BuildKit 缓存挂载(—mount=type=cache)
  • 并行构建(docker buildx)

运行时

  • 使用 exec 格式的 ENTRYPOINT/CMD
  • 设置 HEALTHCHECK
  • 单一职责(一个容器一个服务)

5.7 安全扫描

# Docker Scout(内置)
docker scout cves myapp:latest
 
# Trivy(第三方,更全面)
trivy image myapp:latest
trivy image --severity HIGH,CRITICAL myapp:latest