【问题标题】:How pass environment variables in the container using Dockerfile but with a separate file for environment variables like .env如何使用 Dockerfile 在容器中传递环境变量,但使用单独的环境变量文件(如 .env)
【发布时间】:2020-11-11 16:13:47
【问题描述】:

所以我现在被这个问题困扰了很长一段时间。

我知道我们应该这样编写代码,以便我们可以随时使项目开源,而不会影响机密方面。

所以,我似乎理解直接在 Dockerfile 或 docker-compose.yml 中定义环境变量是不可取的,我们应该将它们全部定义在一个单独的文件中,例如 .env强>。

现在,我知道我们可以在处理 docker-compose 时使用docker-compose -f docker-compose.yml --env-file=.env up

我想知道以下两件事:

  1. 如何处理 Dockerfile 从构建到运行(环境变量文件部分,如 --env-file
  2. 我们是应该在其中构建已定义环境变量的映像,然后与其他人共享该映像,以便他们不必在运行映像时重新定义环境变量而烦恼,还是应该构建一个通用映像和然后让用户随意定义环境变量。什么是最佳做法?

有人也可以评论以下内容(使用 Dockerfile)

方法一:

example_Dockerfile

FROM python:latest

# many other steps in between

RUN printf 'key=21345' > config

ENTRYPOINT ["./boot.sh"]

构建和运行的步骤:

  1. docker build -f example_Dockerfile -t my_container .
  2. docker run --env-file=example.env my_container

我知道方法 1 是正确的,但我想知道是否可以做类似方法 2(以下)的事情。

方法二:

example_Dockerfile

FROM python:latest

# many other steps in between 

RUN printf 'key=${KEY}' > config

ENTRYPOINT ["./boot.sh"]

example.env

# many other environment variables

KEY = 21345

如果您还可以提供命令步骤以这种方法构建和运行容器来执行此操作,那就太棒了。

这两种方法的主要区别在于,在方法 2 中,我在 .env 文件中定义了 KEY 变量,并希望以某种方式将其集成到Dockerfile(通过使用 ${KEY},就像我们在 docker-compose.yml 中所做的那样),而在 Dockerfile

中没有提及该值

如果有人能举例说明上述两点,我将不胜感激。

非常感谢。

【问题讨论】:

    标签: docker docker-compose environment-variables dockerfile containers


    【解决方案1】:

    我的一般经验是,最好将环境变量用于可以(或应该或必须)在运行时更改的设置,但这比命令行参数或配置文件等其他机制更可取。这包括您所依赖的其他服务的主机名之类的内容。典型的最小 docker-compose.yml 设置可能如下所示:

    version: '3.8'
    services:
      app:
        build: .
        ports: ['8080:8080']
        environment:
          - REDIS_HOST=redis
          - PUBLIC_URL=http://example.com/
      redis:
        image: redis:6.0
    

    我倾向于最小化 Dockerfile 中的环境变量。您在问题中暗示了一些保密问题;任何拥有图像的人都可以docker inspectdocker run --rm imagename env 将它们丢弃,无论它们是如何添加的;构建时.env 文件无济于事。在其他 SO 问题中,我看到用于文件系统路径或端口的变量,但这些不需要在运行时更改。

    # Avoid setting filesystem paths or ports as environment variables:
    ENV APP_DIR=/app
    COPY . $APP_DIR
    RUN chmod +x $APP_DIR/entrypoint.sh
    ENV PORT=8080
    EXPOSE $PORT
    
    # It's fine to hard-code these values, and use relative paths:
    WORKDIR /app
    COPY . .
    RUN chmod +x entrypoint.sh
    EXPOSE 8080
    
    # No matter what, the container will always be run as
    docker run -v $PWD/data:/app/data -p 8080:8080 imagename
    # and changing the build-time environment variables has no effect
    

    就风格而言,让 Dockerfile 尽可能简单(但不会更简单)会更好一些。如果你有一个固定键和固定值的配置文件,不要尝试在 Dockerfile 中生成它;直接在磁盘上维护它,如果对您有意义,可以使用不同的名称。

    # Don't RUN echo ... > file; instead just
    COPY config.docker.yaml ./config.yaml
    

    另一方面,它不需要设置环境变量,因此您需要容忍某些未设置的内容。我发现有用的模式是猜测如果程序直接运行,它是由不需要手动配置的开发人员,如果它在容器中运行,像 Compose 或 Kubernetes 这样的自动化将提供环境变量为你。

    # Check that $PUBLIC_URL is set
    public_url = os.environ.get('PUBLIC_URL')
    if public_url is None:
        print('Please set $PUBLIC_URL environment variable')
        os.exit(1)
    
    # Get the Redis host, or assume a developer environment
    redis_host = os.environ.get('REDIS_HOST', 'localhost')
    

    我前面说过,我更喜欢直接使用环境变量而不是配置文件。您的示例 1 和 2 的输出是相同的,因为配置文件在构建时是固定的。如果您需要基于环境变量重新创建配置文件,则必须在容器启动时在入口点脚本中执行此操作。一个典型的例子可能是使用 GNU envsubst 工具(不会预装在基于 Alpine 的镜像上)来重写它:

    #!/bin/sh
    # Rewrite the config file
    envsubst < config.tmpl > config
    # Switch over to the main container process
    exec "$@"
    
    # ENTRYPOINT _must_ be JSON-array syntax for this
    ENTRYPOINT ["./entrypoint.sh"]
    CMD ["./boot.sh"]
    

    envsubst 替换文件中的 shell 样式 $ENVIRONMENT_VARIABLE 引用,因此此处的 config.tmpl 文件可能如下所示

    key=$KEY
    

    【讨论】:

      【解决方案2】:

      这个用例有两个方面:

      • 如果环境变量仅用于构建,最好使用 docker build arg。 Build args 与 env vars 的工作方式相同,但仅在 docker build 期间,它们不会打包在最终映像中。更多信息请查看link
        • docker build --build-arg VAR_A=val_a -f Dockerfile .
        • Dockerfile
          FROM python:3.7-slim
          ARG VAR_A
          RUN echo $VAR_A
          
      • 在某些情况下,环境变量是通用的并且运行时需要,您可以使用 Dockerfile 中的 ENV 指令安全地打包它们

      [Bonus Tip] - 最好避免在 Dockerfile 中使用带有 FROM 指令的 latest 镜像标签,因为这使得很难跟踪哪个版本的镜像正在运行并且很难回滚。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-05-11
        • 1970-01-01
        • 2017-06-12
        • 2021-01-30
        • 1970-01-01
        • 2018-04-09
        • 2019-12-07
        相关资源
        最近更新 更多