我如何在docker-compose中使用环境变量?

431

我希望能在docker-compose.yml中使用环境变量,并在docker-compose up时传递值。这是示例。

今天我正在使用基本的docker run命令来实现这一点,该命令包装了我的自己的脚本。是否有一种方法可以使用compose实现它,而不需要任何bash包装?

proxy:
  hostname: $hostname
  volumes:
    - /mnt/data/logs/$hostname:/logs
    - /mnt/data/$hostname:/data

4
有关不同选项,请参见:https://docs.docker.com/compose/environment-variables/ - Masood Khaari
4
在最新的Compose版本中已经解决了这个问题,你的示例将按原样工作。请查看https://docs.docker.com/compose/compose-file/#variable-substitution 以获取有关变量替换的信息。 - natbusa
1
不要忘记 Docker-App(自2018年6月起):https://dev59.com/rZbfa4cB1Zd3GeqPu4Z6#51007138 - VonC
20个回答

399

看起来现在docker-compose已经原生支持文件中的默认环境变量了。

你只需要在名为.env的文件中声明变量,它们将在docker-compose.yml中可用。

例如,对于一个包含以下内容的.env文件:

MY_SECRET_KEY=SOME_SECRET
IMAGE_NAME=docker_image

您可以在docker-compose.yml中访问您的变量,或将它们转发到容器内:

my-service:
  image: ${IMAGE_NAME}
  environment:
    MY_SECRET_KEY: ${MY_SECRET_KEY}

23
这是最好的解决方案! - Ladenkov Vladislav
16
这对我也起作用了。我不知道为什么,但文件名必须是字面上的.env,例如config.env对我无效。 - HBat
3
最佳解决方案。我们可以添加/etc/environment属性,并将其用作使用.env文件的环境变量。 这样会更加安全。 - Chinthaka Dinadasa
18
@HBat,你可以使用env_file来实现,例如:env_file: config.env - Islam
8
.env 文件是为了将变量解析到 docker-compose.yml 解释器中,而不是针对容器。如果要设置容器中的变量,您需要在 docker-compose.yml 中指定一个包含变量的 .env 文件,使用 env_file: .env.vars 或者您包含变量的文件名。使用 env_file: .env 虽然可以工作,但这是一种令人困惑的反模式,因为它基本上同时将变量用于解释器和容器。 - Peter Kionga-Kamau
显示剩余4条评论

377

Docker 解决方案:

Docker-compose 1.5+ 已启用变量替换:Releases · docker/compose

最新的 Docker Compose 允许您从 compose 文件中访问环境变量。因此,您可以获取环境变量,然后像这样运行 Compose:

set -a
source .my-env
docker-compose up -d

例如,假设我们有以下.my-env文件:
POSTGRES_VERSION=14

如果需要,您可以在调用docker-compose时通过命令行参数传递它们,例如:POSTGRES_VERSION=14 docker-compose up -d

然后,您可以使用${VARIABLE}语法在docker-compose.yml中引用这些变量,如下所示:

db:
  image: "postgres:${POSTGRES_VERSION}"

以下是来自文档的更多信息,摘自Compose file specification

当您使用此配置运行docker-compose up时,Compose会在shell中查找POSTGRES_VERSION环境变量并替换其值。对于此示例,Compose在运行配置之前将映像解析为postgres:9.3。

如果未设置环境变量,则Compose将以空字符串进行替换。在上面的示例中,如果未设置POSTGRES_VERSION,则image选项的值为postgres:。

支持$VARIABLE和${VARIABLE}语法。不支持扩展的shell风格特性,例如${VARIABLE-default}和${VARIABLE/foo/bar}。

如果您需要在配置值中放置字面美元符号,请使用双美元符号($$)。

该功能是通过此拉取请求添加的。

基于Docker的替代方案:通过docker-compose命令隐式源化环境变量文件

如果你想避免任何Bash包装器,或者不必像上面演示的那样显式地源化环境变量文件,那么你可以传递一个--env-file标志给docker-compose命令,并指定你的环境变量文件的位置:使用环境文件 然后你就可以在docker-compose命令中引用它,而不必显式地源化它:
docker-compose --env-file .my-env  up -d

如果您不传递--env-file标志,则默认的环境变量文件将是.env
请注意,使用此方法有以下警告:
在运行时存在的环境变量始终会覆盖在.env文件中定义的变量。同样,通过命令行参数传递的值也具有优先权。
因此,请注意可能会覆盖--env-file中定义的任何环境变量!
Bash解决方案:
我注意到Docker自动处理环境变量可能会引起混淆。让我们回归基础知识,比如Bash!这里提供了一种使用Bash脚本和.env文件的方法,具有一些额外的灵活性,以展示环境变量的实用性:
POSTGRES_VERSION=14
# Note that the variable below is commented out and will not be used:
# POSTGRES_VERSION=15

