PAYATHON 2026

Dockerfile 中通过 build-arg 传入的变量为什么未写入 ENV?

数据小许

结论

在 FROM 后声明 ARG PACKAGE_VERSION,当前构建阶段才能使用传入的参数。随后,通过 ENV PACKAGE_VERSION=$PACKAGE_VERSION 将该值保存为镜像的环境变量。

原来的 RUN [ "echo", "Building Jenkins v${PACKAGE_VERSION}" ] 使用 exec 形式,不会启动 shell,因此 ${PACKAGE_VERSION} 不会展开。要打印变量值,可以改用 shell 形式,也可以显式调用 shell。

为什么传入了参数,仍然提示未定义?

FROM 之前声明的 ARG 属于全局作用域,可以用于 FROM,但不会自动进入 FROM 开始的构建阶段。

原来的结构是:

ARG PACKAGE_VERSION
FROM myupstream_image:latest

ENV PACKAGE_VERSION=$PACKAGE_VERSION

执行 ENV 时,当前阶段尚未声明 ARG PACKAGE_VERSION,因此无法直接取得通过 --build-arg 传入的值。从给出的警告和环境输出看,这次构建将 PACKAGE_VERSION 设为了空字符串。

--build-arg 负责提供参数值,Dockerfile 中的 ARG 声明决定哪些位置可以使用这个值。

如何修改 Dockerfile?

如果 PACKAGE_VERSION 只在当前阶段使用,将 ARG 移到 FROM 后即可:

FROM myupstream_image:latest
ARG PACKAGE_VERSION
LABEL MAINTAINER "Sagar Khushalani <sagar.khushalani@garmin.com>"

ENV PACKAGE_VERSION=$PACKAGE_VERSION

RUN echo "Building Jenkins v${PACKAGE_VERSION}"

如果还需要在 FROM 中引用这个参数,可以保留全局声明,并在阶段内再次声明:

ARG PACKAGE_VERSION
FROM myupstream_image:latest
ARG PACKAGE_VERSION

ENV PACKAGE_VERSION=$PACKAGE_VERSION

RUN echo "Building Jenkins v${PACKAGE_VERSION}"

阶段内再次声明时不必重复写默认值;它可以取得构建命令传入的参数值。

为什么 exec 形式的 RUN 不展开变量?

下面的写法直接执行 echo,${PACKAGE_VERSION} 会作为普通字符串传给它:

RUN ["echo", "Building Jenkins v${PACKAGE_VERSION}"]

即使修复了 ARG 的作用域,这条命令仍不会自动展开变量。可以使用前面示例中的 shell 形式,也可以显式调用 shell:

RUN ["sh", "-c", "echo \"Building Jenkins v${PACKAGE_VERSION}\""]

后一种写法要求基础镜像提供 sh。

如何验证修改结果?

重新构建镜像:

docker buildx build --build-arg PACKAGE_VERSION=9 --tag myimage:9 .

如果使用的 Buildx builder 没有自动将构建结果加载到本地 Docker 镜像存储,可以为这次本地验证添加 --load:

docker buildx build --load --build-arg PACKAGE_VERSION=9 --tag myimage:9 .

如果基础镜像提供 env 命令,可以检查新镜像的环境变量:

docker run --rm --entrypoint env myimage:9

预期输出中应包含:

PACKAGE_VERSION=9

ARG 本身不会成为容器运行时的环境变量;这里通过 ENV 将它保存下来。验证时,还要确认运行的是本次构建的镜像,并且没有在启动容器时覆盖 PACKAGE_VERSION。

备注:内容仅供参考。