2021-03-07 07:29:05 +00:00
# CMD 容器启动命令
2016-11-25 18:02:43 +00:00
` CMD ` 指令的格式和 ` RUN ` 相似 , 也是两种格式 :
* ` shell ` 格式 : ` CMD <命令> `
* ` exec ` 格式 : ` CMD ["可执行文件", "参数1", "参数2"...] `
* 参数列表格式 : ` CMD ["参数1", "参数2"...] ` 。 在指定了 ` ENTRYPOINT ` 指令后 , 用 ` CMD ` 指定具体的参数 。
之前介绍容器的时候曾经说过 , Docker 不是虚拟机 , 容器就是进程 。 既然是进程 , 那么在启动容器的时候 , 需要指定所运行的程序及参数 。 ` 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 ` 格式 , 这类格式在解析时会被解析为 JSON 数组 , 因此一定要使用双引号 ` " ` , 而不要使用单引号 。
如果使用 ` shell ` 格式的话 , 实际的命令会被包装为 ` sh -c ` 的参数的形式进行执行 。 比如 :
2019-10-29 06:31:45 +00:00
` ` ` docker
2016-11-25 18:02:43 +00:00
CMD echo $ HOME
` ` `
在实际执行中 , 会将其变更为 :
2019-10-29 06:31:45 +00:00
` ` ` docker
2016-11-25 18:02:43 +00:00
CMD [ "sh" , "-c" , "echo $HOME" ]
` ` `
这就是为什么我们可以使用环境变量的原因 , 因为这些环境变量会被 shell 进行解析处理 。
提到 ` CMD ` 就不得不提容器中应用在前台执行和后台执行的问题 。 这是初学者常出现的一个混淆 。
2019-03-08 04:21:07 +00:00
Docker 不是虚拟机 , 容器中的应用都应该以前台执行 , 而不是像虚拟机 、 物理机里面那样 , 用 ` systemd ` 去启动后台服务 , 容器内没有后台服务的概念 。
2016-11-25 18:02:43 +00:00
一些初学者将 ` CMD ` 写为 :
2019-10-29 06:31:45 +00:00
` ` ` docker
2016-11-25 18:02:43 +00:00
CMD service nginx start
` ` `
然后发现容器执行后就立即退出了 。 甚至在容器内去使用 ` systemctl ` 命令结果却发现根本执行不了 。 这就是因为没有搞明白前台 、 后台的概念 , 没有区分容器和虚拟机的差异 , 依旧在以传统虚拟机的角度去理解容器 。
对于容器而言 , 其启动程序就是容器应用进程 , 容器就是为了主进程而存在的 , 主进程退出 , 容器就失去了存在的意义 , 从而退出 , 其它辅助进程不是它需要关心的东西 。
而使用 ` service nginx start ` 命令 , 则是希望 upstart 来以后台守护进程形式启动 ` nginx ` 服务 。 而刚才说了 ` CMD service nginx start ` 会被理解为 ` CMD [ "sh", "-c", "service nginx start"] ` , 因此主进程实际上是 ` sh ` 。 那么当 ` service nginx start ` 命令结束后 , ` sh ` 也就结束了 , ` sh ` 作为主进程退出了 , 自然就会令容器退出 。
正确的做法是直接执行 ` nginx ` 可执行文件 , 并且要求以前台形式运行 。 比如 :
2019-10-29 06:31:45 +00:00
` ` ` docker
2017-01-13 06:21:35 +00:00
CMD [ "nginx" , "-g" , "daemon off;" ]
2016-11-25 18:02:43 +00:00
` ` `