Skip to content

Dockerfile 中的 ARG 和 ENV 详解

在 Dockerfile 中,ARGENV 都用于定义变量,但它们的作用域生命周期完全不同。理解两者的区别对编写灵活、安全的 Dockerfile 至关重要。


1. 基本定义

  • ARG构建时变量,仅在 docker build 过程中有效。构建完成后不会保留在镜像中。
  • ENV环境变量,在构建时和运行时都有效。最终会作为容器内的环境变量存在。

2. 语法

dockerfile
ARG <变量名>[=<默认值>]
ENV <key>=<value> ...

示例

dockerfile
ARG VERSION=latest
ENV NODE_ENV=production \
    APP_HOME=/app
  • ARG 可定义默认值;如果未提供默认值,则默认为空字符串。
  • ENV 中的 value 会被视为字符串;如果需要使用其他变量,可以用 ${VAR} 引用(但 ENV 不能跨指令引用 ARG?实际上可以,但需注意顺序)。

3. 作用域与生命周期

特性ARGENV
存在阶段仅构建时构建时 + 运行时
是否保留在最终镜像中❌ 否✅ 是(作为环境变量)
能否被 docker run -e 覆盖❌ 否(构建后消失)✅ 是(启动容器时可覆盖)
能否被 docker build --build-arg 覆盖✅ 是❌ 否
用途参数化构建(如版本号、镜像仓库)配置应用环境(如数据库连接、模式)

关键点

  • ARG 只在 FROMRUNCOPYADDWORKDIRUSERLABEL 等构建指令中生效(CMDENTRYPOINT不能直接使用 ARG,除非通过 ENV 传递)。
  • ENV 不仅影响构建过程(后续指令中的 RUN 等),还会作为容器的环境变量持久化。

4. 如何传递值

构建时(docker build

  • ARG 赋值:docker build --build-arg <变量名>=<值>

  • 无法直接为 ENV 赋值,但可以通过 ARG 间接设置:

    dockerfile
    ARG APP_VERSION
    ENV APP_VERSION=${APP_VERSION}

    这样构建时传入的 APP_VERSION 会通过 ARG 接收,再赋值给 ENV,最终保留在镜像中。

运行时(docker run

  • 覆盖 ENVdocker run -e <变量名>=<新值>
  • ARG 已不存在,无法覆盖。

5. 详细对比表

对比项ARGENV
定义默认值ARG VERSION=1.0ENV VERSION=1.0
构建时引用可在 RUN 等指令中用 $VERSION也可用 $VERSION
是否影响缓存ARG 值变化时,后续依赖它的指令会缓存失效ENV 值变化时,后续所有指令缓存失效(因为 ENV 是层的一部分)
安全性适合存储构建时敏感信息?不推荐,因为 docker history 仍会显示绝对不要存储敏感信息,会留在镜像中
推荐用途控制构建行为(如是否启用某个功能、选择基础镜像标签)配置运行时应用(如 DATABASE_URLLOG_LEVEL

6. 使用场景示例

场景1:动态指定基础镜像版本

dockerfile
ARG UBUNTU_VERSION=22.04
FROM ubuntu:${UBUNTU_VERSION}

RUN apt-get update && apt-get install -y curl

构建时:docker build --build-arg UBUNTU_VERSION=20.04 -t myimage .

场景2:传递构建版本号并保留到镜像中

dockerfile
ARG BUILD_VERSION
ENV APP_VERSION=${BUILD_VERSION}
RUN echo "Building version ${APP_VERSION}"

运行时:docker run -e APP_VERSION=2.0 myimage 可覆盖。

场景3:为不同环境构建(开发 vs 生产)

dockerfile
ARG ENV_TYPE=production
ENV NODE_ENV=${ENV_TYPE}

RUN if [ "$NODE_ENV" = "development" ]; then \
        npm install --dev; \
    else \
        npm install --production; \
    fi

构建开发镜像:docker build --build-arg ENV_TYPE=development -t myapp:dev .

场景4:使用 ARG 控制缓存(避免破坏缓存)

dockerfile
ARG CACHE_BUST=1
RUN curl -fsSL https://example.com/package.tar.gz | tar xz

每次构建时传入不同的 CACHE_BUST(如时间戳),强制 RUN 重新执行。


7. 最佳实践

✅ 推荐做法

  1. 优先使用 ARG 控制构建行为,不污染最终镜像。

  2. 将运行时配置(如数据库连接)交给 ENV,以便通过 -e--env-file 灵活修改。

  3. 敏感信息(密码、令牌)不要用 ENV,而应使用 Docker secrets 或运行时注入(如 -e 从外部传入)。

  4. 使用 ARGENV 提供默认值,兼顾构建灵活性和运行时可配置性:

    dockerfile
    ARG DEFAULT_PORT=8080
    ENV PORT=$DEFAULT_PORT
  5. 利用 ARG 破坏缓存:在需要强制刷新某层时,传入一个随机值(如 --build-arg BUILD_DATE=$(date -Iseconds))。

  6. 在 Dockerfile 开头声明所有 ARG,避免在 FROM 之后意外覆盖(ARGFROM 前声明可用于选择基础镜像)。

❌ 避免的陷阱

  • 不要将 ENV 用于构建专用参数,这些值会泄露到最终镜像,增加体积和暴露风险。
  • 不要在 CMDENTRYPOINT 中直接使用 ARG,它不可用。必须通过 ENV 传递或使用 shell 脚本包装。
  • 注意 ARG 的作用域:每个 FROM 指令会重置 ARG(多阶段构建时)。在多阶段中传递 ARG 需要重复声明。
  • ENV 值一旦设置,后续所有层都会包含它,修改 ENV 会使缓存大面积失效。

8. 验证示例

Dockerfile

dockerfile
ARG BUILD_TIME
ENV RUNTIME_VAR="default"

RUN echo "Build arg: ${BUILD_TIME}" > /build-info.txt
RUN echo "Runtime var: ${RUNTIME_VAR}" > /runtime-info.txt

构建

bash
docker build --build-arg BUILD_TIME="2025-01-01" -t test .

检查

bash
docker run --rm test cat /build-info.txt   # 输出 "Build arg: 2025-01-01"
docker run --rm test cat /runtime-info.txt # 输出 "Runtime var: default"
docker run --rm -e RUNTIME_VAR="override" test printenv RUNTIME_VAR # 输出 "override"

总结

  • ARG 是构建时的“参数”,用完即弃,适合控制构建流程。
  • ENV 是环境变量,永久保留,适合配置运行环境。
  • 两者配合使用可实现灵活、安全的 Docker 镜像:用 ARG 接收构建时输入,通过 ENV 传递给运行时。