前台运行
我们在打镜像时经常会遇到这样的场景,打好的镜像跑起来之后马上就退出了。
在容器中,如果pid=1
的进程退出了,那么容器就会退出。所以要保证容器持久运行,就要保证pid=1
的进程能够持久运行
接下来,我们举几个例子,来看一下如何保证pid=1
的进程不退出。我们以docker官网的镜像nginx:1.11.5
作为基础镜像,该镜像中nginx -g 'daemon off;'
命令是一个持久任务,不会运行一下该程序就结束了;如果在shell下执行该命令,该命令不会退出,会一直占据终端。(像 echo "hello world"
这样的命令就是一个瞬时任务,执行一下就结束了)
情景一
我们把nginx -g 'daemon off;'
直接作为镜像的CMD,并且为executable模式
打成镜像并启动容器,去到容器里查看进程信息如下:
可以看出,pid=1
的进程就是nginx,而nginx是一个持久任务,所以该容器可以持久运行
情景二
我们把nginx -g 'daemon off;'
直接作为镜像的CMD,但为shell模式
打成镜像并启动容器,去到容器里查看进程信息如下:
该容器中pid=1
的进程为shell,该进程的启动命令为/bin/sh -c nginx -g 'daemon off'
。该容器也能持久运行,因为pid=1
的shell进程不会退出。
为什么该shell进程不会退出?因为shell(pid=1
)进程在启动nginx(pid=5
)这个子进程后会阻塞,直到nginx子进程退出返回,shell父进程才会继续往前走。而nginx是一个循环任务,它不会返回,所以shell进程会一直阻塞,不会结束。既然pid=1
的进程一直都在,那么容器也不会退出。
情景三
我们把nginx的启动命令放到一个shell脚本里,容器的CMD设置为该脚本
entrypoint.sh
的内容如下:
打成镜像并启动容器,去到容器里查看进程信息如下:
可以看出,pid=1
的进程为bash进程,启动命令为/bin/bash /entrypoint.sh
。该容器也能持久运行,原因与情景二是一样的。
细心的你可能会发现,在entrypoint.sh
中有两个echo语句,如果我们去查看/hello.log
文件的内容,会发现,第二个echo语句并没有执行。这是为什么呢?因为第二个echo语句会在上一条语句nginx -g 'daemon off;'
执行结束后才会执行,而nginx进程永远不会返回结束,所以后面的echo就不会执行到。
情景四
在情景二与情景三中,我们的容器都可以持久运行,但是我们的目标进程nginx的pid却不为1,那么有没有办法使nginx进程的pid变成1呢?答案是有,只需要在shell中在nginx的命令前加上exec
关键子即可
entrypoint.sh
的内容如下:
打成镜像并启动容器,去到容器里查看进程信息如下:
可以看出,此时pid=1
的进程变成了nginx,而不是shell。entrypoint.sh
中的第二个echo语句还是同样不会执行。
在这里我们可能会问,既然在情景二与三中容器都能持久运行了,nginx进程的pid是否为1重要么?答案是重要。我们知道,我们可以为容器设置资源限制,如果容器中的nginx进程超出限制而被杀掉,而此时nginx进程的pid不为1且pid为1的进程因为其他的原因而没有退出,那么该容器虽然在跑,但已经不能提供nginx服务了,造成容器在,服务不在的问题。请阅读情景五的例子
情景五
接下来,我们来做个实验:nginx的pid不为1,nginx进程挂掉后,容器还能跑
Dockerfile如下:
entrypoint.sh的内容如下:
打成镜像并启动容器,去到容器里查看进程信息如下:
我们发现,bash进程的pid为1,nginx进程的pid为5,而tail -f /dev/null
还没有执行到,所以看不到该进程的信息
接下来,我们在该容器中执行命令 kill 5
来杀掉pid为5的nginx进程,然后再查看容器内的进程信息,如下:
可以看到,nginx进程杀掉后,bash进程(pid=1
)收到了nginx进程(pid=5
)的返回结果,bash进程继续往后执行tail -f /dev/null
命令,该命令起的进程(pid=16
)也是一个持久任务,于是再一次让bash进程(pid=1)发生阻塞,所以容器并不会退出。
接下来,我们在该容器中执行命令 kill 16
来杀掉pid为16的tail进程,当执行完这条命令后,实验证明容器就退出了。这是因为bash进程收到pid为16的tail进程的返回结果后,继续往前走,由于所有的命令已执行完,bash进程退出,pid为1的bash进程退出后,容器也就退出了。
情景六
在物理机或虚拟机中,我们启动nginx进程一般会加上&
符号使进程在后台运行。接下来,我们看一下在entrypoint.sh中为nginx加上后台运行符号会是什么效果。
entrypoint.sh的内容如下:
实验证明,容器很快就退出了。为什么会这样呢,这是因为加上后台运行符号&
后,bash进程不需要等待nginx进程的返回就可以继续往下走,所以bash进程很快就运行结束,然后就退出了。
最佳实践
一个容器尽量只跑一个业务进程
让业务进程的pid保持为1:如果业务进程可以在CMD中启动,则使用executable模式;如果必须放在shell脚本中启动,则在业务进程的启动命令前加上
exec
关键字使其pid保持为1
Last updated
Was this helpful?