大家在使用 Docker 容器或者 Kubernetes 时, 遇到过这个容器么?gcr.io/google_containers/pause-amd64
docker ps 的命令返回的结果:
[root@k8s-minion1 kubernetes]# docker ps |grep pause
c3026adee957 gcr.io/google_containers/pause-amd64:3.0 “/pause” 22 minutes ago Up 22 minutes k8s_POD.d8dbe16c_redis-master-343230949-04glm_default_ce3f60a9-095d-11e7-914b-0a77ecd65f3e_66c108d5
202df18d636e gcr.io/google_containers/pause-amd64:3.0 “/pause” 24 hours ago Up 24 hours k8s_POD.d8dbe16c_kube-proxy-js0z0_kube-system_2866cfc2-0891-11e7-914b-0a77ecd65f3e_c8e1a667
072d3414d33a gcr.io/google_containers/pause-amd64:3.0 “/pause” 24 hours ago Up 24 hours k8s_POD.d8dbe16c_kube-flannel-ds-tsps5_default_2866e3fb-0891-11e7-914b-0a77ecd65f3e_be4b719e
[root@k8s-minion1 kubernetes]#
Kubernetes 的官网解释:
it’s part of the infrastructure. This container is started first in all Pods to setup the network for the Pod.
意思是:pause-amd64 是 Kubernetes 基础设施的一部分,Kubernetes 管理的所有 pod 里,pause-amd64 容器是第一个启动的,用于实现 Kubernetes 集群里 pod 之间的网络通讯。
对这个特殊容器感兴趣的朋友,可以阅读其源代码:https://github.com/kubernetes…
我们查看这个 pause-amd64 镜像的 dockerfile,发现实现很简单,基于一个空白镜像开始:
FROM scratch
ARG ARCH
ADD bin/pause-${ARCH} /pause
ENTRYPOINT [“/pause”]
ARG 指令用于指定在执行 docker build 命令时传递进去的参数。
这个 pause container 是用 C 语言写的:
https://www.ianlewis.org/en/a…
在运行的 Kubernetes node 上运行 docker ps,能发现这些 pause container:
pause container 作为 pod 里其他所有 container 的 parent container,主要有两个职责:
是 pod 里其他容器共享 Linux namespace 的基础
扮演 PID 1 的角色,负责处理僵尸进程
这两点我会逐一细说。在 Linux 里,当父进程 fork 一个新进程时,子进程会从父进程继承 namespace。目前 Linux 实现了六种类型的 namespace,每一个 namespace 是包装了一些全局系统资源的抽象集合,这一抽象集合使得在进程的命名空间中可以看到全局系统资源。命名空间的一个总体目标是支持轻量级虚拟化工具 container 的实现,container 机制本身对外提供一组进程,这组进程自己会认为它们就是系统唯一存在的进程。
在 Linux 里,父进程 fork 的子进程会继承父进程的命名空间。与这种行为相反的一个系统命令就是 unshare:
再来聊聊 pause 容器如何处理僵尸进程的。
Pause 容器内其实就运行了一个非常简单的进程,其逻辑可以从前面提到的 Pause github 仓库上找到:
static void sigdown(int signo) {
psignal(signo, “Shutting down, got signal”);
exit(0);
}
static void sigreap(int signo) {
while (waitpid(-1, NULL, WNOHANG) > 0);
}
int main() {
if (getpid() != 1)
/* Not an error because pause sees use outside of infra containers. */
fprintf(stderr, “Warning: pause should be the first process\n”);
if (sigaction(SIGINT, &(struct sigaction){.sa_handler = sigdown}, NULL) < 0)
return 1;
if (sigaction(SIGTERM, &(struct sigaction){.sa_handler = sigdown}, NULL) < 0)
return 2;
if (sigaction(SIGCHLD, &(struct sigaction){.sa_handler = sigreap,
.sa_flags = SA_NOCLDSTOP},
NULL) < 0)
return 3;
for (;;)
pause();
fprintf(stderr, “Error: infinite loop terminated\n”);
return 42;
}
这个 c 语言实现的进程,核心代码就 28 行:
其中第 24 行里一个无限循环 for(;;), 至此大家能看出来 pause 容器名称的由来了吧?
这个无限循环里执行的是一个系统调用 pause,
因此 pause 容器大部分时间都在沉睡,等待有信号将其唤醒。
接收什么信号呢?
一旦收到 SIGCHLD 信号,pause 进程就执行注册的 sigreap 函数。
看下 SIGCHLD 信号的帮助:
SIGCHLD,在一个进程正常终止或者停止时,将 SIGCHLD 信号发送给其父进程,按系统默认将忽略此信号,如果父进程希望被告知其子系统的这种状态,则应捕捉此信号。
pause 进程注册的信号处理函数 sigreap 里,调用另一个系统调用 waitpid 来获得子进程终止的原因。
希望这篇文章对大家理解 Kubernetes 里的 pause 容器有所帮助。感谢阅读。
要获取更多 Jerry 的原创文章,请关注公众号 ” 汪子熙 ”: