加入收藏 | 设为首页 | 会员中心 | 我要投稿 常州站长网 (https://www.0519zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 移动互联 > 评测 > 正文

K8S 从懵圈到熟练:读懂此文,集群节点不下线!

发布时间:2019-05-19 10:05:02 所属栏目:评测 来源:阿里技术
导读:副标题#e# 导读:排查完全陌生的问题、不熟悉的系统组件,对许多工程师来说是无与伦比的工作乐趣,当然也是一大挑战。今天,阿里巴巴售后技术专家声东跟大家分享一例 Kubernetes 集群上的问题。这个问题影响范围较广,或许某天你也会遇到。更重要的是,作者

分析问题发生前后的系统日志,runC在重复的跑一个libcontainer_%d_systemd_test_default.slice 测试,这个测试非常频繁,但是当问题发生的时候,这个测试就停止了。所以直觉告诉我,这个问题,可能和这个测试有很大的关系。

K8S 从懵圈到熟练:读懂此文,集群节点不下线!

另外,我使用 systemd-analyze 命令,打开了 systemd 的调试级别日志,发现 systemd 有 Operation not supported 的报错。

K8S 从懵圈到熟练:读懂此文,集群节点不下线!

根据以上零散的知识,可以给出一个大概的结论:org.freedesktop.systemd1 这个 bus 在经过大量 unit 创建删除之后,没有了响应。而这些频繁的 unit 创建删除测试,是 runC 某一个改动引入的。这个改动使得 UseSystemd 函数通过创建 unit 来测试 systemd 的功能。UseSystemd 在很多地方被调用,比如创建容器,或者查看容器性能等操作。

代码分析

这个问题在线上所有 Kubernetes 集群中,发生的频率大概是一个月两例。问题一直在发生,且只能在问题发生之后,通过重启 systemd 来处理,这风险极大。

我们分别给 systemd 和 runC 社区提交了 bug,但是一个很现实的问题是,他们并没有像阿里云这样的线上环境,他们重现这个问题的概率几乎是零,所以这个问题没有办法指望社区来解决。硬骨头还得我们自己啃。

在上一节最后,我们看到了,问题出现的时候,systemd 会输出一些 Operation not supported 报错。这个报错看起来和问题本身风马牛不相及,但是直觉告诉我,这,或许是离问题最近的一个地方,所以我决定,先搞清楚这个报错因何而来。

Systemd 代码量比较大,而报这个错误的地方非常多。通过大量的代码分析(这里略去一千字),我发现有几处比较可疑地方,有了这些可疑的地方,接下来需要做的事情,就是等待。在等了三周以后,终于有线上集群,再次重现了这个问题。

Live Debugging

在征求用户同意之后,下载 systemd 调试符号,挂载 gdb 到 systemd 上,在可疑的函数下断点,continue 继续执行。经过多次验证,发现 systemd 最终踩到了sd_bus_message_seal 这个函数里的 EOPNOTSUPP 报错。

K8S 从懵圈到熟练:读懂此文,集群节点不下线!

这个报错背后的道理是,systemd 使用了一个变量 cookie,来追踪自己处理的 dbus message 。每次在加封一个新的 message 的时候,systemd 会先给 cookie的值加一,然后再把这个值复制给这个新的 message。

我们使用 gdb 打印出 dbus->cookie 这个值,可以很清楚看到,这个值超过了0xffffffff 。所以看起来,问题是 systemd 在加封过大量 message 之后,cookie 这个值32位溢出了,导致新的消息不能被加封,从而使得 systemd 对 runC 没有了响应。

K8S 从懵圈到熟练:读懂此文,集群节点不下线!

另外,在一个正常的系统上,使用 gdb 把 bus->cookie 这个值改到接近 0xffffffff,然后观察到,问题在 cookie 溢出的时候立刻出现,则证明了我们的结论。

怎么判断集群节点NotReady是这个问题导致的

首先我们需要在有问题的节点上安装 gdb 和 systemd debuginfo,然后用命令 gdb /usr/lib/systemd/systemd1 把 gdb attach 到 systemd ,在函数sd_bus_send 设置断点,然后继续执行。等 systemd 踩到断点之后,用 p /x bus->cookie 查看对应的cookie值,如果此值超过了 0xffffffff,那么 cookie 就溢出了,则必然导致节点 NotReady 的问题。确认完之后,可以使用 quit 来 detach 调试器。

问题修复

这个问题的修复,并没有那么直截了当。原因之一,是 systemd 使用了同一个 cookie 变量,来兼容 dbus1 和 dbus2 。对于 dbus1 来说, cookie 是32位的,这个值在经过 systemd 三五个月频繁创建删除 unit 之后,是肯定会溢出的;而 dbus2 的 cookie 是64位的,可能到了时间的尽头,它也不会溢出。

另外一个原因是,我们并不能简单的让 cookie 折返,来解决溢出问题。因为这有可能导致 systemd 使用同一个 cookie 来加封不同的消息,这样的结果将是灾难性的。

最终的修复方法是,使用32位 cookie 来同样处理 dbus1 和 dbus2 两种情形。同时在 cookie 达到 0xfffffff 的之后,下一个 cookie 则变成 0x80000000,即用最高位来标记 cookie 已经处于溢出状态。检查到 cookie 处于这种状态时,我们需要检查是否下一个 cookie 正在被其他 message 使用,来避免 cookie 冲突。

后记

这个问题根本原因肯定在 systemd,但是 runC 的函数 UseSystemd 使用不那么美丽的方法,去测试 systemd 的功能,而这个函数在整个容器生命周期管理过程中,被频繁的调用,让这个低概率问题的发生成为了可能。systemd 的修复已经被红帽接受,预期不久的将来,我们可以通过升级 systemd,从根本上解决这个问题。

(编辑:常州站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读