简体   繁体   English

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

[英]How to unset "ENV" in dockerfile?

For some certain reasons, I have to set "http_proxy" and "https_proxy" ENV in my dockerfile.由于某些原因,我必须在我的 dockerfile 中设置“http_proxy”和“https_proxy” ENV I would like to now unset them because there are also some building process can't be done through the proxy.我现在想取消设置它们,因为还有一些构建过程无法通过代理完成。

# 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 

It depends on what effect you are trying to achieve.这取决于您要达到的效果。

Note that, as a matter of pragmatics (ie how developers actually speak), "unsetting a variable" can mean two things: removing it from the environment, or setting the variable to an empty value.请注意,就语用学(即开发人员实际如何说话)而言,“取消设置变量”可能意味着两件事:将其从环境中删除,或将变量设置为空值。 Technically, these are two different operations.从技术上讲,这是两种不同的操作。 In practice though I have not run into a case where the software I'm trying to control differentiates between the variable being absent from the environment, and the variable being present in the environment but set to an empty value.在实践中,虽然我没有遇到过这样的情况:我试图控制的软件区分环境中不存在的变量和环境中存在但设置为空值的变量。 I generally can use either method to get the same result.我通常可以使用任何一种方法来获得相同的结果。

If you don't care whether the variable is in the layers produced by Docker, but leaving it with a non-empty value causes problems in later build steps.如果您不关心变量是否在 Docker 生成的层中,但将其保留为非空值会导致后面的构建步骤出现问题。

For this case, you can use ENV VAR_NAME= at the point in your Dockerfile from which you want to unset the variable.对于这种情况,您可以在 Dockerfile 中要从中取消设置变量的点使用ENV VAR_NAME= VAR_NAME ENV VAR_NAME= Syntactic note: Docker allows two syntaxes for ENV : this ENV VAR=1 is the same as ENV VAR 1 .语法说明:Docker 允许ENV两种语法:此ENV VAR=1ENV VAR 1相同。 You can separate the variable name from the value with a space or an equal sign.您可以使用空格或等号将变量名称与值分开。 When you want to "unset" a variable by setting it to an empty value you must use the equal sign syntax or you get an error at build time.当您想通过将变量设置为空值来“取消设置”变量时,您必须使用等号语法,否则在构建时会出现错误。

So for instance, you could do this:例如,你可以这样做:

ENV NOT_SENSITIVE some_value
RUN something

ENV NOT_SENSITIVE=
RUN something_else

When something runs, NOT_SENSITIVE is set to some_value .something运行时, NOT_SENSITIVE设置为some_value When something_else runs, NOT_SENSITIVE is set to the empty string.something_else运行时, NOT_SENSITIVE设置为空字符串。

It is important to note that doing unset NOT_SENSITIVE as a shell command will not affect anything else than what executes in this shell.请务必注意,将unset NOT_SENSITIVE作为 shell 命令执行不会影响unset NOT_SENSITIVE shell 中执行的内容之外的任何其他内容。 Here's an example:下面是一个例子:

ENV NOT_SENSITIVE some_value
RUN unset NOT_SENSITIVE && printenv NOT_SENSITIVE || echo "does not exist"

RUN printenv NOT_SENSITIVE

The first RUN will print does not exist because NOT_SENSITIVE is unset when printenv executes and because it is unset printenv returns a non-zero exit code which causes the echo to execute.第一次RUN将打印does not exist因为NOT_SENSITIVEprintenv执行时does not exist设置,并且因为它未设置printenv返回一个非零退出代码,导致执行echo The second RUN is not affected by the unset in the first RUN .第二次RUN不受第一次RUNunset影响。 It will print some_value to the screen.它会将some_value打印到屏幕上。

But what if I need to remove the variable from the environment, not just set it to an empty value?但是,如果我需要从环境中删除变量,而不仅仅是将其设置为空值呢?

