首页 > 解决方案 > 在运行时使用 docker-compose.yml 将 ENV 变量传递给 Dockerfile

问题描述

我正在尝试根据 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

码头工人-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

标签: dockerdocker-compose

解决方案


当您ENV在 Dockerfile 中编写语句时,该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:这个包装器模式非常有用且无处不在,最好让它可用,docker run --rm -it imagename sh如果您不必重写入口点来执行它,那么获得交互式调试 shell 会容易得多。例外是,如果我正在构建一个FROM scratch除了运行捆绑的二进制文件之外几乎不可能做任何事情的图像,但这是一个例外。)

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


推荐阅读