【问题标题】:Overriding inherited CMD in Dockerfile does not always work?在 Dockerfile 中覆盖继承的 CMD 并不总是有效?
【发布时间】:2017-03-25 16:22:23
【问题描述】:

在派生的 Dockerfile 中,我遇到了一些与覆盖 CMD 相关的不一致行为。

基础 Dockerfile 如下所示:

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。这看起来不像我从文档中所期望的那样。

更新 2: 使用 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

【问题讨论】:

    标签: java spring docker


    【解决方案1】:

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

    如果你想在没有 shell 的情况下运行你的,那么你必须 将命令表示为 JSON 数组并给出完整路径 可执行。这种数组形式是 CMD 的首选格式。任何 附加参数必须单独表示为字符串 数组:

    来自ubuntu

    CMD ["/usr/bin/wc","--help"]

    如果您希望您的容器每次运行相同的可执行文件 时间,那么您应该考虑将 ENTRYPOINT 与 命令。请参阅入口点。

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

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

    你应该定义一个入口点,在你的情况下:

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

    在您的具体情况下,我会主张将 shell 作为入口点并使用脚本覆盖 CMD,例如:

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

    在你的 compose YML 中:

    command: './script2.sh'
    

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

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

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

    在构建时获取值的替换。 ARG 的优点是可以在您的子图像中继承

    【讨论】:

    • 嗨 Michele,我在派生映像和基础映像中运行相同的程序,它只是应该更改的一个参数。我想避免在撰写文件的命令中重复整个连串。派生的 Dockerfile 中的 CMD 不应该做同样的事情吗?
    • @ArneMertz 是的,但您没有使用 Docker 参考中指定的 JSON 格式,只要您不使用 shell 启动它。尝试定义为默认的 ENTRYPOINT java,让 CMD 只配置参数,如:CMD ["${PROFILE}"]
    • @ArneMertz IÄve 添加了使用 JSON 方法为您的案例使用 ARG 而不是 ENV 的建议
    • 感谢您的耐心等待 :-) ARG 方法适用于 WEBAPP_CONTEXT,因为它对于我想通过 compose 文件创建的所有容器都是相同的。然而,对 spring 配置文件使用 ARG 会导致每个想要避免的容器的图像不同。
    • 然后只留下入口点,删除 CMD,当你用命令运行容器时,附加你需要的参数@ArneMertz
    【解决方案2】:

    不要只是复制和粘贴错误的 Java 命令。如果 -Dspring.profile.active 参数位于 .jar 文件之后,则无法识别它。

    将CMD行固定为

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

    一切都很好。

    另见“Setting active profile and config location from command line in spring boot

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-06-29
      • 2023-03-14
      • 2020-10-07
      • 1970-01-01
      • 1970-01-01
      • 2013-12-10
      • 1970-01-01
      相关资源
      最近更新 更多