In this case using ENV VAR_NAME= won't work.在这种情况下,使用ENV VAR_NAME=将不起作用。 I don't know of any way to tell Docker "from this point on, you must remove this variable from the environment, not just set it to an empty value".我不知道有什么方法可以告诉 Docker“从现在开始,你必须从环境中删除这个变量,而不仅仅是将它设置为空值”。

If you still want to use ENV to set your variable, then you'll have to start each RUN in which you want the variable to be unset with unset VAR_NAME , which will unset it for that specific RUN only.如果您仍然想使用ENV来设置您的变量,那么您必须在每次RUN中启动您希望使用unset VAR_NAME取消unset VAR_NAME变量的情况,这将仅针对特定RUN取消设置。

If you want to prevent the variable from being present in the layers produced by Docker.如果要防止变量出现在 Docker 生成的层中。

Suppose that variable contains a secret and the layer could fall into the hands of people who should not have the secret.假设该变量包含一个秘密,并且该层可能落入不应该拥有该秘密的人的手中。 In this case you CANNOT use ENV to set the variable.在这种情况下,您不能使用ENV来设置变量。 A variable set with ENV is baked into the layers to which it applies and cannot be removed from those layers.使用ENV设置的变量会烘焙到它所应用的图层中,并且无法从这些图层中删除。 In particular, (assuming the variable is named SENSITIVE ) running特别是,(假设变量名为SENSITIVE )正在运行

RUN unset SENSITIVE

does not do anything to remove it from the layer.不执行任何操作将其从图层中移除。 The unset command above only removes SENSITIVE from the shell process that RUN starts.上面的unset命令只从RUN启动的 shell 进程中删除SENSITIVE It affects only that shell.它只影响那个外壳。 It won't affect shells spawned by CMD , ENTRYPOINT , or any command provided through running docker run at the command line.它不会影响通过衍生弹CMDENTRYPOINT ,或通过运行提供的任何命令docker run在命令行。

In order to prevent the layers from containing the secret, I would use docker build --secret= and RUN --mount=type=secret... .为了防止层包含秘密,我将使用docker build --secret=RUN --mount=type=secret... For instance, assuming that I've stored my secret in a file named sensitive , I could have a RUN like this:例如,假设我已经将我的秘密存储在一个名为sensitive的文件中,我可以像这样RUN

RUN --mount=type=secret,id=sensitive,target=/root/sensitive \
 export SENSITIVE=$(cat /root/sensitive) \
 && [[... do stuff that requires SENSITIVE ]] \

Note that the command given to RUN does not need to end with unset SENSITIVE .请注意,给予RUN的命令不需要以unset SENSITIVE结束。 Due to the way processes and their environments are managed, setting SENSITIVE in the shell spawned by RUN does not have any effect beyond what that shell itself spawns.由于进程及其环境的管理方式,在RUN产生的外壳中设置SENSITIVE不会产生超出外壳本身产生的影响。 Environment changes in this shell won't affect future shells nor will it affect what Docker bakes into the layers it creates.此 shell 中的环境更改不会影响未来的 shell,也不会影响 Docker 烘焙到它创建的层中的内容。

Then the build can be run with:然后构建可以运行:

$ DOCKER_BUILDKIT=1 docker build --secret id=secret,src=path/to/sensitive [...]

The environment for the docker build command needs DOCKER_BUILDKIT=1 to use BuildKit because this method of passing secrets is only available if Docker uses BuildKit to build the images. DOCKER_BUILDKIT=1 docker build命令的环境需要DOCKER_BUILDKIT=1才能使用 BuildKit,因为这种传递秘密的方法只有在 Docker 使用 BuildKit 构建镜像时才可用。

If one needs env vars during the image build but they should not persist, just clear them.如果在映像构建期间需要 env vars 但它们不应该持续存在,只需清除它们。 In the following example, the running container shows empty env vars.在以下示例中,正在运行的容器显示空的环境变量。

Dockerfile文件

# 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=

build.sh编译文件

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.sh运行文件

docker run --rm -i \
    the-image \
    sh << COMMANDS
        env
COMMANDS

Output输出

no_proxy=
https_proxy=
http_proxy=
...

