这取决于你想要达到的效果。
请注意,就语用学(即开发人员实际说话的方式)而言,“取消设置变量”可能意味着两件事:将其从环境中删除,或将变量设置为空值。从技术上讲,这是两种不同的操作。在实践中,虽然我没有遇到我试图控制的软件区分环境中不存在的变量和环境中存在但设置为空值的变量的情况。我通常可以使用任何一种方法来获得相同的结果。
如果您不关心变量是否在 Docker 生成的层中,但将其保留为非空值会导致后续构建步骤出现问题。
对于这种情况,您可以在 Dockerfile 中要取消设置变量的位置使用 ENV VAR_NAME=。语法说明:Docker 允许ENV 有两种语法:这个ENV VAR=1 与ENV VAR 1 相同。您可以使用空格或等号将变量名称与值分开。当您想通过将变量设置为空值来“取消设置”变量时,您必须使用等号语法,否则在构建时会出错。
例如,你可以这样做:
ENV NOT_SENSITIVE some_value
RUN something
ENV NOT_SENSITIVE=
RUN something_else
当something 运行时,NOT_SENSITIVE 设置为some_value。当something_else 运行时,NOT_SENSITIVE 被设置为空字符串。
请务必注意,将 unset NOT_SENSITIVE 作为 shell 命令执行不会影响除此 shell 中执行的内容之外的任何其他内容。这是一个示例:
ENV NOT_SENSITIVE some_value
RUN unset NOT_SENSITIVE && printenv NOT_SENSITIVE || echo "does not exist"
RUN printenv NOT_SENSITIVE
第一个RUN 将打印does not exist,因为NOT_SENSITIVE 在printenv 执行时未设置,并且因为它未设置printenv 返回一个非零退出代码,导致echo 执行。第二个RUN 不受第一个RUN 中的unset 的影响。它将在屏幕上打印some_value。
但是如果我需要从环境中移除变量,而不仅仅是将其设置为空值呢?
在这种情况下使用ENV VAR_NAME= 将不起作用。我不知道有什么方法可以告诉 Docker“从现在开始,你必须从环境中删除这个变量,而不仅仅是将它设置为空值”。
如果您仍想使用ENV 来设置变量,那么您必须在每个RUN 开始,您希望在其中使用unset VAR_NAME 取消设置变量,这将为 取消设置 仅限于RUN。
如果要防止变量出现在 Docker 生成的层中。
假设该变量包含一个秘密,并且该层可能落入不应该拥有该秘密的人手中。在这种情况下,您不能使用ENV 来设置变量。带有ENV 的变量集被烘焙到它应用的层中,并且不能从这些层中删除。特别是(假设变量名为SENSITIVE)正在运行
RUN unset SENSITIVE
不做任何事情将其从层中删除。上面的unset 命令仅从RUN 启动的shell 进程中删除SENSITIVE。它只影响那个外壳。它不会影响由CMD、ENTRYPOINT 或通过在命令行运行docker run 提供的任何命令生成的shell。
为了防止层包含秘密,我会使用docker build --secret= 和RUN --mount=type=secret...。例如,假设我已经将我的秘密存储在一个名为 sensitive 的文件中,我可以有一个像这样的 RUN:
RUN --mount=type=secret,id=sensitive,target=/root/sensitive \
export SENSITIVE=$(cat /root/sensitive) \
&& [[... do stuff that requires SENSITIVE ]] \
请注意,给RUN 的命令不需要以unset SENSITIVE 结尾。 由于进程及其环境的管理方式,在@ 生成的shell 中设置SENSITIVE 987654366@ 除了外壳本身产生的效果外,没有任何影响。此 shell 中的环境更改不会影响未来的 shell,也不会影响 Docker 在其创建的层中烘焙的内容。
然后构建可以运行:
$ DOCKER_BUILDKIT=1 docker build --secret id=secret,src=path/to/sensitive [...]
docker build 命令的环境需要DOCKER_BUILDKIT=1 才能使用 BuildKit,因为这种传递秘密的方法只有在 Docker 使用 BuildKit 构建镜像时才可用。