当我尝试通过Dockerfile中的CMD指令在运行时执行两个命令时,不知道发生了什么。我认为这应该起作用:

CMD ["/etc/init.d/nullmailer", "start", ";", "/usr/sbin/php5-fpm"]


但是它不起作用。容器尚未启动。所以我必须这样做:

CMD ["sh", "-c", "/etc/init.d/nullmailer start ; /usr/sbin/php5-fpm"]


我不明白。这是为什么?为什么第一行不是正确的方法?有人可以向我解释这些“ CMD Shell格式与JSON格式等”的东西。简单地说。

需要注意的是-与预期的command:中的docker-compose.yml指令相同。

#1 楼

我相信差异可能是因为第二个命令执行shell处理而第一个命令不执行。根据官方文档,有execshell表格。您的第一个命令是exec表单。 exec窗体不会展开环境变量,而shell窗体则会展开环境变量。通过使用exec形式,该命令可能由于其对shell处理的依赖性而失败。您可以通过运行docker logs CONTAINERID
来检查此问题,您的第二个命令,即shell形式,等效于-
CMD /etc/init.d/nullmailer start ; /usr/sbin/php5-fpm
文档摘录-

注意:与shell不同窗体,exec窗体不调用命令外壳程序。这意味着不会进行常规的外壳处理。例如,CMD [ "echo", "$HOME" ]将不会对$HOME进行变量替换。如果要进行外壳处理,则可以使用外壳形式或直接执行外壳,例如:CMD [ "sh", "-c", "echo", "$HOME" ]


评论


由于环境变量,命令可能失败。我还是应该使用此exec表单,因为它是首选表单?为什么它是首选?还是应该使用更简单的外壳形式?

– Vladan
15年4月27日在10:46

之所以失败,是因为执行一个命令是一个外壳函数。环境变量是一个红色鲱鱼。

–布莱恩(Bryan)
15年4月27日在12:02

如果您在Docker中运行多个服务,我建议您使用类似Supervisor的流程管理器。这样,您仅启动CMD部分下的supervisor,它将负责启动服务。您可以在此处检查详细信息-docs.docker.com/articles/using_supervisord

–丹尼尔(Daniel t。)
15年4月27日在14:36

这是我刚读的一篇文章:)谢谢。

– Vladan
15年4月29日在17:24

我仍然不明白为什么您需要执行CMD [“ sh”,“-c”,“ echo”,“ $ HOME”]。为什么不使用CMD [“ sh”,“-c”,“ echo $ HOME”],或者为什么不使用CMD [“ sh -c echo $ HOME”]?

–sixty4bit
18年1月6日,0:41

#2 楼

不要让自己难受。只需创建一个bash文件“ start.sh”:
#!/bin/bash

/usr/bin/command2 param1
/usr/bin/command1

在Dockerfile中执行:
ADD start.sh /
RUN chmod +x /start.sh

CMD ["/start.sh"]


评论


虽然我完全同意KISS,但不幸的是,这可能会影响旧版本docker的性能和/或不必要地增加构建映像的大小。这是因为您要使用ADD和RUN创建两个图层,请查看最佳做法,并尽量减少图层数。

–luukvhoudt
20年6月28日在13:01

#3 楼

CMD(以及RUNENTRYPOINT)的json语法将参数作为exec syscall直接传递给内核。在exec syscall中,命令与参数之间没有空格,引号转义,IO重定向,变量替换,命令之间的管道传递,运行多个命令等之间的分隔。系统调用仅使可执行文件运行,并将参数列表传递给该可执行文件,然后运行它。

诸如$之类的字符用于扩展变量,;之类用于分隔命令,(空格)用于分隔参数。 ,&&||(用于链接命令),>(用于输出重定向),|(用于在命令之间传递管道等)都是Shell的功能,需要像/bin/sh/bin/bash这样的东西来解释和实现。


如果切换到CMD的字符串语法,则docker将使用shell运行命令:

CMD /etc/init.d/nullmailer start ; /usr/sbin/php5-fpm


否则,您的第二种语法将执行完全相同的操作:

CMD ["sh", "-c", "/etc/init.d/nullmailer start ; /usr/sbin/php5-fpm"]



请注意,我不建议这样在容器内运行多个命令,因为如果您的第一个命令失败(特别是它失败),则不会进行错误处理在后台运行。您还将在容器内留下一个以pid 1运行的外壳,这将中断信号处理,从而导致10秒的延迟,并被docker恶意破坏您的容器。可以使用shell exec命令减轻信号处理:

CMD /etc/init.d/nullmailer start ; exec /usr/sbin/php5-fpm


但是,在后台静默处理失败的进程需要您切换到某种多进程管理器例如有监督者,或者最好将您的应用程序分解为多个容器,然后使用诸如docker-compose之类的东西进行部署。

#4 楼

我猜第一个命令失败了,因为在DOCKER CMD格式中,仅第一个参数被执行,其余的则馈入该命令。

第二个格式有效,因为所有命令都用“;”分隔被送入sh命令,然后执行它们。

#5 楼

我不认为您应该在“开始”后加上半逗号

,而不要使用

CMD ["/etc/init.d/nullmailer", "start", ";", "/usr/sbin/php5-fpm"]


尝试

CMD ["/etc/init.d/nullmailer", "start", "/usr/sbin/php5-fpm"]


,因为docker使用“ sh -c”,上述命令将如下执行

/etc/init.d/nullmailer start
/etc/init.d/nullmailer /usr/sbin/php5-fpm


评论


json语法不使用shell运行命令,在这种情况下没有sh -c。

–BMitch
19年8月7日在14:39