Skip to content

Dockerfile 完整参考

Dockerfile 是用于构建 Docker 镜像的文本文件,包含一系列指令。本文将详细介绍 Dockerfile 的所有指令和最佳实践。

目录

  1. 文件格式
  2. 基础指令
  3. 文件操作指令
  4. 运行指令
  5. 元数据指令
  6. 高级指令
  7. 多阶段构建

文件格式

1.1 基本结构

dockerfile
# 注释
INSTRUCTION arguments

# 示例
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
EXPOSE 3000
CMD ["npm", "start"]

1.2 语法规则

  • 指令不区分大小写,但约定使用大写
  • 每个指令创建一个新层
  • 指令按顺序执行
  • 第一个指令必须是 FROM

1.3 解析器指令

dockerfile
# syntax=docker/dockerfile:1
# 指定 Dockerfile 语法版本

# escape=`
# 指定转义字符(Windows)

基础指令

2.1 FROM

指定基础镜像,必须是第一个指令(除解析器指令外)。

dockerfile
# 基本用法
FROM ubuntu:22.04

# 使用别名(多阶段构建)
FROM node:18-alpine AS builder

# 使用平台变量
FROM --platform=$BUILDPLATFORM golang:1.21 AS builder

# 使用 scratch(空镜像)
FROM scratch

# 有效示例
FROM node:18.17.1-alpine3.18
FROM python:3.11-slim-bookworm
FROM gcr.io/distroless/python3-debian12

2.2 WORKDIR

设置工作目录。

dockerfile
# 基本用法
WORKDIR /app

# 相对路径(相对于上一个 WORKDIR)
WORKDIR /app
WORKDIR src          # 结果: /app/src

# 自动创建目录
WORKDIR /app/src     # 自动创建 /app 和 /app/src

# 使用环境变量
ENV APP_DIR=/app
WORKDIR $APP_DIR     # 结果: /app

# 最佳实践:使用绝对路径
WORKDIR /app         # ✅ 推荐
WORKDIR app          # ❌ 相对路径

2.3 USER

设置运行用户。

dockerfile
# 使用用户名
USER node

# 使用 UID
USER 1000

# 使用 UID:GID
USER 1000:1000

# 创建用户后使用
RUN groupadd -r appgroup && useradd -r -g appgroup appuser
USER appuser

# 多用户切换
USER root
RUN apt-get update && apt-get install -y curl
USER appuser

文件操作指令

3.1 COPY

从构建上下文复制文件到镜像。

dockerfile
# 基本用法
COPY source.txt /app/

# 复制多个文件
COPY file1.txt file2.txt /app/

