docker 容器默认会把容器内部第一个进程,也就是 pid=1
的程序作为docker容器是否正在运行的依据,如果docker容器中 pid = 1 的进程挂了,那么docker容器便会直接退出,也就是说Docker容器中必须有一个前台进程,否则认为容器已经挂掉。
为了验证这一点,我写了两个go程序并静态编译为可执行程序,common 和 daemon ,他们即可前台运行也可后台运行
前台运行
./common
./daemon
后台运行
./common serve -d
./daemon -d
我们通过这两个程序来构建镜像,看他们是如何运行的。
Dockerfile 文件内容:
FROM alpine
ADD daemon /data/www/
ADD common /data/www/
WORKDIR /data/www
CMD cd /data/www/ && ./daemon -d && ./common
执行命令
docker build -t common .
docker run --name common -d common
docker exec -it common sh
ps -ef
PID USER TIME COMMAND
1 root 0:00 ./common
9 root 0:00 ./daemon
16 root 0:00 sh
26 root 0:00 ps -ef
exit
docker stop common
docker rm common
docker rmi common
修改 Dockerfile 文件的 CMD 指令,重新构建,并运行
CMD cd /data/www/ && ./common -d && ./daemon
ps -ef
PID USER TIME COMMAND
1 root 0:00 ./daemon
9 root 0:00 ./common
16 root 0:00 sh
21 root 0:00 ps -ef
再次修改 Dockerfile 文件的 CMD 指令,重新构建,并运行
CMD cd /data/www/ && ./common && ./daemon
ps -ef
PID USER TIME COMMAND
1 root 0:00 ./common && ./daemon
16 root 0:00 sh
20 root 0:00 ps -ef
由此可见docker容器运行后,会将CMD中的第一个前台进程作为pid=1的进程
,而不管在它之前是否还有别的进程。
第一个前台进程后面无法再执行别的进程,因为前台进程会阻塞,所有后面的命令就成为了此进程的参数了。
所以,如果想在一个容器里面启动多个后台进程加一个前台进程
,那么这个前台进程要写在最后面。
这也是为什么,Docker容器启动web服务时,都指定了前台运行的参数。
例如apache:
ENTRYPOINT [ "/usr/sbin/apache2" ]
CMD ["-D", "FOREGROUND"]
又例如nginx:
ENTRYPOINT [ "/usr/sbin/nginx", "-g", "daemon off;" ]
进入nginx容器,查看进程
ps -ef |grep nginx
root 1 0 0 Oct16 ? 00:00:00 nginx: master process nginx -g daemon off;
nginx 19 1 0 Oct16 ? 00:00:00 nginx: worker process
nginx 20 1 0 Oct16 ? 00:00:00 nginx: worker process
nginx 21 1 0 Oct16 ? 00:00:00 nginx: worker process
nginx 22 1 0 Oct16 ? 00:00:00 nginx: worker process
nginx 23 1 0 Oct16 ? 00:00:00 nginx: worker process
nginx 24 1 0 Oct16 ? 00:00:00 nginx: worker process
nginx 25 1 0 Oct16 ? 00:00:00 nginx: worker process
nginx 26 1 0 Oct16 ? 00:00:00 nginx: worker process
nginx 27 1 0 Oct16 ? 00:00:00 nginx: worker process
nginx 28 1 0 Oct16 ? 00:00:00 nginx: worker process
从这个意义上来讲,一个程序从宿主机迁到docker容器,程序是需要做一些调整的。
或者启动额外的进程来充当pid=1的前台进程,例如上面的 daemon 进程。
当然 linux 系统下也提供了一些阻塞的指令,比如:
tali -f xxx
或者
top
但是不管怎么样,pid=1的进程必须存在。