如何在dockerfile中取消设置“ ENV”?

Kim*_*Kim 5 docker dockerfile

由于某些原因,我必须在dockerfile中设置“ http_proxy”和“ https_proxy” ENV。我现在想取消设置它们,因为还有一些构建过程无法通过代理完成。

# dockerfile

# ... some process

ENV http_proxy=http://...
ENV https_proxy=http://...

# ... some process that needs the proxy to finish

UNSET ENV http_proxy # how to I unset the proxy ENV here?
UNSET ENV https_proxy

# ... some process that can't use the proxy 

Run Code Online (Sandbox Code Playgroud)

Lou*_*uis 20

这取决于您要达到的效果。

请注意,就语用学(即开发人员实际如何说话)而言,“取消设置变量”可能意味着两件事:将其从环境中删除,或将变量设置为空值。从技术上讲,这是两种不同的操作。在实践中,虽然我没有遇到过这样的情况,我试图控制的软件区分环境中不存在的变量和环境中存在但设置为空值的变量。我通常可以使用任何一种方法来获得相同的结果。

如果您不关心变量是否在 Docker 生成的层中,但将其保留为非空值会导致后面的构建步骤出现问题。

对于这种情况,您可以ENV VAR_NAME=在 Dockerfile 中要从中取消设置变量的点使用。语法说明:Docker 允许以下两种语法ENV: 这ENV VAR=1ENV VAR 1. 您可以使用空格或等号将变量名称与值分开。当您想通过将变量设置为空值来“取消设置”变量时,您必须使用等号语法,否则在构建时会出现错误。

例如,你可以这样做:

ENV NOT_SENSITIVE some_value
RUN something

ENV NOT_SENSITIVE=
RUN something_else
Run Code Online (Sandbox Code Playgroud)

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 Code Online (Sandbox Code Playgroud)

第一个RUN将打印,does not exist因为NOT_SENSITIVEprintenv执行时未设置,并且因为它未设置printenv返回一个非零退出代码,导致echo执行。第二个RUN不受unset第一个的影响RUN。它将打印some_value到屏幕上。

但是如果我需要从环境中删除变量,而不仅仅是将其设置为空值呢?

在这种情况下使用ENV VAR_NAME=将不起作用。我不知道有什么方法可以告诉 Docker“从现在开始,你必须从环境中删除这个变量,而不仅仅是将它设置为空值”。

如果您仍然想使用ENV来设置您的变量,那么您必须在每个RUN您想要取消设置变量的地方使用 开始unset VAR_NAME,这将仅针对特定情况取消设置RUN

如果要防止变量出现在 Docker 生成的层中。

假设该变量包含一个秘密,并且该层可能落入不应该拥有该秘密的人的手中。在这种情况下,您不能使用ENV来设置变量。一个变量集ENV被烘焙到它所应用的层中,并且不能从这些层中删除。特别是,(假设变量名为SENSITIVE)运行

RUN unset SENSITIVE
Run Code Online (Sandbox Code Playgroud)

不执行任何操作将其从图层中移除。unset上面的命令只SENSITIVERUN启动的 shell 进程中删除。它只影响那个外壳。它不会影响由CMDENTRYPOINT或通过docker run在命令行运行提供的任何命令产生的外壳。

为了防止图层包含秘密,我会使用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 Code Online (Sandbox Code Playgroud)

请注意,给出的命令RUN不需要以unset SENSITIVE. 由于进程及其环境的管理方式,SENSITIVE在产生的外壳中的设置RUN除了外壳本身产生的影响之外没有任何影响。此 shell 中的环境更改不会影响未来的 shell,也不会影响 Docker 烘焙到它创建的层中的内容。

然后构建可以运行:

$ DOCKER_BUILDKIT=1 docker build --secret id=secret,src=path/to/sensitive [...]
Run Code Online (Sandbox Code Playgroud)

