在Dockerfile中覆盖继承的CMD并不总是有效?

时间:2016-11-11 12:22:33

标签: java spring docker

我在派生的Dockerfile中重写CMD时会遇到一些不一致的行为。

基本D​​ockerfile看起来像这样:

FROM myVeryBaseImage

ENV WEBAPP_CONTEXT=my-app
WORKDIR /opt/${WEBAPP_CONTEXT}

COPY app/*.jar ./${WEBAPP_CONTEXT}.jar
COPY baseconfig/* ./config/${WEBAPP_CONTEXT}/

CMD java -jar ${WEBAPP_CONTEXT}.jar --Dspring.profiles.active=docker

此基本图片由其他团队提供,很难更改。我现在正在编写一堆容器,我想多次运行同一个应用程序,但配置不同。

所以我想我会扩展图像,将更多配置复制到其中并使用不同的弹簧配置文件运行它:

FROM baseImage
COPY config/application-*.properties ./config/${WEBAPP_CONTEXT}/
CMD java -jar ${WEBAPP_CONTEXT}.jar -Dspring.profiles.active=${PROFILE}

在docker-compose.yml:

myapp-foo:
 build: ./myapp-custom
 image: myapp-custom
 environment:
  PROFILE: foo
 volumes:
  - /opt/my-app/foo:/opt/my-app

myapp-bar:
 image: myapp-custom
 environment:
  PROFILE: bar
 volumes:
  - /opt/my-app/bar:/opt/my-app

我原本希望有2个容器在运行,分别使用application-foo.propertiesapplication-bar.properties

但是,似乎两者都使用appplication-docker.properties,即基本Dockerfile中定义的docker配置文件。

如果我完全更改派生的Dockerfile中的CMD,它可以工作:

CMD echo "${PROFILE}"

输出是" foo"和" bar"分别。可能会发生什么暗示?

我的版本是:

docker-compose version 1.8.1, build 878cff1
Docker version 1.12.3, build 6b644ec

更新
在@ blackibiza的建议之后,我将派生的Dockerfile更改为

FROM baseImage
COPY config/application-*.properties ./config/${WEBAPP_CONTEXT}/
ENTRYPOINT /opt/jdk1.8.0_102/bin/java
CMD ["-jar", "${WEBAPP_CONTEXT}.jar", "-Dspring.profiles.active=foo"]

没有docker-compose的东西,只是为了看看派生图像的样子。我从java获取错误消息,试图运行容器。检查图像给出以下内容:

$ docker inspect --format='{{.Config.Cmd}} {{.Config.Entrypoint}}' testapp
[-jar ${WEBAPP_CONTEXT}.jar -Dspring.profiles.active=french] [/bin/sh -c /opt/jdk1.8.0_102/bin/java]

所以它仍然试图执行/bin/sh而不是java。这看起来不像我在文档中所期望的那样。

UPDATE2: 使用CMD的JSON数组语法会触发另一个问题:

FROM baseImage
COPY config/application-*.properties ./config/${WEBAPP_CONTEXT}/
CMD ["java", "-jar", "${WEBAPP_CONTEXT}.jar", "-Dspring.profiles.active=foo"]

不会扩展${WEBAPP_CONTEXT}的使用,从而导致错误

Error: Unable to access jarfile ${WEBAPP_CONTEXT}.jar

2 个答案:

答案 0 :(得分:3)

您正在寻找的是覆盖入口点。 如上所述in the Docker reference

  

如果你想在没有外壳的情况下运行你,那么你必须   将命令表示为JSON数组并提供完整路径   可执行文件。此数组形式是CMD的首选格式。任何   其他参数必须单独表示为字符串   阵列:

     

FROM ubuntu

     

CMD [“/ usr / bin / wc”,“ - help”]

     

如果您希望容器每次都运行相同的可执行文件   时间,那么你应该考虑使用ENTRYPOINT   CMD。参见ENTRYPOINT。

使用Composer时,您可以覆盖CMD参数,例如explained here

db:
  command: '-d'
  ports:
    - 5432:5432

你应该定义一个ENTRYPOINT,在你的情况下:

ENTRYPOINT java
CMD ["-jar", "${WEBAPP_CONTEXT}.jar"]

在您的具体情况下,我将争论将shell作为入口点公开并使用脚本覆盖CMD,如:

ENTRYPOINT /bin/sh
CMD ["./script.sh"]

并在你的作品YML中:

command: './script2.sh'

更新(基于已更改的问题):

缺少的是变量的定义。在这种情况下,我建议使用 ARG 而不是 ENV 来构建传入永久值的容器:

docker build -t your_image:your_version --build-arg WEBAPP_CONTEXT=your_context .

在构建时获取值的替换。 ARG具有在子图像中继承的优势

答案 1 :(得分:3)

不要只是复制&粘贴错误的Java命令。如果-Dspring.profile.active参数位于.jar文件之后,则无法识别CMD java -jar -Dspring.profiles.active=${PROFILE} ${WEBAPP_CONTEXT}.jar 参数。

将CMD行修复为

{{1}}

一切都很好。

另请参阅“Setting active profile and config location from command line in spring boot