ChatGPT解决这个技术问题 Extra ChatGPT

如何将环境变量传递给 Docker 容器?

我是 Docker 新手,不清楚如何从容器访问外部数据库。在连接字符串中硬编码的最佳方法是什么?

# Dockerfile
ENV DATABASE_URL amazon:rds/connection?string

Z
Zorawar

您可以使用 -e 标志将环境变量传递给您的容器。

启动脚本的示例:

sudo docker run -d -t -i -e REDIS_NAMESPACE='staging' \ 
-e POSTGRES_ENV_POSTGRES_PASSWORD='foo' \
-e POSTGRES_ENV_POSTGRES_USER='bar' \
-e POSTGRES_ENV_DB_NAME='mysite_staging' \
-e POSTGRES_PORT_5432_TCP_ADDR='docker-db-1.hidden.us-east-1.rds.amazonaws.com' \
-e SITE_URL='staging.mysite.com' \
-p 80:80 \
--link redis:redis \  
--name container_name dockerhub_id/image_name

或者,如果您不想在命令行中使用 ps 等将显示的值,则 -e 可以从当前环境中提取值,如果您只是在没有 { 的情况下提供它3}:

sudo PASSWORD='foo' docker run  [...] -e PASSWORD [...]

如果您有许多环境变量,特别是如果它们是保密的,您可以use an env-file

$ docker run --env-file ./env.list ubuntu bash

--env-file 标志将文件名作为参数,并期望每一行采用 VAR=VAL 格式,模仿传递给 --env 的参数。注释行只需要以# 为前缀


我将 docker run 命令存储在 shell 脚本中(./start_staging.sh 等),然后使用 Ansible 远程执行它们。
我无法让第二个版本正常工作;我在环境中设置了PASSWORD=foo,然后通过--env PASSWORD,容器的config.json中只出现了“PASSWORD”这个词;每个其他环境变量都有一个键和一个值。我正在使用 Docker 1.12.1。
@KevinBurke:改为 export PASSWORD=foo ,变量将作为环境变量传递给 docker run ,使 docker run -e PASSWORD 工作。
为了清楚起见,命令行中的 -e 和 Dockerfile 中的 ENV 做同样的事情吗?
我学到的痛苦的事情是,您应该在 docker 映像的名称之前传递所有 -e 值,否则不会引发错误,并且所有变量都不会有值!
P
Pang

您可以使用 -e 参数与 docker run .. 命令一起传递,如 here 和 @errata 所述。

但是,这种方法可能的缺点是您的凭据将显示在您运行它的进程列表中。

为了使其更安全,您可以在配置文件中写入您的凭据,然后使用 --env-file 执行 docker run,如提到的 here。然后您可以控制该配置文件的访问权限,以便其他有权访问该计算机的人看不到您的凭据。


我在@errata 的回答中添加了另一种解决此问题的方法。
请注意 --env-file,当您使用 --env 时,您的 env 值将使用您正在使用的任何 shell 的标准语义进行引用/转义,但在使用 --env-file 时,您将在容器中获得的值会有所不同。 docker run 命令只是读取文件,进行非常基本的解析并将值传递给容器,它并不等同于你的 shell 的行为方式。如果您要将一堆 --env 条目转换为 --env-file,请注意一个小问题。
为了详细说明 Shorn 的答案,在使用 env 文件时,我不得不将一个很长的环境变量的值全部放在一行上,因为似乎没有任何方法可以在其中放置换行符,或者将其分成多行,例如: $MY_VAR=stuff $MY_VAR=$MY_VAR more stuff
P
Pang

如果您使用 'docker-compose' 作为启动容器的方法,实际上有一种有用的方法可以将服务器上定义的环境变量传递给 Docker 容器。

在您的 docker-compose.yml 文件中,假设您正在启动一个基本 hapi-js 容器,代码如下所示:

hapi_server:
  container_name: hapi_server
  image: node_image
  expose:
    - "3000"

假设您的 docker 项目所在的本地服务器有一个名为“NODE_DB_CONNECT”的环境变量,您希望将它传递给您的 hapi-js 容器,并且您希望它的新名称为“HAPI_DB_CONNECT”。然后在 docker-compose.yml 文件中,将本地环境变量传递给容器并重命名,如下所示:

hapi_server:
  container_name: hapi_server
  image: node_image
  environment:
    - HAPI_DB_CONNECT=${NODE_DB_CONNECT}
  expose:
    - "3000"

我希望这可以帮助您避免在容器中的任何文件中硬编码数据库连接字符串!


这行不通。这些变量不会传递给容器。
@Frondor 真的吗?根据这些docs,它似乎应该。
这种方法的问题是您将 docker-compose.yml 文件中的环境变量提交到您不应该的 git 存储库。你怎么解决这个问题?理想情况下,您将拥有一个单独的 gitignored env 文件,并且可以导入/加载到 Dockerfile 或 docker-compose.yml
g
godblessstrawberry

使用 -e--env 值设置环境变量(默认 [])。

启动脚本的示例:

 docker run  -e myhost='localhost' -it busybox sh

