在“docker build”过程中如何设置环境变量?

237
我试图在构建过程中在Docker容器中设置环境变量,但是没有成功。在使用run命令时设置它们是有效的,但是我需要在构建过程中设置它们。
Dockerfile
FROM ubuntu:latest
ARG TEST_ENV=something

我正在使用的命令来构建

docker build -t --build-arg TEST_ENV="test" myimage .

跑步

docker run -dit myimage

我正在使用这个方法来检查可用的环境变量。
docker exec containerid printenv

结果是

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=e49c1abfd58b
TERM=xterm
no_proxy=*.local, 169.254/16
HOME=/root

TEST_ENV不存在

2个回答

265

ARG用于设置环境变量,这些变量在docker build过程中使用,但不会出现在最终的镜像中,这就是为什么在使用docker run时看不到它们的原因。

您可以使用ARG来进行设置,这些设置仅在构建镜像时才相关,并且不需要容器从镜像中运行时使用。您可以使用ENV来设置在构建和容器中使用的环境变量。

使用以下Dockerfile:

FROM ubuntu
ARG BUILD_TIME=abc
ENV RUN_TIME=123
RUN touch /env.txt
RUN printenv > /env.txt

您可以像使用 docker build -t temp --build-arg BUILD_TIME=def . 一样覆盖构建参数。这样您就可以得到您期望的结果:

> docker run temp cat /env.txt                                                                                         
HOSTNAME=b18b9cafe0e0                                                                                                  
RUN_TIME=123                                                                                                           
HOME=/root                                                                                                             
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin                                                      
BUILD_TIME=def                                                                                                         
PWD=/ 

使用 ENV 添加了环境变量,但是使用 docker build -t temp --build-arg BUILD_TIME=def 命令会打印出 "One or more build-args [BUILD_TIME] were not consumed, failing build." 的错误信息。添加单独的 ARG BUILD_TIME_ARG=defaultENV BUILD_TIME=$BUILD_TIME_ARG 可以解决这个问题。你能把它加入到你的回答中吗?感谢您的帮助。 - Michał Pietraszko
27
我意识到你可以用与ENV相同的名称创建ARGARG BUILD_TIME=default ENV BUILD_TIME=$BUILD_TIME - Michał Pietraszko
16
具有讽刺意味的是,埃尔顿的回答并不正确。迈克尔最后的评论正中要害,并显示了如何在构建时实际设置环境变量(在撰写本文时)。 - Ken J

228

我将从这篇优秀文章中总结要点。


ARG和ENV的区别

1 ) 简短解释:
ARG 只在构建 Docker 镜像的期间(RUN等命令)可用,而在创建镜像后和从其中启动容器(ENTRYPOINT、CMD)时不可用。

ENV 值可用于容器,也可用于运行 Docker 构建时带有该值的 RUN-style 命令(从引入该值的行开始)。如果您使用 bash 在中间容器中设置环境变量(RUN export VARI=5 && …),则该变量将无法保留到下一个命令中。

2 ) 详细解释:
ARG 也称为构建时变量。只能在 Dockerfile 中使用 ARG 指令宣告时到构建镜像时之间使用。正在运行的容器无法访问 ARG 变量的值。CMD 和 ENTRYPOINT 指令告诉容器其默认运行内容。如果您通知 Dockerfile 利用各种 ARG 变量(没有默认值),但在运行构建命令时未提供任何值,则会出现错误消息。

但是,ARG 值可以通过查看镜像的 docker history 轻松检查。因此,它们不适用于敏感数据。

ENV 变量同样在构建期间可用,在使用 ENV 指令引入后即可使用。然而,与 ARG 不同,容器也可以从最终镜像中获取其值。下面更详细地解释了如何在启动容器时覆盖 ENV 值。

3 ) 图形解释:
以下是 Dockerfile 构建 Docker 镜像并运行容器过程中 ARG 和 ENV 可用性的简化概述。

它们有重叠,但是 ARG 不能从容器内部使用。

enter image description here

设置 ARG 和 ENV 值

设置 ARG 值

所以,您有一个定义了 ARGENV 值的 Dockerfile。如何设置它们?您可以在 Dockerfile 中将它们留空或设置默认值。如果没有为预期的 ARG 变量提供值且没有默认值,则会出现错误消息。

以下是一个 Dockerfile 示例,包括默认值和不包括默认值:

ARG some_variable_name
# or with a hard-coded default:
#ARG some_variable_name=default_value

