Skip to content

Dockerfile 指令详解

本节详细介绍 Dockerfile 中所有指令的语法、参数和使用场景。

指令总览

指令说明
FROM指定基础镜像
LABEL添加元数据标签
ENV设置环境变量
ARG构建参数
WORKDIR设置工作目录
COPY复制文件或目录
ADD添加文件(支持 URL 和解压)
RUN执行命令
CMD容器启动命令(可被覆盖)
ENTRYPOINT容器入口点(不可被覆盖)
EXPOSE声明端口
VOLUME声明匿名数据卷
USER切换运行用户
HEALTHCHECK健康检查
STOPSIGNAL停止信号
ONBUILD子镜像构建触发器
SHELL修改默认 Shell

FROM

dockerfile
FROM <镜像>[:<标签>] [AS <名称>]

# 示例
FROM ubuntu:22.04
FROM node:20-alpine AS builder
FROM scratch
  • 每个 Dockerfile 必须有一条 FROM 指令
  • 多个 FROM 表示多阶段构建
  • FROM scratch 创建空白镜像,适用于静态二进制

LABEL

为镜像添加元数据,便于管理和检索:

dockerfile
LABEL version="1.0"
LABEL maintainer="name@example.com"
LABEL description="My application"

# 推荐:一条 LABEL 声明所有标签
LABEL version="1.0" \
      maintainer="name@example.com" \
      description="My application" \
      org.opencontainers.image.created="2024-01-01"
bash
# 查看镜像标签
docker inspect myimage --format '{{json .Config.Labels}}'

# 按标签过滤镜像
docker images --filter label=version=1.0

ENV

dockerfile
ENV <key>=<value> ...

# 示例
ENV NODE_ENV=production
ENV PORT=3000 HOST=0.0.0.0

# 可以在后续指令中引用
ENV APP_HOME=/app
WORKDIR $APP_HOME
  • 在构建阶段和容器运行时都有效
  • 可以在 docker run 时通过 -e 覆盖

ARG

dockerfile
ARG <name>[=<默认值>]

# 示例
ARG NODE_VERSION=20
ARG BUILD_DATE
ARG GIT_COMMIT

FROM node:${NODE_VERSION}-alpine

# 使用 ARG
RUN echo "Build date: ${BUILD_DATE}"
  • 只在构建阶段有效,不会保留到最终镜像
  • 可以通过 docker build --build-arg key=value 传入
bash
docker build \
  --build-arg NODE_VERSION=18 \
  --build-arg BUILD_DATE=$(date -u +%Y-%m-%d) \
  -t myapp .

ARG 与 ENV 的区别:

特性ARGENV
作用范围构建时构建时 + 运行时
是否保留到镜像
是否可通过环境变量覆盖--build-arg-e

WORKDIR

dockerfile
WORKDIR <目录路径>

# 示例
WORKDIR /app
WORKDIR /app/src  # 绝对路径

# 相对路径(相对于上一个 WORKDIR)
WORKDIR /a
WORKDIR b     # /a/b
WORKDIR c     # /a/b/c
  • 目录不存在时自动创建
  • 影响后续的 RUNCMDCOPYADD 指令
  • 推荐使用绝对路径

COPY

dockerfile
COPY [--chown=<user>:<group>] [--chmod=<权限>] <源路径> <目标路径>

# 示例
COPY app.js .
COPY package*.json ./
COPY src/ ./src/
COPY . .  # 复制所有文件(配合 .dockerignore 使用)

# 设置文件所有者
COPY --chown=node:node . .

# 设置文件权限
COPY --chmod=755 scripts/entrypoint.sh /entrypoint.sh

# 从其他构建阶段复制(多阶段构建)
COPY --from=builder /app/dist ./dist

ADD

dockerfile
ADD [--chown=<user>:<group>] <源路径> <目标路径>

# 自动解压 tar 文件
ADD source.tar.gz /app/

# 从 URL 下载(不推荐,推荐用 RUN curl)
ADD https://example.com/file.txt /app/

# 从其他阶段复制
ADD --from=builder /app/dist ./

推荐使用 COPY,只在需要自动解压时使用 ADD。URL 下载推荐用 RUN curl 代替。

RUN

dockerfile
# shell 格式(默认 /bin/sh -c)
RUN <命令>

# exec 格式(推荐)
RUN ["executable", "param1", "param2"]

# 示例
RUN apt-get update && apt-get install -y curl
RUN ["apt-get", "install", "-y", "vim"]

