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

我希望能够在docker-compose中使用env变量。docker-compose up时传入的值。这就是例子。

我今天用基本的docker运行命令来做这个,它围绕着我自己的脚本。 有没有一种方法来实现它与撰写,没有任何这样的bash包装?< / p >

proxy:
hostname: $hostname
volumes:
- /mnt/data/logs/$hostname:/logs
- /mnt/data/$hostname:/data
534460 次浏览

你不能……然而。但这是另一种选择,像码头作曲家一样思考。yml发电机:

https://gist.github.com/Vad1mo/9ab63f28239515d4dafd

基本上是一个替换变量的shell脚本。你也可以使用Grunt任务在CI进程结束时构建docker合成文件。

我有一个简单的bash脚本,我为此创建,这只是意味着在使用之前在你的文件上运行它: https://github.com/antonosmond/subber < / p >

基本上只要创建你的合成文件使用双花括号表示环境变量,例如:

app:
build: "\{\{APP_PATH}}"
ports:
- "\{\{APP_PORT_MAP}}"

双花括号中的任何内容都将被同名的环境变量替换,因此如果我设置了以下环境变量:

APP_PATH=~/my_app/build
APP_PORT_MAP=5000:5000

在运行subber docker-compose.yml时,生成的文件看起来像这样:

app:
build: "~/my_app/build"
ports:
- "5000:5000"

据我所知,这还在进行中。他们想这么做,但还没有发布。参见1377 (@Andy提到的“新”495)。

我最终实现了@Thomas提出的“生成。yml作为CI的一部分”方法。

DOCKER解决方案:

Docker-compose 1.5+启用了变量替换:https://github.com/docker/compose/releases

最新的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}"

这里是来自文档的更多信息,从这里取:https://docs.docker.com/compose/compose-file/#variable-substitution

当你用这个配置运行docker-compose up时,Compose看起来 为shell中的POSTGRES_VERSION环境变量 将其值代入。对于本例,Compose解析图像

. 0 .执行命令前必须执行postgres:9.3

如果没有设置环境变量,则使用 空字符串。在上面的例子中,如果POSTGRES_VERSION没有设置, image选项的值为postgres:.

同时支持$VARIABLE和${VARIABLE}语法。扩展 shell风格的特性,例如${VARIABLE-default}和

. ${VARIABLE/foo/bar

如果你需要在配置值中放入美元符号,请使用 双美元符号($$)

该特性被添加到在这个拉请求中

另一种基于docker的解决方案:通过docker-compose命令隐式地派生一个env vars文件

如果你想避免任何bash包装器,或者必须显式地来源一个env vars文件(如上所示),那么你可以将一个--env-file标志传递给docker-compose命令,其中包含env var文件的位置:https://docs.docker.com/compose/env-file/

然后你可以在你的docker-compose命令中引用它,而不需要显式地源它:

docker-compose --env-file .my-env  up -d

如果不传递--env-file标志,默认的env var文件将是.env

使用这种方法需要注意以下注意事项:

运行时环境中呈现的值总是覆盖.env文件中定义的值。类似地,通过命令行参数传递的值也具有优先级。

因此,要小心任何可能覆盖--env-file!

BASH解决方案:

我注意到Docker对环境变量的自动处理可能会引起混乱。我们不需要在Docker中处理环境变量,而是回到基础,比如bash!下面是一个使用bash脚本和.env文件的方法,具有一些额外的灵活性,以演示env vars的实用程序:

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 env 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文件中引用你的env变量与通常的bash语法(即${POSTGRES_VERSION}插入.env文件中的POSTGRES_VERSION)。

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

注意,COMPOSE_CONFIG是在我的.env文件中定义的,并在我的bash脚本中使用,但你可以很容易地用bash脚本中的my-compose-file.yml替换{$COMPOSE_CONFIG}

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

因为这个答案似乎很受欢迎,所以我写了一篇博客文章,更深入地描述了我的Docker部署工作流:https://modulitos.com/blog/lets-deploy-part-1/这可能会在你为部署配置添加更多复杂性时有所帮助,比如Nginx配置,LetsEncrypt certs和链接容器。

  1. 创建一个template.yml,这是你的docker-compose.yml环境变量。
  2. 假设你的环境变量在文件'env.sh'中
  3. 将下面的代码放在sh文件中并运行它。
< p > env.sh来源; Rm -rf docker-compose.yml; envsubst & lt;”模板。

. Yml "docker- composer . Yml "

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

示例模板。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

将env添加到。env文件中

VERSION=1.0.0

然后保存到deploy.sh

INPUTFILE=docker-compose.yml
RESULT_NAME=docker-compose.product.yml
NAME=test


prepare() {
local inFile=$(pwd)/$INPUTFILE
local outFile=$(pwd)/$RESULT_NAME
cp $inFile $outFile
while read -r line; do
OLD_IFS="$IFS"
IFS="="
pair=($line)
IFS="$OLD_IFS"
sed -i -e "s/\${${pair[0]}}/${pair[1]}/g" $outFile
done <.env
}
       

deploy() {
docker stack deploy -c $outFile $NAME
}


        

prepare
deploy
    

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

这是我的docker-compose。Yml看起来像:

services:
web:
env_file: variables.env
< p >注意: docker-compose期望env文件中的每一行都是VAR=VAL格式。避免在.env文件中使用export。另外,.env文件应该放在docker-compose命令执行的文件夹中

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

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

  2. 声明变量.env文件:

    HOSTNAME=your_hostname
    
  3. Change $hostname to ${HOSTNAME} at docker-compose.yaml file

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

Of course you can do that dynamically on each build like:

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

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}