# You can even define the compose file in an environment variable like so:
COMPOSE_CONFIG=my-compose-file.yml
# You can define other compose files, and just comment them out
# when not needed:
# COMPOSE_CONFIG=another-compose-file.yml

然后在同一目录下运行此Bash脚本,应该可以正确部署所有内容:

#!/bin/bash

docker rm -f `docker ps -aq -f name=myproject_*`
set -a
source .env
cat ${COMPOSE_CONFIG} | envsubst | docker-compose -f - -p "myproject" up -d

只需在compose文件中使用常规的Bash语法(例如${POSTGRES_VERSION}将从.env文件中插入POSTGRES_VERSION),即可引用环境变量。

虽然此解决方案涉及Bash,但由于它具有更好的关注点分离,因此有些人可能更喜欢它。

请注意,COMPOSE_CONFIG在我的.env文件中定义并在我的Bash脚本中使用,但您可以轻松地将{$COMPOSE_CONFIG}替换为Bash脚本中的my-compose-file.yml

还要注意,我通过给所有容器添加“myproject”前缀来标记此部署。您可以使用任何名称,但这有助于识别您的容器,以便稍后轻松引用它们。假设您的容器是无状态的,正如它们应该是的,此脚本将根据您的.env文件参数和compose YAML文件快速删除和重新部署您的容器。

由于这个答案似乎很受欢迎,我写了一篇博客文章更详细地描述了我的Docker部署工作流程:让我们开始部署!(第一部分) 当您在部署配置中添加更多复杂性,例如Nginx配置、Let's Encrypt证书和链接容器时,这可能会有所帮助。


2
你可以直接使用 grep foo file.text 而不是 cat file.text | grep foo。在我的情况下,我必须执行 export $(grep "^[^#]" .config | xargs) && cat docker-compose.yml | envsubst - Jorge Lavín
1
我注意到有人在Docker的环境变量支持方面遇到了问题 - 你有任何详细信息或者一个工单链接吗? - tleyden
13
提示:这仅适用于 docker-compose up,而不适用于 docker-compose run - halbgut
7
有一个解决方案 https://docs.docker.com/compose/compose-file/#envfile,我使用其中的 env_file.env 文件中添加环境变量。然后你可以在 docker-compose.yml 中使用 ${VARIABLE} 引用这些变量。 - musale
2
这个答案已经过时了。你不再需要手动SOURCE环境脚本,docker-compose会为你完成。https://docs.docker.com/compose/environment-variables/#substitute-environment-variables-in-compose-files - ndemarco
显示剩余6条评论

122
  1. 创建一个名为template.yml的文件,它是带有环境变量的docker-compose.yml文件。
  2. 假设你的环境变量在一个名为'env.sh'的文件中。
  3. 将下面的代码放入一个sh文件中并运行它。
source env.sh
rm -f docker-compose.yml
envsubst < "template.yml" > "docker-compose.yml"

将生成一个新的文件docker-compose.yml,其中包含正确的环境变量值。

示例template.yml文件:

oracledb:
        image: ${ORACLE_DB_IMAGE}
        privileged: true
        cpuset: "0"
        ports:
                - "${ORACLE_DB_PORT}:${ORACLE_DB_PORT}"
        command: /bin/sh -c "chmod 777 /tmp/start; /tmp/start"
        container_name: ${ORACLE_DB_CONTAINER_NAME}

示例env.sh文件:

#!/bin/bash 
export ORACLE_DB_IMAGE=<image-name> 
export ORACLE_DB_PORT=<port to be exposed> 
export ORACLE_DB_CONTAINER_NAME=ORACLE_DB_SERVER

13
目前还没有更好的解决方案吗? - lvthillo
26
为什么要递归地删除一个文件?(rm -rf docker-compose.yml) - moritzschaefer
22
这个解决方案只会让事情变得更加复杂,应该根据docker-compose的新功能进行更新。https://docs.docker.com/compose/environment-variables/#substitute-environment-variables-in-compose-files - Efrat Levitan
3
我建议在圆括号中使用上述命令,以便即使变量也不会被全局设置。例如:(source env.sh; rm -rf docker-compose.yml; envsubst < "template.yml" > "docker-compose.yml";) - Ishtiyaq Husain
5
这个回答已经过时。 - ndemarco

74

最佳方式是在docker-compose.yml文件之外指定环境变量。您可以使用env_file设置,并在同一行内定义您的环境文件。然后再次运行docker-compose up应该使用新的环境变量重新创建容器。

这是我的docker-compose.yml文件的样子:

