CMD

指令

EXEC格式:CMD ["executable","param1","param2"] 
SHELL格式:CMD command param1 param2 
做为ENTRYPOINT参数:CMD ["param1","param2"] 

描述

CMD 提供容器运行时的默认主进程命令及参数。

一个Dockerfile中只能有一行 CMD 有效,如果定义了多行 CMD,则只有最后一行生效。

该指令不是在镜像 docker build 构建时执行,而是在镜像构建完成后 docker run 运行容器时默认执行的指令。

如果在 docker run 时指定了启动命令时,那么该 Dockerfile 中定义的 CMD 指令将会被覆盖不会执行。

比如,ubuntu 镜像默认的 CMD 是 /bin/bash,如果直接 docker run -it ubuntu 的话,会直接进入 bash。也可以在运行时指定其他的运行命令,如 docker run -it ubuntu cat /etc/os-release。这就是用 cat /etc/os-release 命令替换了默认的 /bin/bash 命令,输出了系统版本信息。

EXEC格式 和 SHELL格式 的差异

EXEC格式 和 SHELL格式 是两种不同的进程运行方式。使用 SHELL 格式 时是Docker调用 SHELL 来执行命令,而 EXEC 格式 是由Docker直接进行系统调用的方式执行命令。所以在使用 EXEC格式 执行时是无法获取SHELL的信息的,例如:

CMD ["echo", "$HOME"]

这是无法获取 $HOME 的变量内容的,因为该变量是SHELL中的环境变量,系统调用时是没有该环境变量信息的。使用EXEC格式时获取SHELL信息可以使用如下方法:

CMD["sh", "-c", "echo", "$HOME"]

这时docker会调用 sh 来执行命令,就可以获取到 $HOME 的内容了

如果希望容器每次都运行相同的可执行文件,则应考虑将 ENTRYPOINT 结合使用 CMD,使用 CMD 来指定运行参数,而实际执行的命令由 ENTRYPOINT 来指定。

当 Dockerfile 中指定了 ENTRYPOINT 时,CMD 将作为该指令的参数。这时 CMD 和 ENTRYPOINT 指令都需要使用 EXEC参数 的形式,他将会被解析为 JSON 数组的格式。需要使用双引号 括起来而不能单引号

容器中应用在前台执行和后台执行的问题

Docker 不是虚拟机,容器中的应用都应该以前台执行,而不是像虚拟机、物理机里面那样,用 systemd 去启动后台服务,容器内不建议使用后台服务。

错误的CMD使用:

CMD service nginx start

在运行容器时会发现容器执行后就立即退出了。甚至在容器内去使用 systemctl 命令结果却发现根本执行不了。这就是因为没有搞明白前台、后台的概念,没有区分容器和虚拟机的差异,依旧在以传统虚拟机的角度去理解容器。

对于容器而言,其启动的程序就是容器应用进程,容器就是为了主进程而存在的,主进程退出,容器就失去了存在的意义,从而退出,其它辅助进程不是它需要关心的东西。

而使用 service nginx start 命令,则是希望以后台守护进程形式启动 nginx 服务。 CMD service nginx start 会被理解为 CMD [ "sh", "-c", "service nginx start"],因此主进程实际上是 sh。

那么当 service nginx start 命令结束后,sh 也就结束了,sh 作为主进程退出了,自然就会令容器退出。

正确的做法是直接执行 nginx 可执行文件,并且要求以前台形式运行。比如:

CMD ["nginx", "-g", "daemon off;"]