Docker的ARG、ENV和.env配置完整指南


本文将帮助您自信地使用 Docker ARG、ENV、env_file 和 .env 文件。您将了解如何使用 Docker 构建时变量、环境变量和 docker-compose 模板轻松配置 Docker 映像和 dockerized 应用程序。
 
常见的误解

  • .env 文件仅在使用docker-compose.yml 文件时的预处理步骤中使用。美元符号变量(如 $HI)被替换为同一目录中名为“.env”的文件中包含的值。
  • ARG仅在构建 Docker 映像(RUN 等)期间可用,而不是在创建映像并从其启动容器之后(ENTRYPOINT、CMD)。您可以使用 ARG 值来设置 ENV 值来解决这个问题。
  • ENV值可用于容器,但在 Docker 构建期间也可使用 RUN 样式的命令,从引入它们的行开始。
  • 如果您使用 bash (RUN export VARI=5 && …) 在中间容器中设置环境变量,它将不会在下一个命令中持续存在。有办法解决这个问题。
  • env_file是一种将许多环境变量一次性传递给单个命令的便捷方式。这不应与.env文件混淆。
  • 设置 ARG 和 ENV 值会在 Docker 映像中留下痕迹。不要将它们用于不打算保留的秘密(好吧,您可以使用多阶段构建)。

 
该指南分为以下主题:

The Dot-Env File (.env)
如果您的项目中有一个名为.env的文件,它仅用于将值放入同一文件夹中的 docker-compose.yml 文件中。这些与 Docker Compose 和 Docker Stack 一起使用。它与 ENV、ARG 或上面解释的任何 Docker 特定无关。这完全是 docker-compose.yml 的事情。
文件中的值.env用以下表示法编写:

VARIABLE_NAME=some value
OTHER_VARIABLE_NAME=some other value, like 5

这些键值对用于替换 docker-compose.yml 文件中的美元符号变量。这是一种预处理步骤,并使用生成的临时文件。这是避免硬编码值的好方法。
这是一个示例 docker-compose.yml 文件,依赖于 .env 文件提供的值:

version: '3'

services:
  plex:
    image: linuxserver/plex
      environment:
        - env_var_name=${VARIABLE_NAME} # here it is

提示:使用 .env 文件时,您可以很容易地调试 docker-compose.yml 文件。只需输入docker-compose config。通过这种方式,您将看到 docker-compose.yml 文件内容在执行替换步骤后的样子,而无需运行任何其他内容。
  
ARG 和 ENV 可用性
在使用Docker时,我们区分了两种不同类型的变量--ARG和ENV。

ARG也被称为构建时变量。它们只在Docker文件中用ARG指令 "宣布 "的那一刻起可用,直到镜像被构建。运行中的容器不能访问ARG变量的值。这也适用于CMD和ENTRYPOINT指令,它们只是告诉容器在默认情况下应该运行什么。如果你告诉Dockerfile期待各种ARG变量(没有默认值),但在运行构建指令时却没有提供任何ARG变量,就会出现错误信息。
然而,ARG值可以在镜像建立后,通过查看镜像的docker历史,很容易被检查出来。因此,他们是敏感数据的一个糟糕选择。
 
ENV变量在构建过程中也是可用的,只要你用ENV指令引入它们。然而,与ARG不同的是,它们也可以被从最终镜像中启动的容器所访问。在启动一个容器时,ENV值可以被覆盖,下面会有更多的介绍。
 
下面是围绕从Docker文件构建Docker镜像和运行容器的过程中,对ARG和ENV可用性的简化概述。它们是重叠的,但ARG不能从容器内部使用。

 
设置ARG值
所以,你有你的Docker文件,其中定义了ARG和ENV值。如何设置它们,以及在哪里设置?你可以在Docker文件中留空,或者设置默认值。如果你没有为预期的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运行该命令,将导致以下一行被打印出来(除其他外)。

Oh dang look at that a_value

那么,这如何转化为使用docker-compose.yml文件?当使用docker-compose时,你可以在args块中指定传递给ARG的值。

version: '3'

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

当你试图设置一个在Docker文件中没有提到的ARG变量时,Docker会抱怨。
 
设置ENV值
那么,如何设置ENV值呢?你可以在启动容器时设置(我们将在下面讨论这个问题),但你也可以通过硬编码在Docker文件中直接提供默认ENV值。此外,你还可以为环境变量设置动态的默认值!

在构建镜像时,你能提供的唯一东西是ARG值,如上所述。你不能直接提供ENV变量的值。然而,ARG和ENV都可以一起工作。你可以使用ARG来设置ENV变量的默认值。下面是一个基本的Docker文件,使用硬编码的默认值。

# 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

这里是一个Docker文件的片段,使用动态构建时的环境值。
# 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文件。所有这些都将覆盖Docker文件中的任何默认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
 

  • [b]2. 从你的主机传递环境变量值[/b]

这和上面的方法是一样的。唯一的区别是,你不提供一个值,而只是命名这个变量。这将使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可用性的概述以及从命令行设置它们的常用方法。


 

 
在我们继续之前:如果你是Docker的新手,并且不习惯考虑镜像和容器,就会经常遇到一个问题:如果你试图在RUN语句中设置一个环境变量的值,比如RUN export VARI=5 && ...,你将无法在接下来的RUN语句中访问它。其原因是,对于每个RUN语句,一个新的容器会从一个中间映像中启动。命令结束后会保存一个镜像,但环境变量不会以这种方式持续存在。

如果你对一个映像感到好奇,想知道它是否在容器启动前提供了默认的ENV变量值,你可以检查Image映像,看看哪些ENV条目是默认设置的。

# first, get the images on your system and their ids
$ docker images
# use one of those ids to take a closer look
$ docker inspect image-id

# look out for the "Env" entries

 
覆盖ENV值
假设你有一个由Dockerfile构建的镜像,它提供了默认的ENV值。从它启动的容器可以访问Dockerfile中定义的ENV变量。然而,这些值可以通过提供单一的环境变量或env_files来覆盖,环境变量被解析并传入容器。

一旦一个进程在容器内运行,或者当一个命令被评估时,他们可以为自己改变环境值。像这样的东西。
$docker run myimage SOME_VAR=hi python app.py
将完全覆盖你可能为app.py脚本设置的任何SOME_VAR,即使在最后命令之前有一些带有-e标志的值。
优先顺序是,从强到弱:容器化应用程序集的东西,来自单一环境条目的值,来自env_file(s)的值,最后是Dockerfile默认值。