在运行时将带有docker-compose.yml的ENV变量传递给Dockerfile [英] Pass ENV variable with docker-compose.yml at runtime to Dockerfile
问题描述
我试图根据docker-compose.yml中的环境(dev / uat / prod)覆盖dockerfile中的ENV值。由于某些设置是敏感的,因此我不能将这些设置放在docker映像的构建步骤之外,因此必须在运行时覆盖这些值。我一直试图通过docker stack deploy -c命令部署以下docker-compose,但是我注意到我的环境变量没有覆盖dockerfile中的现有变量。
I am trying to override the ENV values in my dockerfile depending on the environment (dev/uat/prod) from my docker-compose.yml. Because some of the settings are sensitive I cant have these settings be apart of the build step of the docker image, instead these values must be overridden during runtime. Ive been trying to deploy the following docker-compose via the docker stack deploy -c command, however i noticed my environment variables are not overriding the existing ones in the dockerfile.
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
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 build
步骤完全扩展了该范围,并嵌入了所构建映像中的扩展值。 Docker没有提供在 docker run
时重新扩展环境变量的步骤。也就是说,您的问题不是 ENV_SETTINGS
没有设置,而是 JAVA_OPTS
没有被重新解释。 ENV_SETTINGS
的新值。
When you write an ENV
statement in a Dockerfile, the docker build
step fully expands that and "bakes in" the expanded value in the image it builds. Docker does not provide a step that will re-expand environment variables at docker run
time. That is, your issue is not that ENV_SETTINGS
is not getting set, but rather that JAVA_OPTS
is not getting reinterpreted with the new value of ENV_SETTINGS
.
要解决此问题,您可以提供一个设置环境的Shell脚本。我倾向于将图像分为两部分:
To work around this, you can provide a shell script that sets up the environment. I tend to structure images that need this in two parts:
- 图像的ENTRYPOINT是设置环境变量并进行其他准备工作的脚本,并以
exec $ @
结尾以运行映像的CMD或传递给docker run
的任何命令。 - 图像的CMD是您要运行的实际命令。
- The image's ENTRYPOINT is a script that sets up environment variables and does other preparation, and ends with
exec "$@"
to run the image's CMD or whatever command got passed todocker run
. - The image's CMD is the actual command you want it to run.
您的shell脚本可能看起来像像这样:
Your shell script might look like this:
#!/bin/sh
JAVA_OPTS="$JAVA_OPTS -Dspring.profiles.active=${ENV_SETTINGS:-ssldev}"
exec "$@"
相应的Dockerfile如下所示:
The corresponding Dockerfile would look like:
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
要获得交互式调试外壳,如果不必重写入口点,则要容易得多例外是,如果我正在构建 FROM暂存器
映像,那么除了运行捆绑的二进制文件外,实际上什么也做不了,但这是一个例外。)
(I always prefer CMD to ENTRYPOINT: this wrapper pattern is useful and ubiquitous enough that it's better to have it available, and it's much easier to docker run --rm -it imagename sh
to get an interactive debugging shell if you don't have to override the entrypoint to do it. The exception is if I'm building a FROM scratch
image where it's literally impossible to do anything other than run the bundled binary, but that's an exception.)
(还要注意,从Java 8的修补程序版本开始,较新的JVM可以选择遵循容器的内存限制,从而导致 JAVA_OPTS较小
设置;请参见 openjdk映像文档。)
(Also note that newer JVMs, starting with a patch release of Java 8, have options to honor the container's memory limits, which leads to a much smaller JAVA_OPTS
setting; see "Make JVM respect CPU and RAM limits" on the openjdk image documentation.)
这篇关于在运行时将带有docker-compose.yml的ENV变量传递给Dockerfile的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!