使用.env文件在docker-compse.yml中定义动态值。不管是port还是其他值。

示例docker-compose:

testcore.web:
image: xxxxxxxxxxxxxxx.dkr.ecr.ap-northeast-2.amazonaws.com/testcore:latest
volumes:
- c:/logs:c:/logs
ports:
- ${TEST_CORE_PORT}:80
environment:
- CONSUL_URL=http://${CONSUL_IP}:8500
- HOST=${HOST_ADDRESS}:${TEST_CORE_PORT}

在.env文件中,你可以定义这些变量的值:

CONSUL_IP=172.31.28.151
HOST_ADDRESS=172.31.16.221
TEST_CORE_PORT=10002
以下内容适用于docker-compose 3.x 在容器内设置环境变量

方法- 1 直接的方法

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

method - 2 ”。env”文件

在docker-compose.yml文件所在的位置创建一个.env文件

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

你的合成文件会是这样的

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

source

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"

我从这个链接得到它 https://github.com/docker/cli/issues/939 < / p >

从1.25.4开始,docker-compose支持选项--env-file,它允许你指定一个包含变量的文件。

你的简历应该是这样的:

hostname=my-host-name

和命令:

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

最后我用了"sed"在我的deploy.sh脚本中完成这一点,尽管我的要求略有不同,因为docker-compose是由Terrafom: 通过Azure应用程序服务的Terraform脚本将变量传递给Docker Compose调用

eval "sed -i 's/MY_VERSION/$VERSION/' ../docker-compose.yaml"


cat ../docker-compose.yaml


terraform init
terraform apply -auto-approve \
-var "app_version=$VERSION" \
-var "client_id=$ARM_CLIENT_ID" \
-var "client_secret=$ARM_CLIENT_SECRET" \
-var "tenant_id=$ARM_TENANT_ID" \
-var "subscription_id=$ARM_SUBSCRIPTION_ID"


eval "sed -i 's/$VERSION/MY_VERSION/' ../docker-compose.yaml"

要添加和env变量,你可以定义一个env_file(让我们称之为var.env)为:

ENV_A=A
ENV_B=B

并将其添加到docker组合清单服务。此外,你可以直接用environment定义env变量。

例如,在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"

有关更多/更新信息,请查看这里:https://docs.docker.com/compose/environment-variables/

仅关注环境变量的默认值和强制值的问题,并作为@modulito的回答的更新:

现在支持在docker-compose.yml文件中使用默认值和强制强制值(来自文档):

同时支持$VARIABLE和${VARIABLE}语法。此外,当使用2.1文件格式时,可以使用典型的shell语法提供内联默认值:

${VARIABLE:-default}如果环境中的VARIABLE未设置或为空,则计算为默认值。 ${VARIABLE-default}只有在环境中未设置VARIABLE时才计算为默认值

类似地,下面的语法允许你指定强制变量:

< p > ${变量:?如果环境中的VARIABLE未设置或为空,则err}退出,并显示包含err的错误消息。 ${变量?如果环境中未设置VARIABLE,则err}退出,并返回包含err的错误消息

其他扩展的shell样式的特性,如${VARIABLE/foo/bar},是不支持的。

就像这样简单:

使用命令行正如文件中提到的:

docker-compose --env-file ./config/.env.dev config

或者使用.env文件,我认为这是最简单的方法:

 web:
env_file:
- web-variables.env

Doc with sample

这是为Docker v20编写的,使用docker compose v2命令。

我遇到了类似的障碍,发现--env-file参数docker compose config0用于docker compose config命令。在使用docker组合env_file变量的基础上,当我想在Dockerfile之外的其他地方重用它们时,仍然迫使我重复变量的值,例如environment用于docker-compose.yml。我只想要一个真相的来源,我的.env,能够在每个部署阶段交换它们。下面是我如何让它工作的,基本上使用docker compose config来生成一个基本的docker-compose.yml文件,该文件将ARG传递给Dockerfile

