02 | 理解进程(1):为什么我在容器中不能kill 1号进程?
该思维导图由 AI 生成,仅供参考
问题再现
- 深入了解
- 翻译
- 解释
- 总结
在容器中无法通过kill 1号进程来重启容器的问题是一个常见的挑战,本文深入探讨了其中的技术原理。作者通过描述一个同学在Kubernetes中遇到的问题引出了这个话题,详细解释了init进程的概念和作用,以及Linux系统中如何启动1号进程。通过实际操作演示了无论是使用kill 1还是kill -9 1,都无法终止进程的情况。文章通过具体案例和技术原理,解释了为什么在容器中无法kill 1号进程的问题,帮助读者更好地理解容器中init进程的特殊之处。这篇文章适合技术人员了解容器中init进程的特殊情况,同时也提出了思考题,引发读者对于1号进程的更深层思考。通过深入浅出的方式,本文帮助读者快速了解容器中init进程的技术特点,为解决类似问题提供了有益的参考。
《容器实战高手课》,新⼈⾸单¥59
全部留言(62)
- 最新
- 精选
- 赵守忠[开心每一天]kill 1 分两种情况,如果 1 号进程没有注册 SIGTERM 的 handler,那么对 SIGTERM 信号也不响应,如果注册了 handler,那么就可以响应 SIGTERM 信号。 ---在k8s的容器环境内测试,基于tini。和老师讲的有些出入: bash-5.0# ps -ef PID USER TIME COMMAND 1 root 0:00 /tini -- /bin/sh -c java -javaagent:/opt/jmx/jmx.jar=7080:config.yaml $JAVA_OPTS -jar /app.jar bash-5.0# cat /proc/1/status|grep SigCgt SigCgt: 0000000000000000 bash-5.0# kill 1 bash-5.0# command terminated with non-zero exit code: Error executing in Docker Container: 137 实际情况是容器重启了。响应了kill 1操作。
作者回复: @赵守忠[开心每一天] 非常好的发现啊! tini的确没有注册SIGTERM,它的退出并不是因为SIGTERM的信号让它退出的,而是它发现它子进程都退出之后,主动退出的,这样容器也退出了。 可以看一下tini的源代码,它把所有接收到的信号(除了SIGHILD)都转发给了子进程,也包括了SIGTERM, 那么子进程收到SIGTERM就退出了,而tini自己可以收到SIGHILD, 然后tini自己退出,并且容器退出。 tini 的源代码和主循环: https://github.com/krallin/tini/blob/master/src/tini.c while (1) { /* Wait for one signal, and forward it */ if (wait_and_forward_signal(&parent_sigset, child_pid)) { return 1; } /* Now, reap zombies */ if (reap_zombies(child_pid, &child_exitcode)) { return 1; } if (child_exitcode != -1) { PRINT_TRACE("Exiting: child has exited"); return child_exitcode; } }
2020-11-21352 - Helios有一种老师说了一大圈,但是没有说容器的本质就是宿主机上的一个进程这个本质。
作者回复: @Helios, 很好的一个问题。 很多介绍容器的文章可能都会强调容器是进程,不过它们讨论的背景应该是和虚拟机做比较之后这么说的,因为在容器之前虚拟机是云平台上最流行的技术。强调容器是进程的目的是区分容器与虚拟机的差别,但是我不认为这个是容器的本质。 其实无论是namespace (pid namespace)还是cgroups都是在管理进程, 容器中运行是进程,这个是个明显的特征了,但不是本质。 我们如果换一个角度去思考,如果容器流行先于虚拟机技术, 我们是否还会强调容器是进程了呢?
2020-11-19429 - daydreamer思考题: kill <pid> 不可以杀掉容器init进程 kill -9 <pid> 可以 不同点在于SIGTERM不是内核信号,所以!(force && sig_kernel_only(sig)为True,加上前面两个if也为true,所以忽略;SIGKILL是内核信号 !(force && sig_kernel_only(sig)为False,信号没有办法忽略,所以被杀掉
作者回复: @daydreamer 赞!
2020-11-28415 - 上邪忘川从网上找到了不错的关于SigCgt 掩码位的解释,不懂的可以看一下,豁然开朗。https://qastack.cn/unix/85364/how-can-i-check-what-signals-a-process-is-listening-to
作者回复: 谢谢 @上邪忘川, 很好的补充材料!
2020-11-2113 - 维c查了一下资料,貌似sig_kernel_only函数是用了判断信号是不是kill或者stop的,是这两个信号才会返回true,这就意味着force不为0,同时信号是kill或者stop的时候信号是不会被忽略的,这也就解释了为什么宿主机是可以通过kill信号来杀掉容器里的进程,而sigterm由于force的值可能会被忽略,那么force的值又是又什么决定的呢?
作者回复: @维c 很好的分析!force 由发送信号的进程和接受信号进程是否在同一个namespace里决定。你可以再看一下代码。
2020-12-03210 - 莫名man pid_namespace 提到了老师在文中强调的两个细节。 ----------------------------- Only signals for which the "init" process has established a signal handler can be sent to the "init" process by other members of the PID namespace. This restriction applies even to privileged processes, and prevents other members of the PID namespace from accidentally killing the "init" process. Likewise, a process in an ancestor namespace can—subject to the usual permission checks described in kill(2)—send signals to the "init" process of a child PID namespace only if the "init" process has established a handler for that signal. SIGKILL or SIGSTOP are treated exceptionally: these signals are forcibly delivered when sent from an
作者回复: 谢谢 @莫名! 仔细读文档也是很有用的!
2020-11-1910 - 良凯尔虽然在容器内kill 1号进程行不通,但是我可以在宿主上kill容器的1号进程来达到重启容器的目的,是这样吗?
作者回复: 在宿主机上kill容器的1号进程是可以的。不过,有时候容器的用户没有宿主机登陆的权限。
2020-11-2038 - ActionHost Namespace 向c程序init进程,发送SIGTERM,忽略了,发送SIGKILL杀掉了,是特权信号就给杀掉了对吧? 还有一块不明白,在handler == SIG_DFL这里,SIGTERM,它是可以被捕获的。也就是说如果用户不注册handler,那么这个条件对 SIGTERM 也是满足的,为什么呢?
作者回复: 至于为什么即使在宿主机机上向容器1号进程发送SIGTERM,在1号进程没有注册handler的情况下,不能被杀死的问题 (思考题), 原因是这样的: 开始要看内核里的那段代码,“ !(force && sig_kernel_only(sig))”, 虽然由不同的namespace发送信号, 虽然force是1了,但是sig_kernel_only(sig)对于SIGTERM来说还是0, 这里是个&&, 那么 !(1 && 0) = 1。 #define sig_kernel_only(sig) siginmask(sig, SIG_KERNEL_ONLY_MASK) #define SIG_KERNEL_ONLY_MASK (\ rt_sigmask(SIGKILL) | rt_sigmask(SIGSTOP))
2021-01-056 - Helios不知道什么要在容器内执行,直接在去宿主机上docker kill不行么。或者直接edit一下编排文件加个环境变量啥的,不就能触发原地升级么。 比较新的信号应该不止31个了,还增加了31个可靠信号,为了解决以前linux中信号堆积忽略信号的问题。
作者回复: > 不知道什么要在容器内执行,直接在去宿主机上docker kill不行么。或者直接edit一下编排文件加个环境变量啥的,不就能触发原地升级么。 在生产环境中,用户是没有权限登陆宿主机的。 在Pod spec部分,runtime允许修改的只有 cotainer image了。 > 比较新的信号应该不止31个了,还增加了31个可靠信号,为了解决以前linux中信号堆积忽略信号的问题。 你说的没错,还有新的 32-64 可靠信号。因为和这一讲的问题没有太大关系,在这里就不展开了。
2020-11-196 - 1900在容器中不能响应SIGKILL 和 SIGSTOP,但是在宿主机中可以响应,因为在宿主机中所看到的“容器1号进程”在宿主机上只是一个普通进程
作者回复: @1900, 如果可以的话,你可以动手试一下,看看结果是不是和你分析的一样 :-)
2020-11-1935