容器实战高手课
李程远
eBay 总监级工程师,云平台架构师
24647 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 31 讲
容器实战高手课
15
15
1.0x
00:00/00:00
登录|注册

02 | 理解进程(1):为什么我在容器中不能kill 1号进程?

重点信号:SIGTERM和SIGKILL
信号的处理方式
信号的概念
init进程的功能
Linux系统启动流程
init进程的概念
从Host Namespace向未注册信号handler的1号进程发送SIGTERM的结果
容器中1号进程对信号的处理要点
init进程和Linux信号的关键概念
验证1号进程对SIGTERM的响应
1号进程对信号的处理
内核层面的处理过程
测试不同程序作为init进程的结果
理解Linux信号
理解init进程
模拟操作
问题背景
问题描述
思考题
重点总结
现象解释
知识详解
反思问题再现
为什么我在容器中不能kill 1号进程?

该思维导图由 AI 生成,仅供参考

你好,我是程远。
今天,我们正式进入理解进程的模块。我会通过 3 讲内容,带你了解容器 init 进程的特殊之处,还有它需要具备哪些功能,才能保证容器在运行过程中不会出现类似僵尸进程,或者应用程序无法 graceful shutdown 的问题。
那么通过这一讲,我会带你掌握 init 进程和 Linux 信号的核心概念。

问题再现

接下来,我们一起再现用 kill 1 命令重启容器的问题。
我猜你肯定想问,为什么要在容器中执行 kill 1 或者 kill -9 1 的命令呢?其实这是我们团队里的一位同学提出的问题。
这位同学当时遇到的情况是这样的,他想修改容器镜像里的一个 bug,但因为网路配置的问题,这个同学又不想为了重建 pod 去改变 pod IP。
如果你用过 Kubernetes 的话,你也肯定知道,Kubernetes 上是没有 restart pod 这个命令的。这样看来,他似乎只能让 pod 做个原地重启了。当时我首先想到的,就是在容器中使用 kill pid 1 的方式重启容器。
为了模拟这个过程,我们可以进行下面的这段操作。
如果你没有在容器中做过 kill 1 ,你可以下载我在 GitHub 上的这个例子运行 make image 来做一个容器镜像。
然后,我们用 Docker 构建一个容器,用例子中的 init.sh 脚本作为这个容器的 init 进程。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
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-21
    3
    52
  • Helios
    有一种老师说了一大圈,但是没有说容器的本质就是宿主机上的一个进程这个本质。

    作者回复: @Helios, 很好的一个问题。 很多介绍容器的文章可能都会强调容器是进程,不过它们讨论的背景应该是和虚拟机做比较之后这么说的,因为在容器之前虚拟机是云平台上最流行的技术。强调容器是进程的目的是区分容器与虚拟机的差别,但是我不认为这个是容器的本质。 其实无论是namespace (pid namespace)还是cgroups都是在管理进程, 容器中运行是进程,这个是个明显的特征了,但不是本质。 我们如果换一个角度去思考,如果容器流行先于虚拟机技术, 我们是否还会强调容器是进程了呢?

    2020-11-19
    4
    29
  • 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-28
    4
    15
  • 上邪忘川
    从网上找到了不错的关于SigCgt 掩码位的解释,不懂的可以看一下,豁然开朗。https://qastack.cn/unix/85364/how-can-i-check-what-signals-a-process-is-listening-to

    作者回复: 谢谢 @上邪忘川, 很好的补充材料!

    2020-11-21
    13
  • 维c
    查了一下资料,貌似sig_kernel_only函数是用了判断信号是不是kill或者stop的,是这两个信号才会返回true,这就意味着force不为0,同时信号是kill或者stop的时候信号是不会被忽略的,这也就解释了为什么宿主机是可以通过kill信号来杀掉容器里的进程,而sigterm由于force的值可能会被忽略,那么force的值又是又什么决定的呢?

    作者回复: @维c 很好的分析!force 由发送信号的进程和接受信号进程是否在同一个namespace里决定。你可以再看一下代码。

    2020-12-03
    2
    10
  • 莫名
    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-19
    10
  • 良凯尔
    虽然在容器内kill 1号进程行不通,但是我可以在宿主上kill容器的1号进程来达到重启容器的目的,是这样吗?

    作者回复: 在宿主机上kill容器的1号进程是可以的。不过,有时候容器的用户没有宿主机登陆的权限。

    2020-11-20
    3
    8
  • Action
    Host 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-05
    6
  • Helios
    不知道什么要在容器内执行,直接在去宿主机上docker kill不行么。或者直接edit一下编排文件加个环境变量啥的,不就能触发原地升级么。 比较新的信号应该不止31个了,还增加了31个可靠信号,为了解决以前linux中信号堆积忽略信号的问题。

    作者回复: > 不知道什么要在容器内执行,直接在去宿主机上docker kill不行么。或者直接edit一下编排文件加个环境变量啥的,不就能触发原地升级么。 在生产环境中,用户是没有权限登陆宿主机的。 在Pod spec部分,runtime允许修改的只有 cotainer image了。 > 比较新的信号应该不止31个了,还增加了31个可靠信号,为了解决以前linux中信号堆积忽略信号的问题。 你说的没错,还有新的 32-64 可靠信号。因为和这一讲的问题没有太大关系,在这里就不展开了。

    2020-11-19
    6
  • 1900
    在容器中不能响应SIGKILL 和 SIGSTOP,但是在宿主机中可以响应,因为在宿主机中所看到的“容器1号进程”在宿主机上只是一个普通进程

    作者回复: @1900, 如果可以的话,你可以动手试一下,看看结果是不是和你分析的一样 :-)

    2020-11-19
    3
    5
收起评论
显示
设置
留言
62
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部