关于docker:在CMD之前执行脚本

Execute a script before CMD

搬运工:AS发送文档有一个CMD指令只能在一dockerfile。如果你只有一个列表比颞下颌关节会带负载效应。

我想执行一个简单的bash脚本(这过程码头环境变量在命令(CMD)是初始化的案例)。

有没有任何方式这样做?


使用自定义入口点

创建一个自定义的入口点,它可以满足您的需要,然后在末尾执行命令。

NOTE: if your image already defines a custom entrypoint, you may need to extend it rather than replace it, or you may change behavior you need.

入口:

1
2
3
4
5
6
#!/bin/sh

## Do whatever you need with env vars here ...

# Hand off to the CMD
exec"$@"

Dockerfile:

1
2
3
4
COPY entrypoint.sh /entrypoint.sh
RUN chmod 755 /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]

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

1
/entrypoint.sh init

entrypoint脚本末尾的exec负责在entrypoint完成所需操作后将其移交给cmd。

为什么会这样

entrypoint和cmd的使用经常使Docker的新手感到困惑。在评论中,你对此表示困惑。这是它的工作原理和原因。

入口点是容器中运行的初始内容。它将cmd作为参数列表。因此,在本例中,在容器中运行的是以下参数列表:

1
2
3
4
5
6
# ENTRYPOINT = /entrypoint.sh
# CMD        = init
["/entrypoint.sh","init"]

# or shown in a simpler form:
/entrypoint.sh init

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

因此,在您最初的情况下,没有入口点,并且使用init的命令,docker将运行以下命令:

1
2
3
4
/bin/sh -c 'init'
^--------^  ^--^
    |         \------- CMD
    \--------------- ENTRYPOINT

起初,docker只提供了cmd,/bin/sh -c被硬编码为入口点(不能更改)。在这个过程中的某个时刻,人们有一些用例需要做更多的定制工作,Docker暴露了入口点,这样您就可以将其更改为您想要的任何内容。

在我上面展示的示例中,入口点被一个自定义脚本替换。(虽然它最终仍由sh运行,因为它从#!/bin/sh开始。)

该入口点将cmd作为is参数。在entrypoint.sh脚本的末尾是exec"$@"。由于$@扩展到脚本的参数列表中,这就变成了

1
exec"init"

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

如何包含命令

在注释中,您询问了如何在dockerfile中添加cmd。是的,你能做到。

Dockerfile:

1
CMD ["init"]

或者,如果您有更多的命令,例如,像init -a -b这样的参数,则如下所示:

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


丹的回答是正确的,但我发现执行起来相当混乱。对于相同情况下的那些,这里有一些代码示例,说明我是如何实现他对使用entrypoint而不是cmd的解释的。

下面是我的dockerfile中的最后几行:

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/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"$@"

以下是代码的执行方式:

  • entrypoint命令启动mergeandlaunchshell脚本
  • "node"和"keystone.js"这两个参数将传递给shell脚本。
  • 在脚本末尾,参数被传递到exec命令。
  • 然后,exec命令启动了我的节点程序,就像docker命令cmd一样。

  • 感谢丹的回答。

    虽然我发现我必须在dockerfile里做这样的事情:

    1
    2
    3
    4
    WORKDIR /
    COPY startup.sh /
    RUN chmod 755 /startup.sh
    ENTRYPOINT sh /startup.sh /usr/sbin/init

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

    这里的关键是我需要提供"sh",否则我会不断得到"no such file…"错误从"docker logs-f container_name"中出现。

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