在“docker build”过程中如何将环境变量的值传递到Dockerfile中?

376
我正在为一个Ruby应用构建一个容器。我的应用配置包含在环境变量中(在应用内部加载使用dotenv)。
其中一个配置变量是应用的公共IP,用于内部生成链接。 我需要在容器内部添加一个dnsmasq条目,将此IP指向127.0.0.1,以便它可以像未容器化一样获取应用的链接。
因此,我尝试在我的Dockerfile中设置一个ENV,以传递一个环境变量给容器。
我尝试了几种方法。
ENV REQUEST_DOMAIN $REQUEST_DOMAIN
ENV REQUEST_DOMAIN `REQUEST_DOMAIN`

一切都传递了"REQUEST_DOMAIN"字符串,而不是环境变量的值。 有没有办法将主机机器上的环境变量值传递给容器?
10个回答

576

您应该在Dockerfile中使用ARG指令,这个指令就是为此目的而设计的。

ARG指令定义一个变量,用户可以在构建时使用--build-arg <varname>=<value>标志通过docker build命令传递给构建器。

因此,您的将包含以下行:

ARG request_domain

或者如果您更喜欢默认值:

ARG request_domain=127.0.0.1

现在,您可以在Dockerfile中引用此变量:

ENV request_domain=$request_domain

那么你可以这样构建你的容器:

$ docker build --build-arg request_domain=mydomain Dockerfile


注意1:如果您在Dockerfile中引用了一个ARG,但在--build-arg中排除了它,则您的镜像将不会构建。

注意2: 如果用户指定了Dockerfile中未定义的构建参数,则构建会输出警告:

[警告] 一个或多个构建参数[foo]未被使用。


16
ARG自docker v1.9版本开始提供。 - Synesso
7
如果您在Dockerfile中引用了ARG但在--build-arg中将其排除,则无法构建图像的说法是错误的。事实上,即使没有--build-arg,您也可以使用引用构建镜像。此外,您还可以为构建参数设置默认值。 - ALex_hha
7
ENV 行的目的是什么?对我来说,--build-arg + ARG 已经足够了。 - Phil
17
ARG指定一个变量在Dockerfile后续的命令中使用。ENV定义一个环境变量,该变量会传递给容器。 - herm
2
请注意,无论是在Windows还是Linux上,Dockerfile本身都将使用$var语法来期望变量... 在Windows上使用RUN命令时,cmd仍将期望%var%语法,而powershell仍将期望$env:var语法。 - cowlinator
显示剩余5条评论

69
你可以这样操作: cat Dockerfile | envsubst | docker build -t my-target - 然后,使用类似下面的Dockerfile:
ENV MY_ENV_VAR $MY_ENV_VAR

我猜这可能会有一些特殊字符的问题,但至少对于大多数情况来说,这是有效的。


14
如果需要从包含Dockerfile的目录中添加文件,似乎这并不起作用。 - Tom Hennen
3
非常好的解决方案!在Mac上,您可以通过“brew install gettext”安装获得envsubst。但由于可能与BSD构建系统存在冲突,它是“仅限 keg”的,并且不会创建符号链接。但是,可以安全地执行“ln -s /usr/local/Cellar/gettext/*/bin/envsubst /usr/local/bin/”将该命令添加到您的PATH中。(实际上,库是需要考虑的问题。)或者,您可以在其/usr/local/Cellar/gettext/*/bin/envsubst位置使用它。 - Bruno Bronosky
2
澄清@TomHennen的评论,无论环境变量替换如何,将Dockerfile导入docker build -是不起作用的,特别是当您从Dockerfile引用相对路径时。 - superEb
5
针对@TomHennen的评论,如果您确实想在Dockerfile中使用上下文相关的命令(如COPY),您可以将envsubst的输出重定向到一个临时文件,然后将其输入到“docker build”中。示例:cat Dockerfile | envsubst > DockerfileWithEnvVars,然后docker build -t my-target -f DockerfileWithEnvVars .,最后 rm DockerfileWithEnvVars - snark
ADD命令无法工作的情况下,在-之前添加-f似乎可以解决问题:cat Dockerfile | envsubst | docker build -t my-target . -f - - Daniel
显示剩余3条评论

59
这是给那些想要通过使用`.env`文件从docker-compose传递环境变量到dockerfile,在构建期间将这些参数作为环境变量传递给容器的人准备的。 典型的docker-compose文件。
services:
  web:
    build:
      context: ./api
      dockerfile: Dockerfile
      args:
        - SECRET_KEY=$SECRET_KEY
        - DATABASE_URL=$DATABASE_URL
        - AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
.env文件中存在的环境变量传递给build命令中的args参数。 典型的.env文件。
SECRET_KEY=blahblah
DATABASE_URL=dburl

现在,当您运行docker-compose up -d命令时,docker-compose文件会从.env文件中获取值,然后将其传递给docker-compose文件。现在,web的Dockerfile在构建过程中通过args包含了所有这些变量。现在,Web的典型Dockerfile如下:

FROM python:3.6-alpine

ARG SECRET_KEY
ARG DATABASE_URL
ARG AWS_ACCESS_KEY_ID
ARG AWS_SECRET_ACCESS_KEY
ARG AWS_BUCKET
ARG AWS_REGION
ARG CLOUDFRONT_DOMAIN