如果您想从命令行使用多个环境,那么在每个环境变量之前使用 -e 标志。

例子:

 sudo docker run -d -t -i -e NAMESPACE='staging' -e PASSWORD='foo' busybox sh

注意:确保将容器名称放在环境变量之后,而不是之前。

如果您需要设置许多变量,请使用 --env-file 标志

例如,

 $ docker run --env-file ./my_env ubuntu bash

如需任何其他帮助,请查看 Docker 帮助:

 $ docker run --help

官方文档:https://docs.docker.com/compose/environment-variables/


为什么我们需要 ubuntu bash?它适用于使用 ubuntu 作为基础镜像创建的镜像还是每个镜像?
@ReyanshKharga ubuntu 是图像的名称,而 bash 是您正在执行的命令。 bash 为您提供了一个终端(尽管我认为您需要 -it 作为交互式终端)。
P
Pang

使用 docker-compose,您可以继承 docker-compose.yml 中的环境变量,然后继承 docker-compose 调用的任何 Dockerfile(s) 以构建映像。这在 Dockerfile RUN 命令应该执行特定于环境的命令时很有用。

(您的外壳 RAILS_ENV=development 已存在于环境中)

码头工人-compose.yml:

version: '3.1'
services:
  my-service: 
    build:
      #$RAILS_ENV is referencing the shell environment RAILS_ENV variable
      #and passing it to the Dockerfile ARG RAILS_ENV
      #the syntax below ensures that the RAILS_ENV arg will default to 
      #production if empty.
      #note that is dockerfile: is not specified it assumes file name: Dockerfile
      context: .
      args:
        - RAILS_ENV=${RAILS_ENV:-production}
    environment: 
      - RAILS_ENV=${RAILS_ENV:-production}

Dockerfile:

FROM ruby:2.3.4

#give ARG RAILS_ENV a default value = production
ARG RAILS_ENV=production

#assign the $RAILS_ENV arg to the RAILS_ENV ENV so that it can be accessed
#by the subsequent RUN call within the container
ENV RAILS_ENV $RAILS_ENV

#the subsequent RUN call accesses the RAILS_ENV ENV variable within the container
RUN if [ "$RAILS_ENV" = "production" ] ; then echo "production env"; else echo "non-production env: $RAILS_ENV"; fi

这样,我不需要在文件或 docker-compose build/up 命令中指定环境变量:

docker-compose build
docker-compose up

他们必须是相同的名字吗?似乎有点令人困惑..如果我想运行开发,我将如何覆盖 args?
@CyberMew 是的,您的环境、docker-compose 和 Dockerfile 之间的名称必须相同。如果你想改为运行开发,在运行 docker-compose build 之前,在终端中运行 RAILS_ENV=development 来设置环境变量,这样 docker-compose 和 Dockerfile 将从你的环境中继承该值。
S
Syed Habib M

我们还可以使用 -e 标志和 $ 使用主机环境变量:

在运行以下命令之前,需要导出(意味着设置)本地环境变量。

docker run -it -e MG_HOST=$MG_HOST \
    -e MG_USER=$MG_USER \
    -e MG_PASS=$MG_PASS \
    -e MG_AUTH=$MG_AUTH \
    -e MG_DB=$MG_DB \
    -t image_tag_name_and_version

通过使用此方法,您可以使用您的给定名称自动设置环境变量。在我的情况下(MG_HOST,MG_USER)

此外:

如果您使用的是 python,您可以通过以下方式访问 docker 内的这些环境变量

import os

host = os.environ.get('MG_HOST')
username = os.environ.get('MG_USER')
password = os.environ.get('MG_PASS')
auth = os.environ.get('MG_AUTH')
database = os.environ.get('MG_DB')


如果有人仍然对这个 docker run 命令有问题,值得注意的是 env 变量 -e 必须在 -t 之前,如图所示。我把我的放在图像之后,但它不起作用。
是的,您应该在 -t 标志之前使用 -e 标志,但如果您分享错误,那么我们可以更好地理解......
就是这样,没有错误。环境变量根本没有传递。在我像您在答案中显示的那样切换顺序之前,我无法弄清楚问题出在哪里。
A
Alex T

有一个很好的 hack 如何将主机环境变量通过管道传输到 docker 容器:

env > env_file && docker run --env-file env_file image_name

非常小心地使用这种技术,因为 env > env_file 会将所有主机 ENV 变量转储到 env_file 并使其在运行的容器中可访问。


对于 zsh 进程替换会将此命令简化为:docker run --env-file =(env) image_name
T
T Brown

我遇到的问题是我将 --env-file 放在命令的末尾

docker run -it --rm -p 8080:80 imagename --env-file ./env.list

使固定

docker run --env-file ./env.list -it --rm -p 8080:80 imagename

我犯了这个愚蠢的错误。谢谢@T布朗。
s
sanmai

另一种方法是使用 /usr/bin/env 的幂:

docker run ubuntu env DEBUG=1 path/to/script.sh

P
Peter Mortensen

