K8S 从懵圈到熟练:读懂此文,集群节点不下线!
Containerd 作为一个 gRPC 的服务器,会在接到 docker daemon 的远程调用之后,新建一个线程去处理这次请求。关于 gRPC 的细节,我们这里其实不用太多关注。在这次请求的客户端调用栈上,可以看到这次调用的核心函数在 Start 一个Process 。我们在 containerd 的调用栈里搜索 Start,Process 以及 process.go 等字段,很容易发现下边这个线程。 这个线程的核心任务,就是依靠 runC 去创建容器进程。而在容器启动之后,runC 进程会退出。所以下一步,我们自然而然会想到,runC 是不是有顺利完成自己的任务。查看进程列表,我们会发现,系统中有个别 runC 进程还在执行,这不是预期的行为。容器的启动,跟进程的启动,耗时应该是差不多数量级的,系统里有正在运行的 runC 进程,则说明 runC 不能正常启动容器。 什么是D-Bus? RunC请求D-Bus 容器 runtime 的 runC 命令,是 libcontainer 的一个简单的封装。这个工具可以用来管理单个容器,比如容器创建和容器删除。在上节的最后,我们发现 runC 不能完成创建容器的任务。我们可以把对应的进程杀掉,然后在命令行用同样的命令启动容器,同时用 strace 追踪整个过程。 分析发现,runC 停在了向带有 org.free 字段的 dbus socket 写数据的地方。那什么是 dbus 呢?在 Linux 上,dbus 是一种进程间进行消息通信的机制。 原因并不在 D-Bus 我们可以使用 busctl 命令列出系统现有的所有 bus 。如下图,在问题发生的时候,我看到问题节点 bus name 编号非常大。所以我倾向于认为,dbus 某些相关的数据结构,比如 name,耗尽了引起了这个问题。 Dbus 机制的实现,依赖于一个组件叫做 dbus daemon。如果真的是 dbus 相关数据结构耗尽,那么重启这个 daemon,应该可以解决这个问题。但不幸的是,问题并没有这么直接。重启 dbus daemon 之后,问题依然存在。 在上边 strace 追踪 runC 的截图中,runC 停在向带有 org.free 字段的 bus 写数据的地方。在 busctl 输出的 bus 列表里,显然带有这个字段的 bus,都在被 systemd使用。这时,我们用 systemctl daemon-reexec 来重启 systemd,问题消失了。所以基本上我们可以判断一个方向,问题可能跟 systemd 有关。 Systemd是硬骨头 Systemd 是相当复杂的一个组件,尤其对没有做过相关开发工作的同学来说,比如我自己。基本上,排查 systemd 的问题,我用到了四个方法,(调试级别)日志,core dump,代码分析,以及 live debugging。其中第一个,第三个和第四个结合起来使用,让我在经过几天的鏖战之后,找到了问题的原因。但是这里我们先从“没用”的 core dump 说起。 “没用的”Core Dump 因为重启 systemd 解决了问题,而这个问题本身,是 runC 在使用 dbus 和systemd 通信的时候没有了响应,所以我们需要验证的第一件事情,就是 systemd不是有关键线程被锁住了。查看 core dump 里所有线程,只有以下一个线程,此线程并没有被锁住,它在等待 dbus 事件,以便做出响应。 零散的信息 因为无计可施,所以只能做各种测试、尝试。使用 busctl tree 命令,可以输出所有bus 上对外暴露的接口。从输出结果看来,org.freedesktop.systemd1 这个 bus 是不能响应接口查询请求的。 使用下边的命令,观察 org.freedesktop.systemd1 上接受到的所以请求,可以看到,在正常系统里,有大量 Unit 创建删除的消息,但是有问题的系统里,这个 bus 上完全没有任何消息。
(编辑:源码网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |