Dockerfile 指令全解析:从基础到高阶实践

Dockerfile指令全解析:从基础到高阶实践

前言

Dockerfile 是构建 Docker 镜像的核心配置文件,它定义了镜像的构建过程和最终内容。掌握 Dockerfile 的每个指令对于创建高效、安全的容器镜像至关重要。本文将详细解析每个 Dockerfile 指令,并提供实际应用场景和最佳实践。

一、基础指令详解

1. FROM - 指定基础镜像

作用:定义构建过程的基础镜像,必须是 Dockerfile 的第一个非注释指令。

语法

dockerfile

css 复制代码
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]

详细说明

  • <image>:可以是官方镜像(如 ubuntu)、私有仓库镜像(如 my-registry.com/app)或中间镜像(在多阶段构建中)
  • :<tag>:指定镜像版本,强烈建议始终明确指定版本而非使用默认的 latest
  • AS <name>:在多阶段构建中为构建阶段命名
  • --platform:指定目标平台,如 linux/amd64, linux/arm64

最佳实践

dockerfile

vbnet 复制代码
# 推荐:使用特定版本的基础镜像
FROM node:18.20.1-alpine3.19 AS builder

# 多阶段构建示例
FROM golang:1.22.4-alpine3.19 AS build-stage
FROM alpine:3.19.1 AS production-stage

# 指定平台
FROM --platform=linux/amd64 amazoncorretto:21.0.3-al2023

2. LABEL - 添加元数据

作用:为镜像添加元数据标签,用于提供镜像相关信息。

语法

dockerfile

xml 复制代码
LABEL <key>=<value> <key>=<value> <key>=<value> ...

详细说明

  • 标签是键值对形式,用于记录维护者、版本、描述等信息
  • 一个 LABEL 指令可以设置多个标签,推荐这样做以减少镜像层数
  • 标签会继承自基础镜像,可以被覆盖

最佳实践

dockerfile

ini 复制代码
# 单指令多标签(推荐)
LABEL maintainer="team@example.com" \
      version="1.2.3" \
      description="Production API service" \
      org.opencontainers.image.authors="Dev Team <dev@example.com>" \
      org.opencontainers.image.version="v1.2.3" \
      org.opencontainers.image.licenses="MIT"

# 符合 Open Containers Initiative 标准的标签
LABEL org.opencontainers.image.title="My Application" \
      org.opencontainers.image.description="A custom application" \
      org.opencontainers.image.url="https://example.com" \
      org.opencontainers.image.source="https://github.com/example/repo" \
      org.opencontainers.image.vendor="Example Inc."

3. WORKDIR - 设置工作目录

作用:设置后续指令的工作目录,如果目录不存在会自动创建。

语法

dockerfile

bash 复制代码
WORKDIR /path/to/workdir

详细说明

  • 相当于在容器内执行 mkdir -p /pathcd /path
  • 后续的 RUN, CMD, ENTRYPOINT, COPY, ADD 指令都会在此目录下执行
  • 可以使用相对路径,相对的是前一个 WORKDIR 指令设置的路径
  • 可以多次使用,每次都会改变当前工作目录

最佳实践

dockerfile

bash 复制代码
WORKDIR /app

# 使用相对路径
WORKDIR src
WORKDIR ./api
# 当前工作目录为 /app/src/api

# 清晰的工作目录结构
WORKDIR /usr/src/app
COPY package.json ./
RUN npm install
COPY . .

4. RUN - 执行命令

作用:在构建过程中执行命令并创建新的镜像层。

语法

dockerfile

bash 复制代码
# Shell 格式(默认使用 /bin/sh -c)
RUN <command>

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

详细说明

  • Shell 格式:使用默认 shell 执行命令,支持变量替换和通配符
  • Exec 格式:直接执行命令,不通过 shell,需要提供可执行文件的完整路径
  • 每个 RUN 指令都会创建一个新的镜像层,应尽量减少层数