.local.env这将是你的.env,我有我的分裂不同的部署。

DEVELOPMENT=1
PLATFORM=arm64

docker-compose.config.yml -这是我的核心docker合成文件。

services:
server:
build:
context: .
dockerfile: docker/apache2/Dockerfile
args:
- PLATFORM=${PLATFORM}
- DEVELOPMENT=${DEVELOPMENT}
environment:
- PLATFORM=${PLATFORM}
- DEVELOPMENT=${DEVELOPMENT}

现在不幸的是,我需要两次传入变量,一次是Dockerfile,另一次是environment。然而,它们仍然来自单个源.local.env,因此至少我不需要重复值。

然后使用docker compose config生成一个半决赛的docker-compose.yml。这让我传入我的同伴override docker-compose.local.yml,用于最终部署发生的位置。

docker compose --env-file=.local.env -f docker-compose.config.yml config > docker-compose.yml

这将让我的Dockerfile访问.env变量。

FROM php:5.6-apache


# Make sure to declare after FROM
ARG PLATFORM
ARG DEVELOPMENT


# Access args in strings with $PLATFORM, and can wrap i.e ${PLATFORM}
RUN echo "SetEnv PLATFORM $PLATFORM" > /etc/apache2/conf-enabled/environment.conf
RUN echo "SetEnv DEVELOPMENT $DEVELOPMENT" > /etc/apache2/conf-enabled/environment.conf

然后将.env变量从docker-compose.yml传递到Dockerfile,然后将它传递到我的Apache HTTP服务器,该服务器将它传递到我的最终目的地,即PHP代码。

我的下一步是然后从我的部署阶段传入我的docker compose 覆盖

docker-compose.local.yml -这是我的docker重写。

services:
server:
volumes:
- ./localhost+2.pem:/etc/ssl/certs/localhost+2.pem
- ./localhost+2-key.pem:/etc/ssl/private/localhost+2-key.pem

最后,运行docker compose命令。

docker compose -f docker-compose.yml -f docker-compose.local.yml up --build

请注意,如果你改变了你的.env文件中的任何内容,你将需要重新运行docker compose config并为docker compose up添加--build。因为构建是缓存的,所以影响很小。

因此,对于我的最后一个命令,我通常运行:

docker compose --env-file=.local.env -f docker-compose.config.yml config > docker-compose.yml; docker compose --env-file=.local.env -f docker-compose.yml -f docker-compose.local.yml up --build

不要混淆.env文件和env_file选项!

它们的用途完全不同!

.env文件只将这些环境变量提供给你的码头工人组成文件,而你的码头工人组成文件也可以传递给容器。

但是env_file选项只将这些变量传递给容器,而则传递给docker合成文件😵‍💫

例子

  1. 假设我们有这个简单的合成文件:
services:
foo:
image: ubuntu
hostname: suchHostname # <-------------- hard coded 'suchHostname'
volumes:
- /mnt/data/logs/muchLogs:/logs # <--- hard coded 'muchLogs'
- /mnt/data/soBig:/data # <----------- hard coded 'soBig'
  1. 我们不想再硬编码了!因此,我们可以将它们放在当前终端的环境变量中,并检查docker-compose是否理解它们:
$ export the_hostname="suchHostName"
$ export dir_logs="muchLogs"
$ export dir_data="soBig"
  1. 并将docker-compose.yml文件更改为:
services:
foo:
image: ubuntu
hostname: $the_hostname # <-------------- use $the_hostname
volumes:
- /mnt/data/logs/$dir_logs:/logs # <--- use $dir_logs
- /mnt/data/$dir_data:/data # <-------- usr $dir_data
  1. 现在,让我们检查一下它在执行$ docker-compose convert并检查输出时是否有效:
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
  1. 好的,它工作!但是让我们改用.env文件。因为docker-compose理解.env文件,所以我们只创建一个并设置它:
# .env file (in the same directory as 'docker-compose.yml')
the_hostname="suchHostName"
dir_logs="muchLogs"
dir_data="soBig"

好,你可以用终端测试它(这样我们用export设置的旧环境变量就不会干扰,并确保一切都在干净的终端中工作)🖥只需再次遵循步骤4,看看它是否工作!

到目前为止还不错😃然而,当你偶然发现env_file选项时,它会让人困惑🤔让我们说你想传递一个密码到码头工人组成文件(容器)。

🙄在错误的方法中,你可能会把密码放在.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合成文件。

结束

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

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

env_file选项只将这些额外的变量传递给容器📦,而不是合成文件🐳