According to docker docs you need to use shell command instead:根据 docker docs,您需要改用 shell 命令:

FROM alpine
RUN export ADMIN_USER="mark" \
&& echo $ADMIN_USER > ./mark \
&& unset ADMIN_USER
CMD sh

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

Short-answer :简答

Try to avoid unnecessary environment variables, so you don't need to unset them.尽量避免不必要的环境变量,因此您无需取消设置它们。

In case you have to unset for a command you can do the following:如果您必须取消设置命令,您可以执行以下操作:

RUN unset http_proxy https_proxy no_proxy \
    && execute_your_command_here  

In case you have to unset for the built image you can do the following:如果您必须取消构建图像的设置,您可以执行以下操作:

FROM ubuntu_with_http_proxy

ENV http_proxy= \
    https_proxy= \
    no_proxy=

Once environment variables are set using the ENV instruction we can't really unset them as it is detailed:一旦使用ENV指令设置了环境变量,我们就不能真正取消它们,因为它是详细的:

Each ENV line creates a new intermediate layer, just like RUN commands.每个 ENV 行都会创建一个新的中间层,就像 RUN 命令一样。 This means that even if you unset the environment variable in a future layer, it still persists in this layer and its value can be dumped.这意味着即使您在以后的层中取消设置环境变量,它仍然会保留在该层中,并且可以转储其值。

See: Best practices for writing Dockerfiles请参阅: 编写 Dockerfile 的最佳实践

Details :详情

I prefer to define http_proxy as an argument during build like the following:我更喜欢在构建期间将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

On corporate proxy we need authentication anyways, so we need to configure local proxy server listening on 127.0.0.1:3128 witch is accessible over host.docker.internal:3128 from containers.在公司代理上,我们无论如何都需要身份验证,因此我们需要配置本地代理服务器侦听127.0.0.1:3128可以从容器通过host.docker.internal:3128访问。 This way it also works on docker desktop if we connect to corporate network over VPN (with local/home network blocked).这样,如果我们通过 VPN 连接到公司网络(本地/家庭网络被阻止),它也适用于 docker 桌面。

Setting no_proxy is also important to avoid flooding the proxy server.设置no_proxy对于避免代理服务器泛滥也很重要。

See the following article for more details on no_proxy related topics:有关 no_proxy 相关主题的更多详细信息,请参阅以下文章:

Sometimes it is also good to read the related documentation:有时阅读相关文档也不错:

In case we need to configure those environment variables we can use the following command:如果我们需要配置这些环境变量,我们可以使用以下命令:

  • during build ( link ):在构建期间( 链接):
docker build ... --build-arg http_proxy='http://alternative.proxy:3128/' ...
  • during runs ( link ):在运行期间( 链接):
docker run ... -env http_proxy='http://alternative.proxy:3128/' ...

You can add below lines in the Dockerfile您可以在 Dockerfile 中添加以下几行

ENV http_proxy ""
ENV https_proxy ""

I found the secret approach didn't work because I needed the env variable to persist in the container when I ran it in interactive mode but then needed to completely remove the variable for a later stage build for production.我发现秘密方法不起作用,因为当我以交互模式运行它时,我需要 env 变量在容器中持久化,但随后需要完全删除该变量,以便在后期构建生产。

What worked was in building for the development phase I appended the environment variable to the /root/.basrc file as在开发阶段的构建中起作用的是我将环境变量附加到 /root/.basrc 文件中

RUN echo export AWS_PROFILE=role-name >> /root/.bashrc
``

In the production stage of the build I then removed the last line of /root/.bashrc:

RUN sed -i '$ d' /root/.bashrc运行 sed -i '$ d' /root/.bashrc

AFAIK, there is no statement in Dockerfile which natively supports this feature. AFAIK,Dockerfile中没有声明本身支持此功能。 However, you might be able to unset it using the normal shell command - 但是,您可以使用普通的shell命令取消设置 -

...   
ENV http_proxy=http://...
RUN unset http_proxy
...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM