DEBUG.WIKI
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国
雨云;给项目一台云服务器
RackNerd;$21.99/年 · 1GB KVM

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 回退而自动改变。