作者回复: @赵守忠[开心每一天]
非常好的发现啊!
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;
}
}
作者回复: @Helios,
很好的一个问题。
很多介绍容器的文章可能都会强调容器是进程,不过它们讨论的背景应该是和虚拟机做比较之后这么说的,因为在容器之前虚拟机是云平台上最流行的技术。强调容器是进程的目的是区分容器与虚拟机的差别,但是我不认为这个是容器的本质。
其实无论是namespace (pid namespace)还是cgroups都是在管理进程, 容器中运行是进程,这个是个明显的特征了,但不是本质。
我们如果换一个角度去思考,如果容器流行先于虚拟机技术, 我们是否还会强调容器是进程了呢?
作者回复: 谢谢 @上邪忘川, 很好的补充材料!
作者回复: 谢谢 @莫名!
仔细读文档也是很有用的!
作者回复: @daydreamer
赞!
作者回复: 在宿主机上kill容器的1号进程是可以的。不过,有时候容器的用户没有宿主机登陆的权限。
作者回复: @维c
很好的分析!force 由发送信号的进程和接受信号进程是否在同一个namespace里决定。你可以再看一下代码。
作者回复: @朱雯
很好的问题!我在文中只是说了31个posix标准里的signal. Linux 里还有编号32-64的real-time signal的扩展定义。因为和这一讲的问题没有太大的关系,我没有具体展开了。
作者回复: 至于为什么即使在宿主机机上向容器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))
作者回复: @1900, 如果可以的话,你可以动手试一下,看看结果是不是和你分析的一样 :-)
作者回复: > 不知道什么要在容器内执行,直接在去宿主机上docker kill不行么。或者直接edit一下编排文件加个环境变量啥的,不就能触发原地升级么。
在生产环境中,用户是没有权限登陆宿主机的。 在Pod spec部分,runtime允许修改的只有 cotainer image了。
> 比较新的信号应该不止31个了,还增加了31个可靠信号,为了解决以前linux中信号堆积忽略信号的问题。
你说的没错,还有新的 32-64 可靠信号。因为和这一讲的问题没有太大关系,在这里就不展开了。
作者回复: @Geek_71d4ac
你说的没错, D state (uninterruptible) 进程 还有 Zombie进程都是不能接受任何信号的。我在后面的章节里还有介绍。
作者回复: @JIanxu,
> 因为不是宿主机上的第一个进程所以 UNKILLABLE 也没有置位
这个SIGNAL_UNKILLABLE 是目标进程(容器里的1号进程)在创建的时候置位的,所以无论发送信号的进程在容器namespace还是在host namespace, 这个flag都是存在的。
> 能不能进一步介绍一下为什么在Kernel 里面要放置这三个 if 语句来 ignore signal 呢?
这里可以看一下这段代码最初check-in的comments. 我的理解是如果1号进程被杀,会是整个系统处于混乱并且难调试的状态,我们要尽量的避免这种情况。
commit 86989c41b5ea08776c450cb759592532314a4ed6
Author: Eric W. Biederman <ebiederm@xmission.com>
Date: Thu Jul 19 19:47:27 2018 -0500
signal: Always ignore SIGKILL and SIGSTOP sent to the global init
If the first process started (aka /sbin/init) receives a SIGKILL it
will panic the system if it is delivered. Making the system unusable
and undebugable. It isn't much better if the first process started
receives SIGSTOP.
So always ignore SIGSTOP and SIGKILL sent to init.
This is done in a separate clause in sig_task_ignored as force_sig_info
can clear SIG_UNKILLABLE and this protection should work even then.
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
作者回复: @争光Alan, 谢谢,很好的建议!
我在这一章的最后,也拿了tini做了example来做一些简单best practice的说明。
作者回复: > 啥叫从host namespace向他发送sigterm,这是啥意思,是宿主机对他发送sigterm吗
是的,在宿主机上的缺省namespace是host namespace.
> 不同的namespace,force不一定为0
是的, signal sender不在同一个namespace的时候, force不为0.
> 我的问题在于SIGNAL_UNKILLABLE 标签还会不会打上,打上以后是对宿主机这个标签也生效吗。
进程创建后这个标签是一直有的,只是pid在容器namespace里看到的是1,而在宿主机的namespace里是另外一个pid值
作者回复: 1. 你可以解决程序无法连接数据而退出的问题。
2. 或者你需要一个controllerl来监控容器状态,自动使容器重启,这个如果使用kubernetes就可以了
作者回复: 谢谢,华仔!
作者回复: > 问题A
在容器内部无法kill 1/ kill -9 1, 但是在宿主机上是可以SIGKILL 容器pid1在host namespace里对应的pid的。
> 问题B
是的。在宿主机上SIGKILL肯定可以杀掉容器的pid1, 而SIGTERM也要看情况。