在CMD之前执行脚本

时间:2017-03-17 12:42:25

标签: docker environment-variables

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

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

有没有办法做到这一点?

3 个答案:

答案 0 :(得分:33)

使用自定义入口点

创建一个自定义入口点,它可以满足您的需要,然后在最后执行您的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的新用户感到困惑。在评论中,你表达了对它的困惑。以下是它的工作原理和原因。

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

# ENTRYPOINT = /entrypoint.sh
# CMD        = init
["/entrypoint.sh", "init"]

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

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

因此,根据您的原始情况,没有ENTRYPOINT,并使用init的CMD,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.({1}}的作用是什么 - 替换使用不同命令的当前进程。)

如何包含CMD

在评论中,您询问了如何在Dockerfile中添加CMD。是的,你可以做到。

<强> Dockerfile

exec

或者如果你的命令还有更多,例如像CMD ["init"] 这样的参数看起来像这样:

init -a -b

答案 1 :(得分:4)

丹的答案是正确的,但我发现实施起来相当混乱。对于那些处于相同情况的人,下面是我如何实现他对使用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. 两个论点&#39; node&#39;和&#39; keystone.js&#39;传递给shell脚本。
  3. 在脚本结束时,参数将传递给exec命令。
  4. 然后exec命令以与Docker命令CMD相同的方式启动我的节点程序。

答案 2 :(得分:0)

感谢Dan的回答。

虽然我发现我必须在Dockerfile中执行类似的操作:

MERGE (dummy:Dummy)
WITH dummy, ID(dummy) AS dummy_id
CALL algo.pageRank.stream(
  'OPTIONAL MATCH (p:Page) WHERE p.timestamp > 1000 RETURN CASE WHEN p IS NOT NULL THEN ID(p) ELSE ' + dummy_id + ' END AS id',
  'OPTIONAL MATCH (p1:Page)-[link:LINKS]->(p2:Page) WHERE EXISTS(link.x) WITH CASE WHEN link IS NOT NULL THEN [ID(p1), ID(p2)] ELSE [' + dummy_id + ',' + dummy_id + '] END AS res RETURN res[0] AS source, res[1] as target',
  {graph:'cypher', iterations:20, dampingFactor:0.85})
YIELD node, score
WITH dummy, node, score
WHERE node <> dummy
RETURN node, score ORDER BY score DESC LIMIT 20;

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

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

请参阅: https://github.com/docker/compose/issues/3876