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。
备注:内容仅供参考。