RUN echo "Oh dang look at that $some_variable_name"
# you could also use braces - ${some_variable_name}

在命令行中构建Docker镜像时,可以使用“--build-arg”设置“ARG”值。
$ docker build --build-arg some_variable_name=a_value

运行上述Dockerfile命令,将导致打印以下行(其中之一):哦,看那个a_value

那么,如何将其转换为使用docker-compose.yml文件呢?
使用docker-compose时,可以在args块中指定要传递的ARG值:
(docker-compose.yml文件)

version: '3'

services:
  somename:
    build:
      context: ./app
      dockerfile: Dockerfile
      args:
        some_variable_name: a_value

当你尝试设置一个在 Dockerfile 中没有提到的变量时,Docker 会报错提示。

设置 ENV 变量

那么,如何设置 ENV 变量呢?你可以在启动容器时进行设置(我们下面会详细说明),但是你也可以通过硬编码直接在 Dockerfile 中提供默认的 ENV 值。此外,你还可以为环境变量设置动态默认值!

在构建镜像时,你只能提供像前面所述的 ARG 值。你不能直接为 ENV 变量提供值。然而,ARGENV 可以一起使用。你可以使用 ARG 来设置 ENV 变量的默认值。
下面是一个基本的 Dockerfile,使用硬编码的默认值:

# no default value
ENV hey
# a default value
ENV foo /bar
# or ENV foo=/bar

# ENV values can be used during the build
ADD . $foo
# or ADD . ${foo}
# translates to: ADD . /bar

以下是一个使用动态构建环境值的Dockerfile片段:

# expect a build-time variable
ARG A_VARIABLE
# use the value to set the ENV var default
ENV an_env_var=$A_VARIABLE
# if not overridden, that value of an_env_var will be available to your containers!

一旦镜像被构建完成,你可以通过三种不同的方式来启动容器并提供ENV变量的值,可以通过命令行或使用docker-compose.yml文件进行配置。所有这些都会覆盖Dockerfile中的默认ENV值。
ARG不同的是,你可以向容器传递各种环境变量。甚至是Dockerfile中没有显式定义的变量。但是是否会对应用程序产生影响则取决于你的应用程序。

选项1:逐个提供值
从命令行使用 -e 标志:

$ docker run -e "env_var_name=another_value" alpine env

docker-compose.yml 文件中:

version: '3'

services:
  plex:
    image: linuxserver/plex
      environment:
        - env_var_name=another_value

选项 2:从主机传递环境变量值
这与上述方法相同。
唯一的区别是,您不提供值,而只是命名变量。这将使 Docker 访问主机环境中的当前值并将其传递给容器。

$ docker run -e env_var_name alpine env

对于 docker-compose.yml 文件,省略等号和等号之后的部分即可获得相同的效果。
version: '3'

services:
  plex:
    image: linuxserver/plex
      environment:
        - env_var_name

选项3:从文件中获取值(env_file)
而不是将变量写出或硬编码(根据12因素宣言并不好的习惯),我们可以指定一个文件来读取值。这样的文件内容看起来像这样:

env_var_name=another_value

上面的文件名为env_file_name(名称任意),位于当前目录中。
您可以引用文件名,解析其中包含的环境变量来设置:
$ docker run --env-file=env_file_name alpine env

使用 docker-compose.yml 文件时,我们只需引用一个 env_file,Docker 将解析其中的变量并设置它们。
version: '3'

services:
  plex:
    image: linuxserver/plex
    env_file: env_file_name

设置ARG和ENV的常见命令行方式

以下是一张小抄表,结合ARGENV的概述与常用的命令行设置方式。

enter image description here


如果我没有设置环境变量,容器中的默认环境变量是什么?它会遵循主机环境吗? - Hoo
3
如果您在Dockerfile中没有配置任何环境变量,它将只包含基础镜像带有的默认变量(可能更多是系统级别的),而不是主机的变量。 - RtmY
1
这个答案仅凭备忘单就值得了。谢谢 :) - JDS
将 .env 文件放在容器内部还是将 ENV 值加载到容器的环境中更好? - Zaffer
1
@Zaffer,两种选择都是有效的,这真的取决于情况或者在代码中更容易使用它(读取环境变量或从文件中读取可能会更慢)。 - RtmY
1
正如你所说,由于参数可以在事后检查,它们不是保密信息的好选择。那么什么才是保密信息的好选择呢?有没有其他方法?如果没有,是否有一种方法可以从最终镜像中清除构建参数? - Freedom_Ben

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接