对于 Amazon AWS ECS/ECR,您应该通过私有 S3 存储桶管理您的环境变量(尤其是机密)。请参阅博文How to Manage Secrets for Amazon EC2 Container Service–Based Applications by Using Amazon S3 and Docker


或 SSM 参数存储
P
Pang

如果您在本地的 env.sh 中有环境变量并希望在容器启动时设置它,您可以尝试

COPY env.sh /env.sh
COPY <filename>.jar /<filename>.jar
ENTRYPOINT ["/bin/bash" , "-c", "source /env.sh && printenv && java -jar /<filename>.jar"]

该命令将使用 bash shell 启动容器(我想要一个 bash shell,因为 source 是一个 bash 命令),获取 env.sh 文件(设置环境变量)并执行 jar 文件。

env.sh 看起来像这样,

#!/bin/bash
export FOO="BAR"
export DB_NAME="DATABASE_NAME"

我添加 printenv 命令只是为了测试实际的源命令是否有效。当您确认源命令正常工作或环境变量将出现在您的 docker 日志中时,您可能应该将其删除。


使用这种方法,每次要传递不同/修改的环境集时,都必须重建 docker 映像。在“docker --run --env-file ./somefile.txt”期间传递环境是优越/动态的方法。
@DmitryShevkoplyas 我同意。我的用例是没有将 --env-file 参数指定给 docker run 命令的选项。例如,如果您使用 Google 应用引擎部署应用程序,并且在容器内运行的应用程序需要在 docker 容器内设置环境变量,则您无法直接设置环境变量,因为您无法控制 { 2}命令。在这种情况下,您可以使用 KMS 解密 env 变量的脚本,并将它们添加到 env.sh 中,该 env.sh 可用于设置 env 变量。
您可以使用常规 sh 而不是 source 中可用的 POSIX .(点)命令。 (source. 相同)
请注意,如果您将调用包装在 shell 命令中,信号将不会到达您的可执行文件。基本上,如果没有一些额外的 bash fu,您将无法中断您的进程。
D
Daniel Compton

docker run --rm -it --env-file <(bash -c 'env | grep <your env data>') 是一种 grep 存储在 .env 中的数据并将它们传递给 Docker 的方法,不会不安全地存储任何内容(因此您不能只查看 docker history 并获取密钥。

假设您的 .env 中有大量 AWS 内容,如下所示:

AWS_ACCESS_KEY: xxxxxxx
AWS_SECRET: xxxxxx
AWS_REGION: xxxxxx

使用 docker run --rm -it --env-file <(bash -c 'env | grep AWS_') 运行 docker 将全部抓取并安全地传递它,以便从容器内访问。


A
Alexander Mills

使用 jq 将 env 转换为 JSON:

env_as_json=`jq -c -n env`
docker run -e HOST_ENV="$env_as_json" <image>

这需要 jq 版本 1.6 或更高版本

这将主机环境作为 json 推送,基本上就像在 Dockerfile 中一样:

ENV HOST_ENV  (all env from the host as json)

那条线对你有什么作用?:docker run -e HOST_ENV="$env_as_json" <image>?在我的情况下,当作为 docker args 传递时,Docker 似乎没有解析变量或子 shell(${}$())。例如:A=123 docker run --rm -it -e HE="$A" ubuntu 然后在该容器内:root@947c89c79397:/# echo $HE root@947c89c79397:/# .... HE 变量没有成功。
E
EDU_EVER

这就是我能够解决它的方法

docker run --rm -ti -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY -e AWS_SESSION_TOKEN -e AWS_SECURITY_TOKEN amazon/aws-cli s3 ls

再举一个例子:

export VAR1=value1
export VAR2=value2

$ docker run --env VAR1 --env VAR2 ubuntu env | grep VAR
VAR1=value1
VAR2=value2

D
Dark Light

为了通过 docker-compose 传递多个环境变量,也可以在 docker-compose 文件中使用环境文件。

web:
 env_file:
  - web-variables.env

https://docs.docker.com/compose/environment-variables/#the-env_file-configuration-option


B
Brent Bradburn

使用 docker run 设置环境变量时存在一些文档不一致之处。

online referece 说明了一件事:

--env , -e 设置环境变量

manpage 有点不同:

-e, --env=[] 设置环境变量

docker run --help 再次给出了其他内容:

-e, --env list 设置环境变量

在任何可用文档中都不一定清楚的东西:

-e--env 之后的尾随空格可以替换为 =,或者在 -e 的情况下可以完全省略:

$ docker run -it -ekey=value:1234 ubuntu env
key=value:1234

我通过反复试验发现的一个技巧(以及上面的线索)......

如果您收到错误消息:

未知标志:--env

那么您可能会发现在 --env 中使用等号会很有帮助,例如:

--env=key=value:1234

启动容器的不同方法可能有不同的解析场景。

在各种组合配置中使用 Docker 时,这些技巧可能会有所帮助,例如 Visual Studio Code devcontainer.json,其中 runArgs 数组中不允许有空格。


y
yash bhangare

您可以使用 -e 或 --env 作为参数,后跟键值格式,例如: docker build -f file_name -e MYSQL_ROOT_PASSWORD=root