DockerFile-ARG值为空的问题
文章翻译自: https://qmacro.org/blog/posts/2024/05/13/using-arg-in-a-dockerfile-beware-the-gotcha/
先说结论:
“在
FROM
之前的ARG
声明是构建阶段之外的,因此它不能在FROM
之后的任何指令中使用。” 换句话说,用ARG
声明的变量看起来像是脚本中的变量,这些变量通常也在开始时声明,然后在脚本中使用。
ARG
值为空的问题
作者花费了不少时间来理解为什么他自定义构建的CAP Node.js容器镜像的CAP版本并不是他指定的版本,无论是在Dockerfile中通过ARG
指令隐式声明的默认值,还是通过命令行的--build-arg
选项显式指定的值。
为了说明问题,作者提供了一个简化版的Dockerfile:
# syntax=docker/dockerfile:1
ARG DEBVER="10"
ARG CAPVER="7.8"
FROM debian:${DEBVER}
RUN printf "DEB=${DEBVER}\nCAP=${CAPVER}\n" > /tmp/log
这里声明的第一个变量DEBVER
是一个常见的用例,允许使用不同版本的基础镜像,这里的例子是从不同版本的Debian发行版开始,其默认版本是10。
第二个变量CAPVER
也是类似的,作者在Dockerfile后面的构建指令中使用它来指定想要安装的CAP的特定版本。实际的指令看起来像这样:RUN npm install -g @sap/cds-dk@{CAPVER}
。
基于这个简化的Dockerfile构建镜像,没有使用--build-arg
显式指定任何值:
docker build -t argtest .
作者可以成功确认从这个镜像创建的容器中的Debian版本是10:
docker run --rm argtest grep VERSION_ID /etc/os-release
VERSION_ID="10"
但是/tmp/log
的内容呢?
docker run --rm argtest cat /tmp/log
DEB=
CAP=
使用--build-arg
选项又如何呢?
docker build \
--build-arg="DEBVER=11" \
--build-arg="CAPVER=7.9" \
-t argtest .
构建成功完成,现在可以看到容器基于Debian 11:
docker run --rm argtest grep VERSION_ID /etc/os-release
VERSION_ID="11"
但是DEBVER
和CAPVER
在/tmp/log
中的空值问题仍然存在。
不仅在RUN
指令中引用CAPVER
的值是空的,而且,这是迄今为止最神秘的事情,尽管DEBVER
在FROM
指令中确实被识别并设置为11,用于Debian发行版,但在FROM
指令之后的RUN
指令中引用它时却是空的。
ARG
与FROM
之间的关系微妙之处
原因是ARG
和FROM
之间相当微妙的关系,这个解释简短而有点隐藏在主要的Dockerfile参考文档中。
作者在直接查找ARG
的参考时错过了这一点,因为它没有被提及,只有在FROM
的参考文档的末尾才解释,而FROM
的参考文档在页面上更靠前。
关键部分在这里:理解ARG
和FROM
如何反应,并包括这句话:
“在
FROM
之前的ARG
声明是构建阶段之外的,因此它不能在FROM
之后的任何指令中使用。” 换句话说,用ARG
声明的变量看起来像是脚本中的变量,这些变量通常也在开始时声明,然后在脚本中使用。
但它们不是。
解决方案
必须对上面的Dockerfile进行修改,使其看起来像这样:
# syntax=docker/dockerfile:1
ARG DEBVER="10"
FROM debian:${DEBVER}
ARG DEBVER
ARG CAPVER="7.8"
RUN printf "DEB=${DEBVER}\nCAP=${CAPVER}\n" > /tmp/log
将CAPVER
的ARG
指令移动到FROM
指令之后,使其具有生命力和有效性。
DEBVER
的ARG
指令必须保持在原来的位置(因为它在FROM
指令的详细信息中被引用),但如果需要在FROM
指令之后引用它,它必须再次被引用 - 因此是ARG DEBVER
这一行。
使用这个新版本的Dockerfile构建的容器镜像,如果没有指定--build-arg
选项,我们可以看到DEBVER
和CAPVER
的值在FROM
指令之后是可用的:
docker run --rm argtest cat /tmp/log
DEB=10
CAP=7.8
当然,如果我们在命令行上设置构建参数的值,这也适用,测试使用相同的docker build --build-arg ...
命令构建的容器:
docker run --rm argtest cat /tmp/log
DEB=11
CAP=7.9
以及
docker run --rm argtest grep VERSION_ID /etc/os-release
VERSION_ID="11"