【问题标题】:Pass ENV variable with docker-compose.yml at runtime to Dockerfile在运行时使用 docker-compose.yml 将 ENV 变量传递给 Dockerfile
【发布时间】:2019-05-27 05:39:21
【问题描述】:

我正在尝试根据 docker-compose.yml 中的环境 (dev/uat/prod) 覆盖 dockerfile 中的 ENV 值。因为一些设置是敏感的,所以我不能让这些设置成为 docker 映像的构建步骤的一部分,而是必须在运行时覆盖这些值。我一直在尝试通过 docker stack deploy -c 命令部署以下 docker-compose,但是我注意到我的环境变量没有覆盖 dockerfile 中的现有变量。

Dockerfile:

FROM openjdk:8-jdk-alpine
VOLUME /tmp
VOLUME /etc

ADD sample-0.0.1-SNAPSHOT.jar app.jar

ENV ENV_SETTINGS=ssldev
ENV ZK_HOST=zoo1
ENV JAVA_OPTS="-server -Xms6048m -Xmx6048m -XX:+UseParNewGC - XX:+UseConcMarkSweepGC -XX:+UseTLAB -XX:NewSize=128m -XX:MaxNewSize=128m - XX:MaxTenuringThreshold=0 -XX:SurvivorRatio=1024 - XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=40 - XX:MaxGCPauseMillis=1000 -XX:InitiatingHeapOccupancyPercent=50 - XX:+UseCompressedOops -XX:ParallelGCThreads=8 -XX:ConcGCThreads=8 - XX:+DisableExplicitGC -Dspring.profiles.active=${ENV_SETTINGS}"

EXPOSE 8080
ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar" ]
HEALTHCHECK CMD curl --fail -k https://localhost:8080/status || exit 1

docker-compose.yml

version: '3.1'

services:
api:
    image: SOME_RANDOM_IMAGE
    ports:
        - "9083:8080"
    networks:
        - net
    deploy:
        restart_policy:
            condition: on-failure
        mode: global
    environment:
        - ENV_SETTINGS: default,ssldev,postgres
        - ZK_HOST: zoo1:2181,zoo2:2181
networks:
nsp_net:
  external:
    name: net

【问题讨论】:

  • Dockerfile 中的 ENV 语句在构建镜像时会被全面评估。如果你docker inspect你应该看到的图像在图像中 $JAVA_OPTS-Dspring.profiles.active=ssldev结尾;更改 $ENV_SETTINGS 不会影响这个现在固定的值。
  • 我怎样才能在不使用 args 选项重建图像的情况下覆盖这些值?由于 args 只能通过要求重建来修改,其他 Docker 映像如何允许人们设置环境设置以覆盖他们的配置?
  • 如果在构建过程中也对 ENV 语句进行了全面评估,那么即使有 docker-compose 的环境选项又有什么意义呢?

标签: docker docker-compose


【解决方案1】:

当您在 Dockerfile 中编写 ENV 语句时,docker build 步骤会完全扩展该语句,并在其构建的映像中“烘焙”扩展值。 Docker 没有提供在docker run 时间重新扩展环境变量的步骤。也就是说,您的问题不是 ENV_SETTINGS 没有被设置,而是 JAVA_OPTS 没有被重新解释为 ENV_SETTINGS 的新值。

要解决此问题,您可以提供一个设置环境的 shell 脚本。我倾向于将需要此功能的图像分为两部分:

  1. 图像的 ENTRYPOINT 是一个脚本,用于设置环境变量并进行其他准备工作,并以 exec "$@" 结尾以运行图像的 CMD 或传递给 docker run 的任何命令。
  2. 图像的 CMD 是您希望它运行的实际命令。

您的 shell 脚本可能如下所示:

#!/bin/sh
JAVA_OPTS="$JAVA_OPTS -Dspring.profiles.active=${ENV_SETTINGS:-ssldev}"
exec "$@"

相应的 Dockerfile 如下所示:

FROM openjdk:8-jdk-alpine
# Don't declare VOLUME of anything, especially not system directories.
# Prefer COPY to ADD.
COPY sample-0.0.1-SNAPSHOT.jar app.jar
# Should be executable (chmod +x) as checked into source control.
COPY entrypoint.sh entrypoint.sh

ENV ENV_SETTINGS=ssldev
ENV ZK_HOST=zoo1
# Don't include -Dspring.profiles.active here.
# Do include -Djava.security.egd here.
# The JVM knows about $JAVA_OPTS.
ENV JAVA_OPTS="-server -Xms6048m -Xmx6048m -XX:+UseParNewGC - XX:+UseConcMarkSweepGC -XX:+UseTLAB -XX:NewSize=128m -XX:MaxNewSize=128m - XX:MaxTenuringThreshold=0 -XX:SurvivorRatio=1024 - XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=40 - XX:MaxGCPauseMillis=1000 -XX:InitiatingHeapOccupancyPercent=50 - XX:+UseCompressedOops -XX:ParallelGCThreads=8 -XX:ConcGCThreads=8 - XX:+DisableExplicitGC -Djava.security.egd=file:/dev/./urandom"

EXPOSE 8080
ENTRYPOINT ["./entrypoint.sh"]
# Prefer quoted-word form.  But if you need a shell to process the
# command line, use unquoted form; don't explicitly "sh -c".
CMD ["java", "-jar", "app.jar"]
HEALTHCHECK CMD curl --fail -k https://localhost:8080/status || exit 1

(我总是更喜欢 CMD 而不是 ENTRYPOINT:这个包装器模式非常有用且无处不在,最好让它可用,如果您不必重写执行此操作的入口点。例外情况是,如果我正在构建一个 FROM scratch 图像,除了运行捆绑的二进制文件之外几乎不可能做任何事情,但这是一个例外。)

(另请注意,从 Java 8 的补丁版本开始,较新的 JVM 可以选择遵守容器的内存限制,这会导致更小的 JAVA_OPTS 设置;请参阅“让 JVM 尊重 CPU 和 RAM 限制” the openjdk image documentation.)

【讨论】:

  • 感谢这对我帮助很大。我看到的唯一问题是,当我在实施后检查容器时,entrypoint.sh 没有更新现有的 JAVA_OPTS 环境变量。它似乎仍然只是通过 ENV JAVA_OPTS 语句设置它,而不是从 entrypoint.sh 设置它
  • "JAVA_OPTS=-server -Xms6048m -Xmx6048m -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+UseTLAB -XX:NewSize=128m -XX:MaxNewSize=128m -XX:MaxTenuringThreshold=0 -XX:SurvivorRatio=1024 -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=40 -XX:MaxGCPauseMillis=1000 -XX:InitiatingHeapOccupancyPercent=50 -XX:+UseCompressedOops -XX:ParallelGCThreads=8 -XX:ConcGCThreads=8 -XX:+ DisableExplicitGC -Djava.security.krb5.conf=/etc/krb5.conf -Djava.security.egd=file:/dev/./urandom -Djava.security.properties=/etc/disabled_tlsv1.properties"
  • 我的入口点#!/bin/sh JAVA_OPTS="${JAVA_OPTS} -Dspring.profiles.active=${ENV_SETTINGS}" exec "$@"
  • 确实如此;同理,如果你有一个shell,启动一个后台进程会改变一些环境变量,而echo父shell中的变量,子进程无法改变父进程的环境。这不是错误或实际问题。如果你 docker run ... sh -c 'echo $JAVA_OPTS' 你会看到正确的东西。
猜你喜欢
  • 1970-01-01
  • 2021-12-13
  • 2018-08-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-15
  • 1970-01-01
相关资源
最近更新 更多