CMD 指令的格式和 RUN 相似,也是两种格式:
- shell 格式:CMD <命令>
- exec 格式:CMD ["可执行文件", "参数1", "参数2"...]
- 参数列表格式:CMD ["参数1", "参数2"...]。在指定了 ENTRYPOINT 指令后,用 CMD 指定具体的参数。
容器是一个进程。那么在启动容器的时候,就需要指定所运行的程序及参数。
CMD 指令是用于指定默认的容器主进程的启动命令的,CMD 类属于 RUN 命令,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 的参数的形式进行执行。比如:
CMD echo $HOME
在实际执行中,会将其变更为:
CMD [ "sh", "-c", "echo $HOME" ]
这就是为什么我们可以使用环境变量的原因,因为这些环境变量会被 shell 进行解析处理。
提到 CMD 就不得不提容器中应用在前台执行和后台执行的问题。这是初学者常出现的一个混淆。
Docker 不像虚拟机,在容器中的应用都应该以前台执行,不像虚拟机那样,用 systemd 去启动后台服务,容器内没有后台服务饿概念。
如果 CMD 指令写成:
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 可执行文件,并且要求以前台形式运行。比如:
CMD ["nginx", "-g", "daemon off;"]
该CMD 指令只能运行一个,如果存在多个,直留存一个,最后一条 CMD 是生效的。
编写Dockerfile 文件
[root@ip-10-1-0-142 mynginx]# cat Dockerfile
FROM nginx:latest
COPY hello.txt home/
COPY html/index.html /usr/share/nginx/
CMD ["nginx","-g","daemon off;"]
构建镜像的步骤
[root@ip-10-1-0-142 mynginx]# docker build -f Dockerfile -t nginx:v2 app
Sending build context to Docker daemon 3.631kB
Step 1/4 : FROM nginx:latest
---> 08b152afcfae
Step 2/4 : COPY hello.txt home/
---> f471772839c9
Step 3/4 : COPY html/index.html /usr/share/nginx/
---> c6c9239fc8a9
Step 4/4 : CMD ["nginx","-g","daemon off;"]
---> Running in ef82c58b610d
Removing intermediate container ef82c58b610d
---> 808dddfc65d6
Successfully built 808dddfc65d6
Successfully tagged nginx:v2