# 多命令合并(减少层数,利用缓存)
RUN apt-get update \
    && apt-get install -y \
       curl \
       vim \
       git \
    && rm -rf /var/lib/apt/lists/*

RUN 的最佳实践:

  • 多条安装命令合并成一条 RUN(减少镜像层数)
  • 安装后清理缓存(apt-get clean / rm -rf /var/lib/apt/lists/*
  • 合理利用构建缓存

CMD

dockerfile
# exec 格式(推荐)
CMD ["executable", "param1", "param2"]

# shell 格式
CMD command param1 param2

# 作为 ENTRYPOINT 的参数
CMD ["param1", "param2"]

# 示例
CMD ["nginx", "-g", "daemon off;"]
CMD ["node", "app.js"]
CMD ["python", "-m", "flask", "run"]
  • 每个 Dockerfile 只有最后一条 CMD 有效
  • 可以被 docker run 的命令参数覆盖

ENTRYPOINT

dockerfile
# exec 格式(推荐)
ENTRYPOINT ["executable", "param1", "param2"]

# shell 格式(不推荐,会忽略 CMD 和 docker run 的参数)
ENTRYPOINT command param1 param2

# 示例:将容器作为命令工具
ENTRYPOINT ["curl"]
CMD ["--help"]
# docker run myimage      → curl --help
# docker run myimage -v   → curl -v

与 CMD 组合使用:

dockerfile
ENTRYPOINT ["node"]
CMD ["app.js"]
# 默认执行:node app.js
# docker run myimage other.js → 执行:node other.js

EXPOSE

dockerfile
EXPOSE <端口> [<端口>/<协议> ...]

# 示例
EXPOSE 80
EXPOSE 443
EXPOSE 3000/tcp
EXPOSE 5353/udp
  • 仅是文档说明,不会自动进行端口映射
  • 需要配合 docker run -p-P 才能实际映射

VOLUME

dockerfile
VOLUME ["/数据目录"]
VOLUME /var/log /var/db

# 示例
VOLUME ["/var/lib/mysql"]
VOLUME /uploads
  • 声明匿名数据卷挂载点
  • 容器创建时 Docker 会自动创建匿名卷
  • 主要用于文档化,提示用户这些目录需要持久化

USER

dockerfile
USER <user>[:<group>]
USER <UID>[:<GID>]

# 示例:切换到非 root 用户(安全最佳实践)
RUN addgroup --system appgroup \
    && adduser --system --group appuser

USER appuser
  • 影响后续的 RUNCMDENTRYPOINT 指令
  • 安全最佳实践:不要以 root 用户运行应用

HEALTHCHECK

dockerfile
HEALTHCHECK [选项] CMD <命令>
HEALTHCHECK NONE  # 禁用基础镜像的健康检查

# 选项
# --interval=30s   检查间隔(默认 30s)
# --timeout=30s    超时时间(默认 30s)
# --start-period=0s  容器启动后多久开始检查
# --retries=3      失败次数(超过则标记为 unhealthy)

# 示例
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
  CMD curl -f http://localhost/health || exit 1

HEALTHCHECK --interval=5s --timeout=3s \
  CMD wget -qO- http://localhost:3000/health || exit 1

STOPSIGNAL

dockerfile
STOPSIGNAL <信号>

# 示例
STOPSIGNAL SIGTERM  # 默认
STOPSIGNAL SIGQUIT  # Nginx 使用此信号优雅退出
STOPSIGNAL 9        # SIGKILL

ONBUILD

dockerfile
ONBUILD <其他 Dockerfile 指令>

# 示例:创建基础镜像,子镜像构建时自动执行
ONBUILD COPY . /app
ONBUILD RUN npm install
  • 只在作为其他镜像的基础镜像时触发
  • 适用于创建通用基础镜像

SHELL

dockerfile
SHELL ["executable", "parameters"]

# 示例:修改默认 shell
SHELL ["/bin/bash", "-c"]
SHELL ["powershell", "-command"]  # Windows

# 修改后影响后续的 RUN、CMD、ENTRYPOINT(shell 格式)
SHELL ["/bin/bash", "-c"]
RUN echo "using bash"

综合示例

dockerfile
# ==== 多阶段构建:生产级 Node.js 应用 ====

# 构建阶段
FROM node:20-alpine AS builder

WORKDIR /build

COPY package*.json ./
RUN npm ci --only=production

COPY . .
RUN npm run build

# 运行阶段
FROM node:20-alpine AS runtime

LABEL version="1.0" \
      maintainer="team@example.com"

ENV NODE_ENV=production \
    PORT=3000

# 创建非 root 用户
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

WORKDIR /app

# 从构建阶段复制结果
COPY --from=builder --chown=appuser:appgroup /build/dist ./dist
COPY --from=builder --chown=appuser:appgroup /build/node_modules ./node_modules
COPY --chown=appuser:appgroup package.json .

# 切换用户
USER appuser

EXPOSE 3000

HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \
  CMD wget -qO- http://localhost:3000/health || exit 1

CMD ["node", "dist/app.js"]

总结

熟练掌握 Dockerfile 指令是构建高质量镜像的基础。关键要点:

  1. 合并 RUN 指令减少层数
  2. 使用 .dockerignore 排除无关文件
  3. 利用 COPY 分离依赖安装和代码复制以提升缓存效率
  4. 使用 USER 切换非 root 用户提升安全性
  5. 添加 HEALTHCHECK 使容器状态可观测

下一节介绍多阶段构建来进一步优化镜像大小。