services:
  web:
    env_file: variables.env
注意:docker-compose 期望环境文件中的每一行采用 VAR=VAL 格式。避免在 .env 文件内使用 export。此外,.env 文件应放置在执行 docker-compose 命令的文件夹中。

8
确实最好的方式。 - Dany
9
不会自动将环境变量提供给Docker容器,您仍需要在环境部分明确指定这些变量。 - kta
它在我的情况下运行得很顺利,可能与Docker Compose文件版本有关。我使用的是版本3。 - Zisis F
对我来说很有效。Docker compose 2.7.0。 - Rob Sanders

62

别混淆.env文件和env_file选项!

它们有完全不同的用途!

.env文件仅将环境变量传递给您的Docker Compose文件,然后可以传递给容器。

env_file选项仅将这些变量传递给容器,而不会传递给Docker Compose文件。

关于密钥的重要说明

该示例使用环境变量将秘密凭据传递给Docker Compose文件。在官方文档中详细解释了更好的加密和安全方法:如何在Docker Compose中使用密钥

示例

好的,假设我们有这个简单的compose文件:
``` services: foo: image: ubuntu hostname: suchHostname # <-------------- 硬编码的'suchHostname' volumes: - /mnt/data/logs/muchLogs:/logs # <--- 硬编码的'muchLogs' - /mnt/data/soBig:/data # <----------- 硬编码的'soBig' ```
我们不想再硬编码这些了!所以,我们可以将它们放在当前终端的环境变量中,并检查`docker-compose`是否能够理解它们:
``` $ export the_hostname="suchHostName" $ export dir_logs="muchLogs" $ export dir_data="soBig" ```
然后将`docker-compose.yml`文件更改为:
``` services: foo: image: ubuntu hostname: $the_hostname # <-------------- 使用$the_hostname volumes: - /mnt/data/logs/$dir_logs:/logs # <--- 使用$dir_logs - /mnt/data/$dir_data:/data # <-------- 使用$dir_data ```
现在让我们执行`docker-compose config`命令并检查输出结果:
``` name: tmp services: foo: hostname: suchHostName # <------------- $the_hostname image: ubuntu networks: default: null volumes: - type: bind source: /mnt/data/logs/muchLogs # <--- $dir_logs target: /logs bind: create_host_path: true - type: bind source: /mnt/data/soBig # <---------- $dir_data target: /data bind: create_host_path: true networks: default: name: tmp_default ```
好的,它起作用了!但是让我们使用`.env`文件代替。由于Docker Compose理解`.env`文件,让我们创建一个并进行设置:
``` # .env文件(与'docker-compose.yml'在同一目录中) the_hostname="suchHostName" dir_logs="muchLogs" dir_data="soBig" ```
好的,您可以在一个新的终端中测试它(这样我们使用`export`设置的旧环境变量不会干扰,并确保一切在一个干净的终端中工作)。只需再次按照第4步操作,看看它是否起作用!
到目前为止一切顺利。然而,当你遇到env_file选项时,会感到困惑。假设你想将密码传递给Docker Compose文件(而不是容器)。
在错误的方法中,你可能会将密码放在.secrets文件中:
# .secrets
somepassword="0P3N$3$@M!"

然后按照以下方式更新Docker Compose文件:
services:
  foo:
    image: ubuntu
    hostname: $the_hostname
    volumes:
      - /mnt/data/logs/$dir_logs:/logs
      - /mnt/data/$dir_data:/data

    #  BAD:
    env_file:
      - .env
      - .secrets
    entrypoint: echo "Hush! This is a secret '$somepassword'"

现在再次像第4步一样检查它的结果会是:
WARN[0000] The "somepassword" variable is not set. Defaulting to a blank string.
name: tmp                       #       ^
services:                       #       |
  foo:                          #       |
    entrypoint:                 #       |
    - echo                      #       |
    - Hush! This is a secret '' # <---- ‍ Oh no!
    environment:
      dir_data: soBig
      dir_logs: muchLogs
      somepassword: 0P3N$$3$$@M! # <---  Huh?!
      the_hostname: suchHostName
    hostname: suchHostName
    image: ubuntu
    networks:
      default: null
    volumes:
    - type: bind
      source: /mnt/data/logs/muchLogs
      target: /logs
      bind:
        create_host_path: true
    - type: bind
      source: /mnt/data/soBig
      target: /data
      bind:
        create_host_path: true
networks:
  default:
    name: tmp_default

所以你可以看到,$somepassord变量只传递给了容器,而没有传递给Docker Compose文件。
总结

你可以通过两种方式将环境变量传递给Docker Compose文件:

  1. 在运行docker compose之前将变量导出到终端。
  2. 将变量放在.env文件中。

