OpenResty XRay如何分析和解决 B 站重大线上事故

来源:岁月联盟 编辑:猪蛋儿 时间:2022-07-20
B 站近日发表了一篇关于去年那场大事故的回顾文章,本文将从 OpenResty 的角度还原此事件的解决过程。
去年,OpenResty Inc. 团队使用商业产品 OpenResty XRay 的动态追踪技术介入 B 站文章中所描述的重大线上事故后,在很短的时间内定位了导致 B 站线上服务不可用的问题根源,并帮助解决重大线上事故。

事故描述
背景
B 站基于开源 OpenResty 开发了他们的内部网关系统。

事故现场
B 站当时所有线上服务器的 OpenResty 进程总是占用 CPU 100%,但不能处理任何请求。重启无法恢复,回滚他们最近的业务代码变更也无法恢复。所有服务器都有这个问题。
B 站是我们的 OpenResty XRay 产品的商业客户。OpenResty XRay 是一款基于动态追踪技术的系统故障排查和性能优化软件。事故发生一段时间后,B 站的运维团队联系到我们,希望我们能帮助分析线上的一个严重问题。

事故分析过程
我们首先通过线上由 OpenResty XRay 产品自动采样的 C 语言级别 CPU 火焰图,确定了他们的 OpenResty 的 nginx 进程的几乎所有 CPU 时间都花费在执行 Lua 代码上面。
出于对客户的隐私保护和数据安全,这张图里只显示了 OpenResty 开源软件里的函数帧,隐去了 B 站自己的 Lua 代码相关的信息。
然后我们再通过 OpenResty XRay 自动采样的 Lua 语言级别的 CPU 火焰图确认了几乎所有的 CPU 时间都花费在了单条 Lua 代码路径上。看起来是那条 Lua 代码路径发生了死循环。我们的 Lua CPU 火焰图定位的 Lua 代码路径可以精确到代码行级别。

同样地,我们只保留了不敏感的开源代码的函数帧。

B 站原文中提到的 Lua 火焰图就是 OpenResty XRay 在 B 站生产服务器上采样有问题的 OpenResty 服务进程得到的。

OpenResty XRay 在 B 站线上生成火焰图也就花费了几十秒到几分钟的时间,因为使用 100% 非侵入的动态追踪技术,整个过程不需要对 B 站的进程和应用进行任何修改。

根据 Lua 火焰图最终确认根源问题是 B 站的业务往配置元数据写入了个字符串类型的权重 0 值的坏数据(即 “0”),而 OpenResty 的 lua-resty-balancer 库的 Lua API 期望的是数值类型的权重值,从而导致了无限递归和无限循环。
LuaJIT 的即时( JIT)编译器在这里并没有 bug。之所以最初怀疑是 JIT 编译器的问题是因为对应的 Lua 代码路径乍一看并没有任何问题,同时 B 站另一个业务团队未告知的操作也对线上服务产生了影响。字符串 0 和数值 0 的区别是非常微妙的。最终我们排除了 JIT 编译器的 bug 可能性,确定了字符串 0 这个问题根源。

事故后续修复和加固

B 站在业务层面确保不会再有字符串类型的上游服务器的权重值被写入配置数据。

OpenResty XRay 新版也提供了打印 Lua 调用栈上所有局部变量的值的新功能,可以让类似问题被更快更直接地定位。

我们事后也对开源 OpenResty 的 lua-resty-balancer 库针对这种 API 误用进行特别加固,确保任何误传的字符串类型的权重值总是会被强制转成数值类型。

结语

事故最终得到完美解决。除了 B 站, 也曾经成功地帮助 Zoom、微软、去哪儿网等很多公司在线上定位和优化了很多线上问题,包括从高 CPU 波峰到高内存使用,再到内存泄漏、异常的请求延时、高硬盘 IO 等各种问题。

OpenResty XRay 目前可以自动针对 OpenResty、Nginx、LuaJIT、PHP、Python、Perl、Go、PostgreSQL、Redis 等各种不同的开源软件以及运行在这些开源软件之上的客户业务代码进行深入的分析和监控。未来我们还会陆续加入对更多技术栈的支持,包括Java、Ruby等。用户使用 OpenResty XRay,可以快速发现和精准定位各种性能问题、功能问题和安全问题, 从而保证应用的稳定性。

OpenResty XRay 中使用的 Lua 语言级别的 CPU 火焰图工具在这里有介绍。OpenResty XRay 使用的是增强过的私有动态追踪技术,感兴趣的同学可以参考我年初时写的系列文章:

关于作者
章亦春是开源项目 OpenResty