在kubernetes的Pod启动时,会有一个叫做pause的容器先启动,然后才会轮到有真正服务的pod容器启动。这个pod的作用是什么呢?
pause容器的Dockerfile的内容很少
1 | FROM scratch |
可以看到,这里就启动了一个pause程序,再来看下pause程序
https://github.com/kubernetes/kubernetes/blob/master/build/pause/pause.c
只看main函数里的几行代码
1 | if (sigaction(SIGINT, &(struct sigaction){.sa_handler = sigdown}, NULL) < 0) |
sigaction函数指定本程序在接收到SIGINT, SIGTERM, SIGCHLD信号之后做什么处理。
设置完后就会通过pause()暂停本进程,一直等待信号,而信号到来后仍然继续循环等待,所以该进程只能通过接收信号后,从信号处理函数里退出了。
信号处理函数就sigdown和sigreap两个函数。
1 | static void sigdown(int signo) { |
结合上面的代码可以看到,进程终止的信号SIGINT, SIGTERM,就是调用psignal输出了信号信息,然后就退出了。集合main函数里的逻辑,pause进程只能从这两个信号退出。
SIGCHLD信号的处理比较特殊。
1 | while (waitpid(-1, NULL, WNOHANG) > 0) |
这段代码,一般用父进程有多个子进程时使用,让父进程等待子进程结束。这样做可以防止产生僵尸进程。
1 | 进程可以使用fork和exec syscalls启动其他进程。当启动了其他进程,新进程的父进程就是调用fork syscall的进程。fork用于启动正在运行的进程的另一个副本,而exec则用于启动不同的进程。每个进程在OS进程表中都有一个条目。这将记录有关进程的状态和退出代码。当子进程运行完成,它的进程表条目仍然将保留直到父进程使用wait syscall检索其退出代码将其退出。这被称为“收割”僵尸进程。 |
总结下,pause进程启动后,也就干了两件事
- 重复循环等待信号,只有SIGTERM和SIGINT时才会退出进程
- 负责回收僵尸子进程
在Kubernetes中,pause容器的功能会有所扩展
- pause容器在创建pod时第一个启动,创建的命名空间作为基础和pod中的其他容器共享
- pause容器在PID namespace中是PID为1的init进程,也是Pod中其他容器的父进程,负责回收pod内的僵尸子进程
如果不使用pause容器呢,一个是命名空间并不好统一管理,一个是容器将会只能自己收集僵尸进程本身,这对内存会是一个隐患。
参考文章:
https://kubernetes.io/docs/concepts/workloads/pods/pod/
http://dockone.io/article/2785
http://dockone.io/article/2682