ENV CELERY_BROKER_URL redis://redis:6379/0
ENV CELERY_RESULT_BACKEND redis://redis:6379/0
ENV C_FORCE_ROOT true
ENV SECRET_KEY  ${SECRET_KEY?secretkeynotset}
ENV DATABASE_URL ${DATABASE_URL?envdberror}

现在我们在dockerfile中将secret_key和db url作为参数接收到了。现在让我们在ENV中使用它们,如ENV SECRET_KEY ${SECRET_KEY?secretkeynotset}。现在即使docker容器也在环境变量中具有这些变量。 记住不要使用ARG $SECRET_KEY(我之前用了)。应该是ARG SECRET_KEY


它不起作用。Compose文件'./docker-compose.yaml'无效,因为:服务.web的配置选项'args'不受支持。 - Maxim Colesnic
4
@MaximColesnic,你的参数应该在 build 部分下面,而不是在 web 下面,这就是你的错误所说的。因此,参数应该在 services.web.build 中。请注意缩进。 - Zincfan
1
非常感谢您提供这个难以找到的信息。正是我所需要的。 - benibilme
2
感谢您提供这个定义明确的答案。 - Shan-Desai
请帮我解决这个问题 https://dev59.com/oXQOtIcB2Jgan1znkIBY?noredirect=1#comment127596994_72216766 - Cyril I
显示剩余2条评论

24

一种不会失去使用COPYADD命令,也不需要使用中间文件的替代方法是使用Bash的进程替换

docker build -f <(envsubst < Dockerfile) -t my-target .

4
不幸的是,这似乎在Docker 17.09中无法工作,我收到了错误消息“无法准备上下文:Dockerfile(/dev/fd/63)必须在构建上下文内”。 - Alexander Klimetschek
请帮我解决这个问题:如何在入口点数组中使用Docker环境变量?此问题出现在Kubernetes中。 - Cyril I

17

在运行时创建一个文件,并从该文件中加载环境变量。

export MYVAR="my_var_outside"
cat > build/env.sh <<EOF
MYVAR=${MYVAR}
EOF

...然后在Dockerfile中进行操作

ADD build /build
RUN /build/test.sh

test.sh从env.sh加载了MYVAR变量。

#!/bin/bash
. /build/env.sh
echo $MYVAR > /tmp/testfile

1
记得给 Shell 文件赋予权限,使用 chmod 775 env.sh 命令。 - tblev
请帮我解决这个问题:如何在entrypoint数组中使用Docker环境变量,这是我在Kubernetes上遇到的问题? - Cyril I

6
如果您只想在Dockerfile中查找并替换所有环境变量($ ExampleEnvVar),然后构建它,这将起作用:
envsubst < /path/to/Dockerfile | docker build -t myDockerImage . -f -

根据最佳答案,可以执行以下操作:envsubst < ./Dockerfile | docker build -squash -t ${DOCKIMG}:${VERSION} . -f -其中FROM行使用环境变量。 - mikequentel

2
使用build-arg时,需要注意在FROM之后变量不再可用。例如:docker build --build-arg CODE_VERSION=1.2 Dockerfile
ARG  CODE_VERSION=latest
FROM base:${CODE_VERSION}

FROM之前声明的ARG位于构建阶段之外,因此不能在FROM之后的任何指令中使用。

通常情况下,如果在FROM期间不需要,请将ARG放置在FROM之后:

FROM base:xy
ARG  ABC=123

在构建阶段中,使用未设置值的 ARG 指令,并将其放置在第一个 FROM 之前声明的 ARG 的默认值处。请参考以下示例:
ARG VERSION=latest
FROM busybox:$VERSION
ARG VERSION
RUN echo $VERSION > image_version

https://docs.docker.com/engine/reference/builder/#understand-how-arg-and-from-interact


1

如果我错了,请纠正我。 问题是关于环境变量,而不是参数。问题是如何将环境变量发送到容器中 - 这意味着容器可以引用该变量。所以我认为答案应该如下:

  1. 不需要使用 ARG,因为它仅在构建镜像时使用
  2. 如果您需要将环境变量传递给容器,可以使用以下语法运行容器: docker run --name [container-name] -e "[variable-name]=[new-value]" [image-name]

1

在容器启动时,您可以直接传递环境变量:

docker run -e [your_variable_name = your_variable_value] [image to instanciate]

示例:

docker run --rm -e TARGET=192.168.1.9 ubuntu env

输出结果为:

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=36399469e519
TARGET=192.168.1.9
HOME=/root

目标变量已添加到当前容器的环境变量中!


-3

添加-e选项以将环境变量传递给容器。 例如:

$ MYSQLHOSTIP=$(sudo docker inspect -format="{{ .NetworkSettings.IPAddress }}" $MYSQL_CONRAINER_ID)
$ sudo docker run -e DBIP=$MYSQLHOSTIP -i -t myimage /bin/bash

root@87f235949a13:/# echo $DBIP
172.17.0.2

9
Damien希望塑造一个形象。-e选项应该与run命令一起使用。 - Andres Restrepo

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