docker build命令的环境需要DOCKER_BUILDKIT=1使用 BuildKit,因为这种传递秘密的方法只有在 Docker 使用 BuildKit 构建镜像时才可用。


Ale*_*lex 12

如果在映像构建期间需要 env vars 但它们不应该持续存在,只需清除它们。在以下示例中,正在运行的容器显示空的环境变量。

文件

# set proxy
ARG http_proxy
ARG https_proxy
ARG no_proxy
ENV http_proxy=$http_proxy
ENV https_proxy=$http_proxy
ENV no_proxy=$no_proxy

# ... do stuff that needs the proxy during the build, like apt-get, curl, et al.

# unset proxy
ENV http_proxy=
ENV https_proxy=
ENV no_proxy=
Run Code Online (Sandbox Code Playgroud)

编译文件

docker build -t the-image \
    --build-arg http_proxy="$http_proxy" \
    --build-arg https_proxy="$http_proxy" \
    --build-arg no_proxy="$no_proxy" \
    --no-cache \
    .
Run Code Online (Sandbox Code Playgroud)

运行文件

docker run --rm -i \
    the-image \
    sh << COMMANDS
        env
COMMANDS
Run Code Online (Sandbox Code Playgroud)

输出

no_proxy=
https_proxy=
http_proxy=
...
Run Code Online (Sandbox Code Playgroud)

  • 这会暴露中间层中的环境吗? (2认同)

Ere*_*ush 9

根据 docker docs,您需要改用 shell 命令:

FROM alpine
RUN export ADMIN_USER="mark" \
&& echo $ADMIN_USER > ./mark \
&& unset ADMIN_USER
CMD sh
Run Code Online (Sandbox Code Playgroud)

有关更多详细信息,请参阅https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#env


Sch*_*hLx 9

简短回答

尽量避免不必要的环境变量,这样就不需要取消设置它们。

如果您必须取消命令设置,您可以执行以下操作:

RUN unset http_proxy https_proxy no_proxy \
    && execute_your_command_here  
Run Code Online (Sandbox Code Playgroud)

如果您必须取消构建图像的设置,您可以执行以下操作:

FROM ubuntu_with_http_proxy

ENV http_proxy= \
    https_proxy= \
    no_proxy=
Run Code Online (Sandbox Code Playgroud)

一旦使用指令设置了环境变量,ENV我们就无法真正取消它们,因为它是详细的:

每个 ENV 行都会创建一个新的中间层,就像 RUN 命令一样。这意味着即使您在未来的层中取消设置环境变量,它仍然保留在该层中并且可以转储其值。

请参阅:编写 Dockerfile 的最佳实践

细节

我更喜欢在构建期间定义http_proxy为参数,如下所示:

FROM ubuntu:20.04

ARG http_proxy=http://host.docker.internal:3128 
ARG https_proxy=http://host.docker.internal:3128 
ARG no_proxy=.your.domain,localhost,127.0.0.1,.docker.internal

Run Code Online (Sandbox Code Playgroud)

在企业代理上,我们无论如何都需要身份验证,因此我们需要配置本地代理服务器,以侦听127.0.0.1:3128可从容器访问的内容host.docker.internal:3128。这样,如果我们通过 VPN 连接到公司网络(本地/家庭网络被阻止),它也可以在 docker 桌面上运行。

设置no_proxy对于避免代理服务器泛滥也很重要。

有关 no_proxy 相关主题的更多详细信息,请参阅以下文章:

有时阅读相关文档也很好:

如果我们需要配置这些环境变量,我们可以使用以下命令:

  • 在构建期间(链接):
docker build ... --build-arg http_proxy='http://alternative.proxy:3128/' ...
Run Code Online (Sandbox Code Playgroud)
docker run ... -env http_proxy='http://alternative.proxy:3128/' ...
Run Code Online (Sandbox Code Playgroud)

另请注意,我们甚至不需要定义与代理相关的参数,因为这些参数已经根据以下部分进行了预定义:

Dockerfile 参考 - 预定义的 ARG