Dockerfile 完整参考
Dockerfile 是用于构建 Docker 镜像的文本文件,包含一系列指令。本文将详细介绍 Dockerfile 的所有指令和最佳实践。
目录
文件格式
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-debian122.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 . /appCOPY 规则:
| 源 | 目标 | 结果 |
|---|---|---|
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:
| 特性 | COPY | ADD |
|---|---|---|
| 本地文件 | ✅ | ✅ |
| 目录 | ✅ | ✅ |
| 自动解压 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.json4.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:
| 场景 | ENTRYPOINT | CMD |
|---|---|---|
| 固定命令 | ✅ | ❌ |
| 默认参数 | ❌ | ✅ |
| 可被覆盖 | 难 | 易 |
| 组合使用 | 命令 | 参数 |
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 -p5.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_proxyARG vs ENV:
| 特性 | ARG | ENV |
|---|---|---|
| 可用时机 | 构建时 | 构建时+运行时 |
| 持久化 | 否 | 是 |
| 查看 | docker history | docker 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))"健康检查选项:
| 选项 | 默认值 | 说明 |
|---|---|---|
--interval | 30s | 检查间隔 |
--timeout | 30s | 超时时间 |
--start-period | 0s | 启动延迟 |
--retries | 3 | 重试次数 |
高级指令
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 # SIGTERM6.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
# .env9.2 安全检查清单
- [ ] 使用特定版本标签
- [ ] 使用非 root 用户运行
- [ ] 最小化镜像层数
- [ ] 清理构建缓存
- [ ] 扫描镜像漏洞
- [ ] 使用只读根文件系统
- [ ] 配置健康检查
- [ ] 限制资源使用
下一步
- 学习 镜像构建最佳实践
- 了解 多阶段构建详解
- 掌握 Docker 安全实践
- 深入 Docker Build 概述