Docker build 已传 build-arg,FROM 后的变量仍为空
Docker 构建参数传入后仍为空,可能是 ARG 只声明在首个 FROM 之前。区分全局与阶段作用域、阶段继承及 ENV 覆盖,给出默认值和显式值的验证方式。
构建命令传入了参数,镜像标签中的变量能替换,后面的 RUN 却读到空值,或者 ENV 使用了默认值。例如,下面的 Dockerfile 将 APP_MODE 放在第一个 FROM 之前:
ARG APP_MODE=development
FROM alpine:3.22
ENV APP_MODE=${APP_MODE:-development}
RUN printf 'mode=%s\n' "$APP_MODE"
即使构建时传入 --build-arg APP_MODE=production,这个构建阶段也没有声明要使用该参数。后续指令可能继续得到 development,不能据此认定参数没有传到构建器。
这里讨论当前 Dockerfile 的 ARG 作用域规则,适用于自定义构建参数。预定义代理参数有特殊行为,不要直接套用本文判断。示例中的镜像、参数和值用于说明作用域,实际项目应使用自己的已批准基础镜像。
FROM 前后是两个使用范围
第一个 FROM 前声明的 ARG 可用于 FROM 中的变量替换,但不会自动成为所有构建阶段都能使用的变量。需要在某个阶段的 RUN、ENV 等指令中使用,就在该阶段再次声明。
如果一个参数根本不用于 FROM,只需把它声明在需要它的阶段内,而且要放在第一次使用之前:
FROM alpine:3.22
ARG APP_MODE=development
RUN printf 'mode=%s\n' "$APP_MODE"
如果希望把默认值集中保留在文件顶部,在阶段内写不带默认值的声明:
ARG BASE_VERSION=3.22
ARG APP_MODE=development
FROM alpine:${BASE_VERSION}
ARG APP_MODE
RUN printf 'mode=%s\n' "$APP_MODE"
这里 BASE_VERSION 用于选择基础镜像,APP_MODE 在阶段内被显式接入。阶段内的 ARG APP_MODE 可以使用全局声明的默认值;构建命令显式传入同名参数时,使用传入值。没有必要把 =development 重复写两遍。
声明只从它出现的位置开始生效。如果 RUN 已经在前面使用了变量,后来再加 ARG 不会补救之前那条指令。参数名也必须一致,包括大小写。
多阶段构建要看继承关系
两个互不相关的阶段都需要同一个参数时,应分别声明:
ARG APP_MODE=development
FROM alpine:3.22 AS build
ARG APP_MODE
RUN printf 'build=%s\n' "$APP_MODE"
FROM alpine:3.22 AS runtime
ARG APP_MODE
RUN printf 'runtime=%s\n' "$APP_MODE"
后一个阶段不会因为写在同一文件里,就自动继承前一个阶段的 ARG。若阶段是基于前面的命名阶段构建,例如 FROM build AS child,则会继承该基础阶段已经声明的参数。这与两个阶段分别使用同一个外部镜像不同。
COPY --from=build 复制文件,也不能替代目标阶段里的参数声明。排查时找到发生问题的那一条 RUN 或 ENV,再向上确认它属于哪个 FROM、在该阶段使用变量前是否已有 ARG。
ARG 与 ENV 同名时,ENV 还会覆盖它
若加上阶段内声明仍读不到传入值,再检查同名 ENV:
FROM alpine:3.22
ARG APP_MODE
ENV APP_MODE=development
RUN printf 'mode=%s\n' "$APP_MODE"
这里固定的 ENV 值覆盖了同名 ARG。若运行容器时也确实需要这个设置,可以明确把构建参数写入镜像环境:
FROM alpine:3.22
ARG APP_MODE=development
ENV APP_MODE=${APP_MODE}
RUN printf 'mode=%s\n' "$APP_MODE"
ENV 会保留在镜像中;ARG 本身不提供容器运行时环境变量。仅构建时需要的参数,不必为了排查而永久改成 ENV。也不要把令牌、密码等秘密放进 ARG 或打印到构建日志,构建参数可能出现在镜像历史或构建证明中。
在独立构建中核对默认值和显式值
先把已经修正的最小示例保存为独立测试目录中的 Dockerfile.arg-check,然后在该目录运行:
docker build --progress=plain \
-f Dockerfile.arg-check \
-t arg-scope-check:explicit \
--build-arg APP_MODE=production .
docker build --progress=plain \
-f Dockerfile.arg-check \
-t arg-scope-check:default .
按上述示例,显式值构建应显示 production,未传值的构建应显示 development。命令会构建本地镜像,可能拉取基础镜像并使用磁盘空间;标签是独立测试名,不应替换项目正在部署的标签。测试目录不要放凭据或无关业务文件。
若对应步骤命中缓存、日志没有重新打印,可以对这个小型验证构建加 --no-cache 查看重新执行的结果。清缓存不会改变 ARG 的作用域,不需要删除全局构建缓存来修复此问题。
最小示例正常而项目仍异常时,核对实际的 -f 文件、构建目标阶段、参数名以及同名 ENV,避免只改了一个未被构建的 Dockerfile。保留原 Dockerfile 和部署镜像标识,验证通过后再改项目构建流程。回退恢复原构建文件及镜像引用;已经构建出的镜像不会随着 Dockerfile 回退而自动改变。