# 使用通配符
COPY *.txt /app/
COPY src/*.js /app/src/

# 保留目录结构
COPY src/ /app/src/

# 复制并重命名
COPY config.json /app/config/production.json

# 使用 --from(多阶段)
COPY --from=builder /app/dist /app/dist

# 使用 --chown
COPY --chown=appuser:appgroup . /app

# 使用 --chmod
COPY --chmod=644 config.ini /app/

# 使用 --link(BuildKit)
COPY --link . /app

COPY 规则:

目标结果
file.txt/app//app/file.txt
file.txt/app/new.txt/app/new.txt
src//app//app/ 下的内容
src/app//app/src/

3.2 ADD

类似 COPY,但支持更多功能。

dockerfile
# 复制本地文件(同 COPY)
ADD file.txt /app/

# 自动解压 tar 归档
ADD archive.tar.gz /app/

# 从 URL 下载
ADD https://example.com/file.txt /app/

# 下载并解压
ADD https://example.com/archive.tar.gz /app/

# 使用 --chown
ADD --chown=appuser:appgroup archive.tar.gz /app/

# 使用 --chmod
ADD --chmod=755 script.sh /app/

# 使用 --link(BuildKit)
ADD --link https://example.com/file.txt /app/

COPY vs ADD:

特性COPYADD
本地文件
目录
自动解压 tar
远程 URL
推荐程度

3.3 VOLUME

创建挂载点。

dockerfile
# 基本用法
VOLUME /data

# 多个卷
VOLUME ["/data", "/config"]

# JSON 数组格式
VOLUME ["/var/www", "/var/log/apache2"]

# 注意:不能在 VOLUME 后修改内容
VOLUME /data
RUN echo "test" > /data/file  # ❌ 不会生效

# 正确做法
RUN mkdir /data && echo "test" > /data/file
VOLUME /data

运行指令

4.1 RUN

执行命令并创建新层。

dockerfile
# Shell 格式(使用 /bin/sh -c)
RUN apt-get update && apt-get install -y curl

# Exec 格式
RUN ["apt-get", "update"]
RUN ["apt-get", "install", "-y", "curl"]

# 多行命令(推荐)
RUN apt-get update && apt-get install -y \
    curl \
    git \
    vim \
    && rm -rf /var/lib/apt/lists/*

# 使用 && 连接命令
RUN command1 && \
    command2 && \
    command3

# BuildKit 缓存挂载
RUN --mount=type=cache,target=/root/.npm npm ci

# BuildKit 秘密挂载
RUN --mount=type=secret,id=api_key cat /run/secrets/api_key

# BuildKit SSH 挂载
RUN --mount=type=ssh,id=github git clone git@github.com:user/repo.git

# BuildKit 绑定挂载
RUN --mount=type=bind,source=package.json,target=package.json cat package.json

4.2 CMD

容器启动时执行的默认命令。

dockerfile
# Shell 格式
CMD echo "Hello World"

# Exec 格式(推荐)
CMD ["echo", "Hello World"]

# 作为 ENTRYPOINT 的参数
CMD ["-c", "config.json"]

# 常见用法
CMD ["npm", "start"]
CMD ["python", "app.py"]
CMD ["node", "server.js"]
CMD ["./myapp"]

4.3 ENTRYPOINT

配置容器启动时运行的可执行文件。

dockerfile
# Shell 格式
ENTRYPOINT echo "Hello"

# Exec 格式(推荐)
ENTRYPOINT ["echo", "Hello"]

# 与 CMD 结合使用
ENTRYPOINT ["python"]
CMD ["app.py"]
# 结果: python app.py

# 覆盖 CMD
ENTRYPOINT ["python"]
CMD ["app.py"]
# docker run myimage script.py
# 结果: python script.py

# 使用 exec 形式确保信号处理
ENTRYPOINT ["node", "server.js"]  # ✅ 推荐
ENTRYPOINT node server.js          # ❌ 可能有问题

ENTRYPOINT vs CMD:

场景ENTRYPOINTCMD
固定命令
默认参数
可被覆盖
组合使用命令参数

4.4 SHELL

更改默认 shell。

dockerfile
# 默认 Linux
SHELL ["/bin/sh", "-c"]

# 使用 bash
SHELL ["/bin/bash", "-c"]

# Windows
SHELL ["cmd", "/S", "/C"]
SHELL ["powershell", "-Command"]

# 示例
SHELL ["/bin/bash", "-c"]
RUN echo $HOME

SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN wget -O - https://example.com | bash

元数据指令

5.1 LABEL

添加元数据标签。

dockerfile
# 基本用法
LABEL version="1.0.0"

# 多个标签
LABEL version="1.0.0" \
      description="My application" \
      maintainer="admin@example.com"

# 多行标签
LABEL com.example.image.authors="author@example.com" \
      com.example.image.created="2024-01-01" \
      com.example.image.version="1.0.0" \
      com.example.image.licenses="MIT"

# 预定义标签(OCI 标准)
LABEL org.opencontainers.image.title="My App" \
      org.opencontainers.image.description="Application description" \
      org.opencontainers.image.version="1.0.0" \
      org.opencontainers.image.authors="Author Name" \
      org.opencontainers.image.source="https://github.com/user/repo" \
      org.opencontainers.image.licenses="MIT"

5.2 EXPOSE

声明容器运行时监听的端口。

dockerfile
# 基本用法
EXPOSE 80

# 多个端口
EXPOSE 80 443

# 指定协议
EXPOSE 80/tcp
EXPOSE 53/udp

# 组合
EXPOSE 80/tcp 443/tcp 53/udp

# 注意:这只是声明,不会实际发布端口
# 实际发布使用 docker run -p

5.3 ENV

设置环境变量。

dockerfile
# 基本用法
ENV NODE_ENV=production

# 多个变量
ENV NODE_ENV=production \
    PORT=3000 \
    DEBUG=false

# 使用变量
ENV APP_HOME=/app
WORKDIR $APP_HOME

# 修改 PATH
ENV PATH=/app/bin:$PATH

# 默认值(可在运行时覆盖)
ENV PORT=3000
# docker run -e PORT=8080 myimage

# 必需变量(运行时设置)
# ENV DATABASE_URL=${DATABASE_URL:?required}

5.4 ARG

定义构建参数。

dockerfile
# 基本用法
ARG VERSION=latest

# 多个参数
ARG NODE_VERSION=18
ARG APP_VERSION=1.0.0

# 使用参数
FROM node:${NODE_VERSION}-alpine

# 参数作用域
ARG VERSION=1.0.0          # 全局
FROM ubuntu:22.04
ARG VERSION=2.0.0          # 此阶段有效
RUN echo $VERSION          # 输出: 2.0.0

FROM alpine:latest
RUN echo $VERSION          # 输出: 1.0.0(全局)

# 传递参数构建
docker build --build-arg VERSION=2.0.0 -t myapp .

# 预定义参数
ARG HTTP_PROXY
ARG http_proxy
ARG HTTPS_PROXY
ARG https_proxy
ARG FTP_PROXY
ARG ftp_proxy
ARG NO_PROXY
ARG no_proxy

ARG vs ENV:

特性ARGENV
可用时机构建时构建时+运行时
持久化
查看docker historydocker inspect
用途构建配置运行配置

5.5 HEALTHCHECK

配置健康检查。

dockerfile
# 基本用法
HEALTHCHECK CMD curl -f http://localhost:8080/health || exit 1

# 完整配置
HEALTHCHECK --interval=30s \
            --timeout=10s \
            --start-period=40s \
            --retries=3 \
            CMD curl -f http://localhost:8080/health || exit 1

# 禁用继承的健康检查
HEALTHCHECK NONE

# 常见健康检查
HEALTHCHECK CMD wget --no-verbose --tries=1 --spider http://localhost:80/ || exit 1
HEALTHCHECK CMD pg_isready -U postgres || exit 1
HEALTHCHECK CMD redis-cli ping || exit 1
HEALTHCHECK CMD node -e "require('http').get('http://localhost:3000/health', (r) => process.exit(r.statusCode === 200 ? 0 : 1))"

健康检查选项:

选项默认值说明
--interval30s检查间隔
--timeout30s超时时间
--start-period0s启动延迟
--retries3重试次数

高级指令

6.1 ONBUILD

配置当镜像作为基础镜像时的触发器。

dockerfile
# 基础镜像 Dockerfile
FROM node:18-alpine
WORKDIR /app
ONBUILD COPY package*.json ./
ONBUILD RUN npm ci
ONBUILD COPY . .
ONBUILD CMD ["npm", "start"]

# 使用基础镜像时,ONBUILD 指令会自动执行
FROM my-base-image
# 自动执行 COPY package*.json ./
# 自动执行 RUN npm ci
# 自动执行 COPY . .
# 自动设置 CMD ["npm", "start"]

6.2 STOPSIGNAL

设置停止容器的系统调用信号。

dockerfile
# 基本用法
STOPSIGNAL SIGTERM

# 常见信号
STOPSIGNAL SIGTERM   # 默认,优雅终止
STOPSIGNAL SIGINT    # Ctrl+C
STOPSIGNAL SIGKILL   # 强制终止

# 数字形式
STOPSIGNAL 9         # SIGKILL
STOPSIGNAL 15        # SIGTERM

6.3 ARG 和 ENV 组合

dockerfile
# 构建参数
ARG NODE_VERSION=18
ARG APP_VERSION=1.0.0

# 使用构建参数
FROM node:${NODE_VERSION}-alpine

# 将 ARG 传递给 ENV(运行时可用)
ARG APP_VERSION
ENV APP_VERSION=${APP_VERSION}

WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .

EXPOSE 3000
CMD ["npm", "start"]

多阶段构建

7.1 基本多阶段构建

dockerfile
# 构建阶段
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -o myapp

# 运行阶段
FROM alpine:3.18
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/myapp .
CMD ["./myapp"]

7.2 多目标构建

dockerfile
# syntax=docker/dockerfile:1

FROM node:18-alpine AS base
WORKDIR /app
COPY package*.json ./

FROM base AS dependencies
RUN npm ci --only=production

FROM base AS development
RUN npm ci
COPY . .
CMD ["npm", "run", "dev"]

FROM base AS build
COPY . .
RUN npm run build

FROM node:18-alpine AS production
WORKDIR /app
COPY --from=dependencies /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
COPY package.json ./
CMD ["node", "dist/main.js"]

构建特定目标:

bash
docker build --target development -t myapp:dev .
docker build --target production -t myapp:prod .

7.3 并行构建

dockerfile
# syntax=docker/dockerfile:1

# 并行构建前端和后端
FROM node:18-alpine AS frontend-builder
WORKDIR /frontend
COPY frontend/package*.json ./
RUN npm ci
COPY frontend/ .
RUN npm run build

FROM golang:1.21-alpine AS backend-builder
WORKDIR /backend
COPY backend/go.mod backend/go.sum ./
RUN go mod download
COPY backend/ .
RUN go build -o server

FROM alpine:3.18
WORKDIR /app
COPY --from=frontend-builder /frontend/dist ./static
COPY --from=backend-builder /backend/server .
CMD ["./server"]

完整示例

8.1 Node.js 应用

dockerfile
# syntax=docker/dockerfile:1

# 依赖阶段
FROM node:18-alpine AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm \
    npm ci --only=production

# 构建阶段
FROM node:18-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

# 生产阶段
FROM node:18-alpine AS runner
WORKDIR /app

ENV NODE_ENV=production
ENV PORT=3000

RUN addgroup --system --gid 1001 nodejs && \
    adduser --system --uid 1001 nextjs

COPY --from=builder --chown=nextjs:nodejs /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs

EXPOSE 3000

ENV PORT 3000

HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
    CMD node -e "require('http').get('http://localhost:3000/api/health', (r) => process.exit(r.statusCode === 200 ? 0 : 1))"

CMD ["node", "server.js"]

8.2 Python 应用

dockerfile
# syntax=docker/dockerfile:1

# 构建阶段
FROM python:3.11-slim AS builder

WORKDIR /app

RUN apt-get update && apt-get install -y --no-install-recommends \
    gcc \
    && rm -rf /var/lib/apt/lists/*

COPY requirements.txt .
RUN --mount=type=cache,target=/root/.cache/pip \
    pip install --user --no-cache-dir -r requirements.txt

# 生产阶段
FROM python:3.11-slim

WORKDIR /app

# 创建非 root 用户
RUN groupadd -r appgroup && useradd -r -g appgroup appuser

# 从构建阶段复制依赖
COPY --from=builder /root/.local /home/appuser/.local

# 设置 PATH
ENV PATH=/home/appuser/.local/bin:$PATH

# 复制应用代码
COPY --chown=appuser:appgroup . .

# 切换到非 root 用户
USER appuser

# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
    CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')" || exit 1

EXPOSE 8000

CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:8000", "app:app"]

8.3 Go 应用

dockerfile
# syntax=docker/dockerfile:1

# 构建阶段
FROM --platform=$BUILDPLATFORM golang:1.21-alpine AS builder

ARG TARGETOS
ARG TARGETARCH

WORKDIR /app

# 安装 git(如果需要)
RUN apk add --no-cache git

# 复制依赖文件
COPY go.mod go.sum ./
RUN --mount=type=cache,target=/go/pkg/mod \
    go mod download

# 复制源码
COPY . .

# 构建
RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH \
    go build -ldflags="-w -s -extldflags '-static'" \
    -a -installsuffix cgo \
    -o myapp .

# 生产阶段
FROM scratch

# 从 builder 复制 CA 证书
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/

# 从 builder 复制用户
COPY --from=builder /etc/passwd /etc/passwd

# 从 builder 复制应用
COPY --from=builder /app/myapp /myapp

# 使用非 root 用户
USER 1000

# 健康检查(需要 wget 或 curl,scratch 不支持)
# 使用 distroless 或 alpine 如果需要健康检查

EXPOSE 8080

ENTRYPOINT ["/myapp"]

最佳实践

9.1 优化建议

dockerfile
# ✅ 使用特定版本
FROM node:18.17.1-alpine3.18

# ❌ 避免使用 latest
FROM node:latest

# ✅ 合并 RUN 指令
RUN apt-get update && apt-get install -y \
    curl \
    git \
    && rm -rf /var/lib/apt/lists/*

# ❌ 避免多个 RUN 产生多个层
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y git

# ✅ 优化层缓存
COPY package*.json ./
RUN npm ci
COPY . .

# ❌ 代码变化导致依赖重新安装
COPY . .
RUN npm ci

# ✅ 使用非 root 用户
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001
USER nodejs

# ✅ 使用 .dockerignore
# node_modules
# .git
# .env

9.2 安全检查清单

  • [ ] 使用特定版本标签
  • [ ] 使用非 root 用户运行
  • [ ] 最小化镜像层数
  • [ ] 清理构建缓存
  • [ ] 扫描镜像漏洞
  • [ ] 使用只读根文件系统
  • [ ] 配置健康检查
  • [ ] 限制资源使用

下一步

基于 MIT 许可发布