【问题标题】:Execute a script before CMD在 CMD 之前执行一个脚本
【发布时间】:2017-08-09 00:48:03
【问题描述】:

根据Docker documentation: 一个 Dockerfile 中只能有一个 CMD 指令。如果你列出了多个 CMD,那么只有最后一个 CMD 会生效。

我希望在 CMD 命令(在我的例子中是 init)之前执行一个简单的 bash 脚本(它处理 docker 环境变量)。

有什么办法吗?

【问题讨论】:

  • 将您的 bash 脚本作为 CMD 放入 Dockerfile 并将您在 CMD 中的当前命令移动到 bash 脚本中,作为它运行的最后一个命令。
  • @axiac 我试过了..它没有帮助。 CMD 正在初始化

标签: docker environment-variables


【解决方案1】:

感谢丹的回答。

虽然我发现我必须在 Dockerfile 中做这样的事情:

WORKDIR /
COPY startup.sh /
RUN chmod 755 /startup.sh
ENTRYPOINT sh /startup.sh /usr/sbin/init

注意:我将脚本命名为 startup.sh 而不是 entrypoint.sh

这里的关键是我需要提供 'sh' 否则我会不断收到来自 'docker logs -f container_name' 的“没有这样的文件...”错误。

见: https://github.com/docker/compose/issues/3876

【讨论】:

    【解决方案2】:

    Dan 的回答是正确的,但我发现实施起来相当混乱。对于那些处于相同情况的人,这里是我如何实现他对使用 ENTRYPOINT 而不是 CMD 的解释的代码示例。

    这是我的 Dockerfile 中的最后几行:

    #change directory where the mergeandlaunch script is located.
    WORKDIR /home/connextcms
    ENTRYPOINT ["./mergeandlaunch", "node", "keystone.js"]
    

    以下是 mergeandlaunch bash shell 脚本的内容:

    #!/bin/bash
    
    #This script should be edited to execute any merge scripts needed to
    #merge plugins and theme files before starting ConnextCMS/KeystoneJS.
    
    echo Running mergeandlaunch script
    
    #Execute merge scripts. Put in path to each merge script you want to run here.
    cd ~/theme/rtb4/
    ./merge-plugin
    
    #Launch KeystoneJS and ConnextCMS
    cd ~/myCMS
    
    exec "$@"
    

    以下是代码的执行方式:

    1. ENTRYPOINT 命令启动 mergeandlaunch shell 脚本
    2. “node”和“keystone.js”这两个参数被传递给 shell 脚本。
    3. 在脚本结束时,参数被传递给exec 命令。
    4. exec 命令随后启动了我的节点程序,就像 Docker 命令 CMD 一样。

    【讨论】:

      【解决方案3】:

      使用自定义入口点

      创建一个自定义入口点来执行您想要的操作,然后在最后执行您的 CMD。

      注意:如果您的图像已经定义了一个自定义入口点,您可能需要扩展它而不是替换它,或者您可能会更改您需要的行为。

      entrypoint.sh

      #!/bin/sh
      
      ## Do whatever you need with env vars here ...
      
      # Hand off to the CMD
      exec "$@"
      

      Dockerfile

      COPY entrypoint.sh /entrypoint.sh
      RUN chmod 755 /entrypoint.sh
      
      ENTRYPOINT ["/entrypoint.sh"]
      

      Docker 将使用 CMD 作为参数运行您的入口点。如果你的 CMD 是init,那么:

      /entrypoint.sh init
      

      入口点脚本末尾的exec 负责在入口点完成所需操作时将其移交给 CMD。

      为什么会这样

      ENTRYPOINT 和 CMD 的使用经常让 Docker 新手感到困惑。在 cmets 中,您对此表示困惑。以下是它的工作原理和原因。

      ENTRYPOINT 是在容器内运行的初始内容。它将 CMD 作为参数列表。因此,在这个例子中,容器中运行的是这个参数列表:

      # ENTRYPOINT = /entrypoint.sh
      # CMD        = init
      ["/entrypoint.sh", "init"]
      
      # or shown in a simpler form:
      /entrypoint.sh init
      

      图像不需要有入口点。如果不定义,Docker 有一个默认值:/bin/sh -c

      因此,在您原来的情况下,没有 ENTRYPOINT,并且使用 CMD init,Docker 会运行这个:

      /bin/sh -c 'init'
      ^--------^  ^--^
          |         \------- CMD
          \--------------- ENTRYPOINT
      

      一开始,Docker 只提供 CMD,/bin/sh -c 被硬编码为 ENTRYPOINT(您无法更改它)。在此过程中的某个时刻,人们遇到了他们必须做更多自定义事情的用例,而 Docker 公开了 ENTRYPOINT,因此您可以将其更改为您想要的任何内容。

      在我上面展示的示例中,ENTRYPOINT 被替换为自定义脚本。 (尽管它最终仍由sh 运行,因为它以#!/bin/sh 开头。)

      ENTRYPOINT 将 CMD 作为参数。 entrypoint.sh 脚本的末尾是exec "$@"。由于$@ 扩展为提供给脚本的参数列表,因此变成了

      exec "init"
      

      因此,当脚本完成时,它会消失并被 init 替换为 PID 1。(这就是 exec 所做的 - 它用不同的命令替换当前进程.)

      如何包含 CMD

      在 cmets 中,您询问了在 Dockerfile 中添加 CMD 的问题。是的,你可以这样做。

      Dockerfile

      CMD ["init"]
      

      或者,如果您的命令还有更多内容,例如像init -a -b 这样的参数看起来像这样:

      CMD ["init", "-a", "-b"]
      

      【讨论】:

      • 谢谢! - 一个问题 - 我可以将 CMD 作为 Dockerfile 的一部分(而不是指定为参数)。比如..`ENTRYPOINT ["/entrypoint.sh"] CMD ["/usr/sbin/init"]
      • stackoverflow.com/a/34245657/124330 - 但这个答案让我感到困惑..我是 Docker 的新手。这里 ENTRYPOINT 是 ping 而 CMD 是 localhost..所以它就像 ping localhost..但在我的情况下它看起来像 /entrypoint.sh /usr/bin/init - 这对我来说没有意义 - init 怎么会是entrypoint.sh 脚本的参数?
      • @kumar 我用 ENTRYPOINT 和 CMD 的解释更新了答案。我希望现在更清楚了。
      • 感谢您的详细评论。我在试图让它工作时遇到了麻烦。运行 'docker logs -f ' 时,它说“/bin/sh: [startup.sh]: command not found。”注意:我的脚本名为 startup.sh。此外,我正在另一个容器之上构建,该容器在其 Dockerfile 中有这个:CMD [“/usr/sbin/init”]。这基本上是我的 Dockerfile 中的内容:WORKDIR / COPY startup.sh / RUN chmod 755 /startup.sh ENTRYPOINT ['/startup.sh']
      • @jerseybean 如果您有任何问题,您应该将其作为问题发布,并在该问题中包含 Dockerfile、startup.sh 的内容
      猜你喜欢
      • 1970-01-01
      • 2017-12-03
      • 1970-01-01
      • 1970-01-01
      • 2020-09-05
      • 2015-12-28
      • 2020-07-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多