在CMD之前执行一个脚本

根据Docker文档 :Dockerfile中只能有一个CMD指令。 如果列出多个CMD,则只有最后一个CMD才会生效。

我希望执行一个简单的bash脚本(处理docker环境variables)之前的CMD命令(这是我的情况下初始化)。

有没有办法做到这一点?

使用自定义入口点

创build一个自定义的入口点,然后exec你的CMD。

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

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。

为什么这个工作

入口点和CMD的使用经常使新来的人困惑于Docker。 在评论中,你对此表示困惑。 这是如何工作,为什么。

入口点是在容器内运行的第一个东西。 它将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被replace为一个自定义脚本。 (尽pipe它最终仍然由sh运行,因为它以#!/bin/sh开头。)

入口点将CMD作为参数。 在entrypoint.sh脚本的末尾是exec "$@" 。 由于$@展开给脚本的参数列表,所以变成了

 exec "init" 

因此,当脚本完成时,它会消失,被initreplace为PID 1.(这就是exec所做的 – 它用一个不同的命令replace当前进程)。

如何包含CMD

在评论中,您问到在Dockerfile中添加CMD。 是的,你可以这么做。

Dockerfile

 CMD ["init"] 

或者如果你的命令有更多的话,比如像init -a -b这样的参数,看起来像这样:

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

丹的回答是正确的,但我觉得实施起来相当混乱。 对于那些在相同的情况下,这里是代码示例我如何实现他的解释使用入口点,而不是CMD。

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

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

这里是合并和启动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一样启动我的节点程序。