【问题标题】:How do I pass environment variables to Docker containers?如何将环境变量传递给 Docker 容器?
【发布时间】:2015-08-10 05:21:03
【问题描述】:

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

# Dockerfile
ENV DATABASE_URL amazon:rds/connection?string

【问题讨论】:

    标签: docker environment-variables dockerfile


    【解决方案1】:

    您可以使用 -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 可以从当前环境中提取值,只要您提供它没有=:

    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 值,否则不会引发错误,并且没有任何变量会有值!
    【解决方案2】:

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

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

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

    【讨论】:

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

    如果您使用“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
    【解决方案4】:

    使用-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 作为一个交互式终端)。
    【解决方案5】:

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

    (您的 shell 环境中已经存在 RAILS_ENV=development

    docker-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-composebuild/up命令中指定环境变量:

    docker-compose build
    docker-compose up
    

    【讨论】:

    • 他们必须同名吗?似乎有点令人困惑.. 如果我想改为运行开发,我将如何覆盖 args?
    • @Cyber​​Mew 是的,您的环境、docker-compose 和 Dockerfile 之间的名称必须相同。如果您想改为运行开发,请在运行 docker-compose build 之前,在终端中运行 RAILS_ENV=development 来设置环境变量,这样 docker-compose 和 Dockerfile 将从您的环境中继承该值。
    【解决方案6】:

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

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

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

    【讨论】:

    • 对于 zsh 进程替换将把这个命令简化为:docker run --env-file =(env) image_name
    【解决方案7】:

    我们还可以使用 -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')
    
    

    【讨论】:

      【解决方案8】:

      我遇到的问题是我将 --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 Brown。
      【解决方案9】:

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

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

      【讨论】:

        【解决方案10】:

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

        【讨论】:

        • 或SSM参数存储
        【解决方案11】:

        如果你在本地有一个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 arg 指定给docker run 命令的选项。例如,如果您正在使用 Google 应用引擎部署应用程序,并且在容器内运行的应用程序需要在 docker 容器内设置环境变量,则您无法直接设置环境变量,因为您无法控制 @ 987654330@ 命令。在这种情况下,您可以有一个脚本,使用 KMS 解密 env 变量,并将它们添加到 env.sh 中,可用于设置 env 变量。
        • 您可以使用常规sh 中提供的POSIX .(点)命令,而不是source。 (source. 相同)
        • 请注意,如果您将调用包装在 shell 命令中,信号将不会到达您的可执行文件。基本上,如果没有一些额外的 bash fu,您将无法中断您的进程。
        【解决方案12】:

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

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

        AWS_ACCESS_KEY: xxxxxxx
        AWS_SECRET: xxxxxx
        AWS_REGION: xxxxxx
        

        使用docker run --rm -it --env-file &lt;(bash -c 'env | grep AWS_') 运行 docker 将获取所有内容并将其安全传递,以便从容器内访问。

        【讨论】:

          【解决方案13】:

          使用 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" &lt;image&gt; ?在我的情况下,当作为 docker args 传递时,Docker 似乎没有解析变量或子 shell(${}$())。例如:A=123 docker run --rm -it -e HE="$A" ubuntu 然后在该容器内:root@947c89c79397:/# echo $HE root@947c89c79397:/# .... HE 变量没有成功。
          【解决方案14】:

          这是我解决它的方法

          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
          

          【讨论】:

            猜你喜欢
            • 2015-09-18
            • 2019-04-10
            • 2022-01-16
            • 2021-06-07
            • 2020-02-23
            • 2021-06-21
            • 2016-12-08
            • 2021-06-25
            • 2017-09-18
            相关资源
            最近更新 更多