env_file选项只会将这些额外的变量传递给容器,而不是compose文件。

有用的链接


6
标题非常明确,对于每个使用Docker的开发人员来说都应该是基本知识。直到现在,我才意识到这一点。 - Jos
是的,我也曾经吃过亏。实际上,在阅读了这个页面的答案后,我进行了更多的研究,并添加了缺失的内容。 - Pedram
1
请使用 docker-compose config 而不是 docker-compose convert 来处理更新版本。 - Maicon Mauricio
好的,但是我想把所有的环境文件都保存在一个单独的专用目录中,我该如何告诉Compose从自定义位置读取变量?似乎它已经硬编码为只能从相同的目录读取变量。 - ruslaniv
1
嘿,你好。
  • 你可以使用 --env-file 来指定路径(更多信息请参考 https://docs.docker.com/compose/environment-variables/set-environment-variables/#substitute-with---env-file)。
  • 如果你想要保持不同的秘密变量分开,请使用这里解释的 secrets 方法:https://docs.docker.com/compose/use-secrets/
  • 对于不同的配置(这样你就不需要重新构建 Docker 镜像),请使用:https://docs.docker.com/compose/compose-file/08-configs/
- Pedram

47

以下适用于docker-compose 3.x 在容器内设置环境变量

方法一 直接法

web:
  environment:
    - DEBUG=1
      POSTGRES_PASSWORD: 'postgres'
      POSTGRES_USER: 'postgres'

第二种方法“.env”文件

在与docker-compose.yml相同的位置创建.env文件。

$ cat .env
TAG=v1.5
POSTGRES_PASSWORD: 'postgres'

你的Compose文件将会像这样

$ cat docker-compose.yml
version: '3'
services:
  web:
    image: "webapp:${TAG}"
    postgres_password: "${POSTGRES_PASSWORD}"

来源


4
我想看到方法1的完整示例。我无法使其工作,所以最终使用了.env文件(这很好用)。 - BobHy
2
“- DEBUG=1” 和 “DEBUG: 1” 变量赋值方法有什么区别? - Gagik

39

当使用环境变量作为卷时,您需要:

  1. 在包含 docker-compose.yaml 文件的同一文件夹中创建.env文件

  2. .env 文件中声明变量:

  3. HOSTNAME=your_hostname
    
  4. $hostname更改为${HOSTNAME},位于docker-compose.yaml文件中

  5. proxy:
      hostname: ${HOSTNAME}
      volumes:
        - /mnt/data/logs/${HOSTNAME}:/logs
        - /mnt/data/${HOSTNAME}:/data
    

当然,你可以在每次构建时动态地执行这个操作,例如:

echo "HOSTNAME=your_hostname" > .env && sudo docker-compose up

17
注意,根据文档:.env 文件功能仅在使用 docker-compose up 命令时有效,不适用于 docker stack deploy - James Gentes
1
这应该放在CONTEXT目录中,而不是docker-compose.yaml目录中。 - Joe
2
@Joe,这个评论可以说是救了我的命。我一直想不通为什么 .env 文件被忽略了。 - Yuri Pozniak

11
自1.25.4版本起,docker-compose支持选项--env-file,使您可以指定一个包含变量的文件。您的文件应该长这样:
hostname=my-host-name

并且这个命令:

docker-compose --env-file /path/to/my-env-file config

8
为了添加一个环境变量,您可以定义一个名为env_file(假设它的名称为var.env)的文件,内容如下:
ENV_A=A
ENV_B=B

将其添加到docker compose清单服务中。此外,您可以使用environment直接定义环境变量。

例如,在docker-compose.yaml文件中:

version: '3.8'
services:
  myservice:
    build:
      context: .
      dockerfile: ./docker/Dockerfile.myservice
    image: myself/myservice
    env_file:
     - ./var.env
    environment:
     - VAR_C=C
     - VAR_D=D
    volumes:
     - $HOME/myfolder:/myfolder
    ports:
     - "5000:5000"

请在此处查看更多/更新的信息:手册 → Docker → Compose → 环境变量 → 概述

7

使用:

env SOME_VAR="I am some var" OTHER_VAR="I am other var" docker stack deploy -c docker-compose.yml

请使用3.6版本:

version: "3.6"
services:
  one:
    image: "nginx:alpine"
    environment:
      foo: "bar"
      SOME_VAR:
      baz: "${OTHER_VAR}"
    labels:
      some-label: "$SOME_VAR"
  two:
    image: "nginx:alpine"
    environment:
      hello: "world"
      world: "${SOME_VAR}"
    labels:
      some-label: "$OTHER_VAR"

这个内容来源于 功能需求:通过cli选项传递环境变量部署Docker堆栈 #939.


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