最佳实践

dockerfile

bash 复制代码
# 不良实践:多个RUN指令创建多层镜像
RUN apt-get update
RUN apt-get install -y package1
RUN apt-get install -y package2
RUN rm -rf /var/lib/apt/lists/*

# 最佳实践:合并指令,清理缓存
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
        package1 \
        package2 \
        package3 && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

# 使用exec格式避免shell处理问题
RUN ["/bin/bash", "-c", "echo $HOME"]

# 复杂的多行命令
RUN set -eux; \
    export DEBIAN_FRONTEND=noninteractive; \
    apt-get update; \
    apt-get install -y --no-install-recommends \
        ca-certificates \
        curl; \
    rm -rf /var/lib/apt/lists/*; \
    curl -fsSL https://package-url -o package.tar.gz; \
    tar -xzf package.tar.gz -C /usr/local/bin; \
    rm package.tar.gz

5. CMD - 容器默认命令

作用 :指定容器启动时的默认执行命令,可以被 docker run 后面的参数覆盖。

语法

dockerfile

bash 复制代码
# Shell 格式
CMD command param1 param2

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

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

详细说明

  • 一个 Dockerfile 中只能有一个 CMD 指令,如果有多个,只有最后一个生效
  • 主要目的是为容器提供默认的执行命令
  • 如果 Dockerfile 中有 ENTRYPOINT,则 CMD 作为其参数
  • 启动容器时在命令行指定的参数会覆盖 CMD 的内容

最佳实践

dockerfile

bash 复制代码
# Exec格式,避免shell处理,支持信号传递
CMD ["nginx", "-g", "daemon off;"]

# 为ENTRYPOINT提供默认参数
ENTRYPOINT ["java", "-jar"]
CMD ["app.jar"]

# 使用shell格式的情况(需要环境变量扩展时)
CMD echo "The current user is $USER"

6. ENTRYPOINT - 入口点

作用 :配置容器启动时运行的命令,不容易被 docker run 的参数覆盖。

语法

dockerfile

bash 复制代码
# Shell 格式
ENTRYPOINT command param1 param2

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

详细说明

  • Shell 格式:命令会在 /bin/sh -c 中执行,不支持信号传递,CMD 参数会被忽略
  • Exec 格式:直接执行命令,支持信号传递,CMD 的内容会作为参数传递给 ENTRYPOINT
  • 使用 --entrypoint 标志可以覆盖 ENTRYPOINT

最佳实践

dockerfile

css 复制代码
# 包装脚本模式
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]

# 与CMD配合使用
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar"]
CMD ["app.jar"]

# 可执行文件直接作为入口点
ENTRYPOINT ["/app/bin/my-app"]

7. COPY - 复制文件

作用:将构建上下文中的文件或目录复制到镜像中。

语法

dockerfile

xml 复制代码
COPY [--chown=<user>:<group>] [--chmod=<perms>] <src>... <dest>
COPY [--chown=<user>:<group>] [--chmod=<perms>] ["<src>",... "<dest>"]

详细说明

  • <src>:源文件或目录,相对于构建上下文
  • <dest>:目标路径,可以是绝对路径或相对于 WORKDIR 的相对路径
  • --chown:设置复制文件的所有权和组
  • --chmod:设置复制文件的权限
  • 支持通配符匹配多个文件

最佳实践

dockerfile

bash 复制代码
# 复制单个文件
COPY package.json ./

# 复制目录
COPY src ./src

# 设置所有权和权限
COPY --chown=node:node --chmod=644 app.js /app/
COPY --chown=1000:1000 data/ /app/data/

# 使用通配符
COPY *.txt /app/texts/
COPY config/*.conf /etc/app/

# 分阶段复制,利用Docker缓存
COPY package.json package-lock.json ./
RUN npm install
COPY . .

8. ADD - 高级复制

作用 :比 COPY 功能更丰富的复制指令,支持自动解压和 URL 下载。

语法

dockerfile

xml 复制代码
ADD [--chown=<user>:<group>] [--chmod=<perms>] <src>... <dest>
ADD [--chown=<user>:<group>] [--chmod=<perms>] ["<src>",... "<dest>"]

详细说明

  • 具备 COPY 的所有功能
  • 如果 <src> 是本地压缩文件(tar, gzip, bzip2, xz等),会自动解压到 <dest>
  • 支持从 URL 下载文件(但不推荐,因为无法清理下载的临时文件)

最佳实践

dockerfile

bash 复制代码
# 自动解压tar包(ADD的主要优势)
ADD application.tar.gz /app/

# 从URL下载(不推荐,因为无法删除下载缓存)
ADD https://example.com/file.tar.gz /tmp/ # 尽量避免

# 设置权限
ADD --chown=app:app --chmod=755 bin/app /usr/local/bin/

# 一般情况下优先使用COPY
COPY file.txt /app/  # 推荐
ADD file.txt /app/   # 不推荐,功能过于复杂

二、高级指令详解

9. ENV - 环境变量

作用:设置环境变量,在构建阶段和容器运行时都可用。

语法

dockerfile

xml 复制代码
ENV <key>=<value> ...

详细说明

  • 设置的环境变量在构建过程中和容器运行时都可以使用
  • 可以使用 ${VARIABLE}$VARIABLE 语法引用已定义的环境变量
  • 支持一次设置多个环境变量

最佳实践

dockerfile

ini 复制代码
# 一次设置多个变量
ENV APP_HOME=/app \
    NODE_ENV=production \
    PORT=3000

# 引用已定义的变量
ENV PATH=$APP_HOME/bin:$PATH

# 应用程序配置
ENV DB_HOST=database \
    DB_PORT=5432 \
    REDIS_URL=redis://cache:6379

# 版本信息
ENV APP_VERSION=1.2.3 \
    BUILD_DATE=2024-05-20

10. ARG - 构建参数

作用:定义在构建过程中使用的变量,只在构建阶段有效。

语法

dockerfile

xml 复制代码
ARG <name>[=<default value>]

详细说明

  • 只在构建阶段有效,容器运行时不可用
  • 可以通过 --build-arg <varname>=<value> 在构建时传递参数
  • 可以有默认值,如果没有默认值且构建时未提供,则值为空
  • ARG 指令有作用域,只在定义之后的有效

最佳实践

dockerfile

ini 复制代码
# 定义构建参数
ARG APP_VERSION=latest
ARG BUILD_NUMBER=1
ARG NPM_TOKEN

# 使用构建参数
LABEL version=$APP_VERSION \
      build=$BUILD_NUMBER

# 在RUN指令中使用
RUN echo "Building version $APP_VERSION" && \
    npm install

# 多阶段构建中传递参数
FROM alpine AS final
ARG APP_VERSION
COPY --from=build /app /app

11. EXPOSE - 声明端口

作用:声明容器运行时监听的网络端口,是一种文档化手段。

语法

dockerfile

xml 复制代码
EXPOSE <port> [<port>/<protocol>...]

详细说明

  • 只是声明容器会使用哪些端口,并不实际发布端口
  • 默认协议是 TCP,可以明确指定 TCP 或 UDP
  • 实际端口映射需要在 docker run 时使用 -p 参数指定
  • 有助于镜像使用者了解需要映射哪些端口

最佳实践

dockerfile

yaml 复制代码
# 声明单个端口
EXPOSE 80

# 声明多个端口
EXPOSE 80 443

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

# Web应用典型配置
EXPOSE 3000

# 数据库应用
EXPOSE 5432

# 微服务应用
EXPOSE 8080 8443

12. VOLUME - 定义卷

作用:创建挂载点,用于持久化数据和共享数据。

语法

dockerfile

css 复制代码
VOLUME ["/path/to/dir"]
VOLUME /path/to/dir

详细说明

  • 创建匿名卷,用于存储需要持久化的数据
  • 在容器运行时自动创建并挂载匿名卷
  • 主要用于数据库文件、日志文件等需要持久化的数据
  • 可以在 docker run 时使用 -v 覆盖

最佳实践

dockerfile

bash 复制代码
# 数据库数据目录
VOLUME /var/lib/mysql

# 应用程序日志
VOLUME /var/log/app

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

# 避免在VOLUME目录后进行写操作
RUN mkdir -p /data && chown app:app /data
VOLUME /data

13. USER - 设置用户

作用:指定运行后续指令的用户身份,以及容器运行时的默认用户。

语法

dockerfile

ruby 复制代码
USER <user>[:<group>]
USER <UID>[:<GID>]

详细说明

  • 切换后续指令的执行用户身份
  • 影响 RUN, CMD, ENTRYPOINT 指令的执行身份
  • 建议使用非 root 用户运行应用程序以提高安全性
  • 用户和组必须已存在,可以在之前用 RUN 指令创建

最佳实践

dockerfile

yaml 复制代码
# 创建应用程序用户
RUN addgroup -g 1000 app && \
    adduser -u 1000 -G app -D app

# 切换用户
USER app

# 使用UID/GID(更可靠)
USER 1000:1000

# 多阶段构建中切换用户
FROM node:18-alpine AS build
RUN adduser -D app
USER app
WORKDIR /home/app
COPY --chown=app:app . .

14. HEALTHCHECK - 健康检查

作用:定义容器健康检查命令,用于检测容器是否正常工作。

语法

dockerfile

css 复制代码
HEALTHCHECK [OPTIONS] CMD command
HEALTHCHECK NONE

选项说明

  • --interval:检查间隔(默认 30s)
  • --timeout:命令超时时间(默认 30s)
  • --start-period:容器启动阶段的初始化时间(默认 0s)
  • --retries:连续失败次数后标记为不健康(默认 3)

最佳实践

dockerfile

bash 复制代码
# HTTP服务健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD curl -f http://localhost:3000/health || exit 1

# 命令检查
HEALTHCHECK --interval=1m --timeout=10s \
    CMD pg_isready -U postgres -d mydb || exit 1

# 脚本检查
HEALTHCHECK CMD /healthcheck.sh

# 禁用健康检查
HEALTHCHECK NONE

三、特殊用途指令

15. SHELL - 更改默认 shell

作用 :覆盖默认的 shell 程序,用于改变 RUNCMDENTRYPOINT 的 shell 格式使用的 shell。

语法

dockerfile

css 复制代码
SHELL ["executable", "parameters"]

最佳实践

dockerfile

bash 复制代码
# 使用PowerShell(Windows容器)
SHELL ["powershell", "-Command"]

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

# 使用更安全的shell选项
SHELL ["/bin/bash", "-o", "pipefail", "-c"]

16. STOPSIGNAL - 停止信号

作用:设置容器停止时发送的系统调用信号。

语法

dockerfile

复制代码
STOPSIGNAL signal

最佳实践

dockerfile

bash 复制代码
# 使用SIGTERM(默认)
STOPSIGNAL SIGTERM

# 使用SIGINT
STOPSIGNAL SIGINT

# 使用特定信号值
STOPSIGNAL 15

17. ONBUILD - 延迟执行

作用:添加延迟执行的指令,当当前镜像被作为基础镜像时,这些指令会在子镜像的构建过程中执行。

语法

dockerfile

xml 复制代码
ONBUILD <INSTRUCTION>

最佳实践

dockerfile

bash 复制代码
# 基础镜像中的ONBUILD指令
ONBUILD COPY package.json ./
ONBUILD RUN npm install
ONBUILD COPY . .

# 应用特定的ONBUILD
ONBUILD ADD https://example.com/scripts/setup.sh /tmp/
ONBUILD RUN chmod +x /tmp/setup.sh && /tmp/setup.sh

四、综合示例与实践建议

完整示例:Node.js 应用 Dockerfile

dockerfile

bash 复制代码
# 多阶段构建:构建阶段
FROM node:18.20.1-alpine3.19 AS builder

# 设置构建参数
ARG APP_VERSION=1.0.0
ARG NPM_TOKEN

# 设置环境变量
ENV NODE_ENV=production \
    CI=true

# 创建应用程序用户
RUN addgroup -g 1001 app && \
    adduser -u 1001 -G app -D app

# 设置工作目录
WORKDIR /app

# 复制依赖文件
COPY package.json package-lock.json* ./

# 设置npm认证(注意安全!)
RUN if [ -n "$NPM_TOKEN" ]; then \
        echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc; \
    fi

# 安装依赖
RUN npm ci --only=production && \
    if [ -f .npmrc ]; then rm .npmrc; fi

# 复制源代码
COPY --chown=app:app . .

# 构建应用
RUN npm run build

# 多阶段构建:生产阶段
FROM node:18.20.1-alpine3.19 AS production

# 添加元数据
LABEL maintainer="dev@example.com" \
      version="$APP_VERSION" \
      description="Node.js application"

# 创建非root用户
RUN addgroup -g 1001 app && \
    adduser -u 1001 -G app -D app

# 设置工作目录
WORKDIR /app

# 安装运行时依赖
RUN apk add --no-cache tini

# 复制构建产物
COPY --from=builder --chown=app:app /app/node_modules ./node_modules
COPY --from=builder --chown=app:app /app/dist ./dist
COPY --from=builder --chown=app:app /app/package.json ./

# 创建数据卷
VOLUME /app/logs
VOLUME /app/uploads

# 声明端口
EXPOSE 3000

# 设置健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD node healthcheck.js

# 使用非root用户
USER app

# 设置入口点
ENTRYPOINT ["/sbin/tini", "--"]

# 设置启动命令
CMD ["node", "dist/index.js"]

通用最佳实践总结

  1. 使用多阶段构建:减少最终镜像大小,提高安全性
  2. 选择合适的基础镜像:使用官方、轻量级、特定版本的基础镜像
  3. 优化层缓存:将不经常变化的指令放在前面,经常变化的放在后面
  4. 使用非 root 用户:提高容器运行时的安全性
  5. 清理不必要的文件:在同一个 RUN 指令中安装软件和清理缓存
  6. 使用 .dockerignore:排除不必要的文件,减少构建上下文大小
  7. 明确标签和版本:为镜像提供清晰的元数据和版本信息
  8. 健康检查:为长时间运行的服务添加健康检查机制
  9. 信号处理:确保应用正确处理停止信号,实现优雅关闭
  10. 安全扫描:定期对镜像进行安全漏洞扫描

通过深入理解和正确使用这些 Dockerfile 指令,你可以创建出高效、安全、可维护的 Docker 镜像,为容器化部署奠定坚实基础。

相关推荐
Cache技术分享5 小时前
178. Java 包
前端·javascript·后端
阴晦5 小时前
llm与RAG的学习与优化
后端
心月狐的流火号5 小时前
详解Java内存模型(JMM)
java·后端
就叫飞六吧5 小时前
企业级主流日志系统架构对比ELKK Stack -Grafana Stack
后端·ubuntu·系统架构
CryptoRzz5 小时前
使用Spring Boot对接印度股票市场API开发实践
后端
River4165 小时前
Javer 学 c++(九):结构体篇
c++·后端
日月卿_宇5 小时前
分布式事务
java·后端
齐 飞5 小时前
XXL-JOB快速入门
spring boot·后端·spring cloud
MacroZheng5 小时前
别再用 BeanUtils 了,这款 PO VO DTO 转换神器不香么?
java·spring boot·后端