分析 OpenResty 或 Nginx 中最耗 CPU 的请求
对于像 OpenResty 和 Nginx 这样的非阻塞 Web 服务器来说,消耗大量 CPU 资源是很常见的。这要归功于操作系统特性如 epoll
和 kqueue
的 I/O 多路复用功能。有时,对于 DevOps 和 SRE 人员来说,快速精确地找出在线服务器或多个服务器中消耗最多 CPU 时间 的请求 URI 或请求主机名是很有帮助的。在本文中,我们将演示如何使用 OpenResty XRay 中的动态追踪工具,实时分析未经修改的 OpenResty 和 Nginx Web 服务器以获取此类统计数据。
我们将使用标准动态追踪工具和通过类 SQL 语言(称为 YSQL)创建的自定义工具,展示真实世界的示例,包括由 OpenResty XRay 自动生成的数据和图表。
系统环境
在此,我们以 Red Hat Enterprise Linux 7 系统为例。任何受 OpenResty XRay 支持的 Linux 发行版都应同样适用,如 Ubuntu、Debian、Fedora、Rocky、Alpine 等。
我们使用未经修改的开源 OpenResty 二进制构建作为目标应用。您可以使用任何 OpenResty 或 Nginx 二进制文件,包括您自己编译的版本。不会向现有的运行进程插入代码,也不要求软件编译带上特殊的编译选项,或者加载特定的插件或者共享库文件。这正是 动态追踪 技术的优势所在。它是真正非侵入式的。
我们还在同一系统上运行 OpenResty XRay 的 Agent 守护进程,并已 安装和配置 了 openresty-xray-cli
包中的命令行工具。
CPU 占用最高的请求主机名
使用标准工具
最便捷的方法是直接运行标准工具 ngx-cpu-hottest-hosts
。
我们首先需要找出 OpenResty 或 Nginx 服务器实例的主进程 PID。
$ ps aux | grep nginx:
root 1691450 0.0 0.0 28868 4140 ? Ss Jul05 0:00 nginx: master process /usr/local/openresty/nginx/sbin/nginx
nobody 3055159 1.5 0.0 40868 4096 ? S 14:38 4:58 nginx: worker process
主进程的 PID 是 1059。我们使用这个 PID 来追踪该进程组中的所有进程,包括 Nginx 工作进程。
$ orxray analyzer run ngx-cpu-hottest-hosts -p -1691450 -t 10
Start tracing...
Go to https://x5vrki.xray.openresty.com.cn/targets/68/history/582346 for charts
在此,我们使用 orxray
命令行工具来运行标准工具 ngx-cpu-hottest-hosts
,针对由主进程 PID 1059 指定的进程组,通过 -p
选项进行操作。请注意 PID 前的减号,它表示我们要实时跟踪该进程的整个进程组。同时,-t
选项指定了我们希望跟踪的秒数。作为一般经验法则,当目标应用不太繁忙时,我们应使用较长的采样时间窗口;而对于较繁忙的应用,则使用较短的窗口。
上述输出显示了一个链接,指向 OpenResty XRay 的 Web 控制台,我们可以在那里看到为此次运行生成的精美图表。不过,您的 Web 控制台 URI 可能会有所不同。
我们可以看到,最热门的是 openresty.org
主机名。其次是 openresty.com
。请记住,我们这里只计算了命中操作系统 CPU 分析器 的请求,而不是所有请求。因此,只有相对数值才有意义。例如,考虑到它们的样本计数分别为 733 和 612,openresty.org
消耗的 CPU 时间比 openresty.com
多约 19%。
有时,我们可能只想分析单个 Nginx 工作进程,这种情况可能发生在某个工作进程消耗的 CPU 时间比其他进程多,或者我们想要最小化引入的跟踪开销时。在这种情况下,我们可以使用该工作进程的 PID 作为 orxray
命令的 -p
选项的值,如下所示:
$ orxray analyzer run ngx-cpu-hottest-hosts -p 3055159 -t 10
这次请务必省略 PID 前的减号(-
)。
默认情况下,该工具分析当前机器上的进程。如果您想分析其他服务器上的进程,可以添加 -a agent_ID
选项来指定您想要运行的服务器。只需使用 orxray agent list
命令获取您的 OpenResty XRay Web 控制台可见的代理 ID 列表。
使用 YSQL 创建自定义工具
使用名为 YSQL 的类 SQL 语言创建自定义动态跟踪工具可以获得最大的灵活性,这更有趣。YSQL 语言从不用于查询任何关系数据库;相反,它始终被编译为动态跟踪工具,用于对实时进程和运行中的应用进行实时检查和分析。
让我们创建一个名为 my-cpu-hottest-hosts.ysql
的纯文本文件,内容如下。请随意使用您喜欢的代码编辑器。
select count(*) count, host
from cpu.profile inner join ngx.reqs
group by host
order by count desc
limit 10;
SQL 查询大部分是自解释的。最有趣的部分是 from
子句,它使用 inner join
将 Nginx 请求与操作系统的 CPU 分析器进行计数。CPU 分析器对应于虚拟表 cpu.profile
。host
列是来自 HTTP host 头的值。我们添加了 limit 10
子句,因为我们只关心前 10 个。
现在让我们运行这个 YSQL 工具。假设工作进程的 PID 是 3055159,我们有以下命令。
$ run-ysql -p 3055159 ./my-cpu-hottest-hosts.ysql -t 10
Start tracing...
Go to https://x5vrki.xray.openresty.com.cn/targets/68/history/583188 for charts
请注意,这次我们使用 run-ysql
命令行工具。
我们可以浏览网页链接,查看与上面标准工具类似的输出图表。
单行 YSQL
我们还可以将 YSQL 作为单行命令运行,无需创建本地文件。
$ run-ysql -p 3055159 -t 10 -e 'select count(*) count, host from cpu.profile inner join ngx.reqs group by host order by count desc limit 10;'
CPU 开销最高的请求 URI
我们还可以追踪目标进程中 CPU 占用最高的请求 URI。
使用标准工具
我们可以运行标准工具 ngx-cpu-hottest-uris
:
$ orxray analyzer run ngx-cpu-hottest-uris -p 3055159 -t 10
Start tracing...
Go to https://x5vrki.xray.openresty.com.cn/targets/68/history/622478 for charts
我们可以看到 CPU 占用最高的前两个请求 URI 分别是 openresty.org
的 /
和 openresty.com
的 /en
。
创建自定义 YSQL 工具
这次的 YSQL 查询略有不同。我们使用 uri
列代替。
select count(*) count, host, uri
from cpu.profile inner join ngx.reqs
group by host, uri
order by count desc
limit 10
现在让我们运行这个 YSQL 工具。假设工作进程的 PID 是 3055159,我们有以下命令。
$ run-ysql -p 3055159 ./my-cpu-hottest-uris.ysql -t 10
Start tracing...
Go to https://x5vrki.xray.openresty.com.cn/targets/68/history/622204 for charts
我们可以浏览网页链接,查看与上述自定义工具类似的输出图表。
深入探究
主机名或 URI 占用更多 CPU 资源的一个自然原因是它们的请求数量比其他的多。我们可以通过在一个时间窗口内按主机名或 URI 分组计算所有请求来验证这一点。
请求最多的最繁忙主机名
使用标准工具
我们可以使用标准工具 ngx-req-counts-by-hosts
进行计数。
$ orxray analyzer run ngx-req-counts-by-hosts -p 3055159
Start tracing...
Go to https://x5vrki.xray.openresty.com.cn/targets/68/history/584324 for charts
我们可以按照指示浏览网页:
我们可以看到,排名第一的域名 openresty.org
也拥有最多的请求。但第二位是 doc.openresty.com
而不是 openresty.com
。这意味着平均而言,openresty.com
的每个请求可能比 doc.openresty.com
的请求消耗更多的 CPU 时间。
使用 YSQL 创建自定义工具
仅出于演示目的,我们可以创建一个简单的 YSQL 工具文件,以创建一个模拟标准工具 ngx-req-counts-by-hosts
的自定义工具:
select count(*) count, host
from ngx.reqs
group by host
order by count desc
limit 10;
请注意 from
子句。我们不再与虚拟表 cpu.profile
进行 inner join
。因此,现在我们统计的是在采样时间窗口内 Nginx 或 OpenResty 处理的所有请求。
现在让我们针对 PID 为 3055159
的 Nginx 工作进程运行这个 YSQL 工具。
$ run-ysql -p 3055159 ./top-10-hosts-req.ysql
Start tracing...
Go to https://x5vrki.xray.openresty.com.cn/targets/68/history/584615 for charts
我们将得到一个类似于上面所示条形图的图表。
网络数据量最大的最繁忙主机名
我们还有 ngx-req-size-by-hosts
标准工具,用于采样具有大量累积网络流量数据(或请求大小,包括请求头和请求体)的最繁忙请求主机名。
$ orxray analyzer run ngx-req-size-by-hosts -p 3055159
Start tracing...
Go to https://x5vrki.xray.openresty.com.cn/targets/68/history/584675 for charts
我们可以看到,openresty.org
和 doc.openresty.com
也占据了大部分网络数据量,这与请求计数的情况类似。
一个自定义的 YSQL 工具可能如下所示:
select sum(req_size) request_size, host
from ngx.reqs
group by host
order by request_size desc
limit 10;
来自 ngx.reqs
虚拟表的 req_size
列代表总请求大小(请求头 + 请求体)。但它不包含任何 TLS/SSL 握手流量。
查找瓶颈并进行优化
为了分析具体的性能瓶颈并获得优化建议,我们可以进一步使用 C 层面和 Lua 层面的 CPU 火焰图工具。
OpenResty XRay 可以自动分析任何繁忙的应用(不仅限于 OpenResty 和 Nginx 应用!),我们的人类专家还可以提供包含可执行建议的丰富分析报告。这样,普通用户甚至不需要知道何时何地运行什么工具。他们也不需要解释分析器的输出。
直接在 Web 控制台中运行
用户可以选择直接在 OpenResty XRay 的 Web 控制台中执行本教程中涉及的任何工具。它们甚至可以在高 CPU 使用率等有趣事件发生时自动触发。openresty-xray-cli
中的命令行实用程序对于演示目的很方便。它们也易于自动化,并可由 DevOps 和 SRE 人员集成到其他系统中。
跟踪容器内的应用
OpenResty XRay 工具支持透明地跟踪容器化应用。Docker 和 Kubernetes(K8s)容器都可以透明地工作。与普通应用进程一样,目标容器不需要任何应用或额外权限。OpenResty XRay Agent 守护进程应该在目标容器外部运行(例如直接在主机操作系统中或在其自己的特权容器中)。
让我们看一个例子。我们首先使用 docker ps
命令检查容器名称或容器 ID。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4465297209d9 openresty/openresty:1.19.3.1-2-alpine-fat "/usr/local/openrest…" 18 months ago Up 11 minutes angry_mclaren
这里的容器名称是 angry_mclaren
。然后我们可以找出此容器中目标进程的 PID。
$ docker top angry_mclaren
UID PID PPID C STIME TTY TIME CMD
root 3310154 3310133 0 14:22 ? 00:00:00 nginx: master process /usr/local/openresty/bin/openresty -g daemon off;
nobody 3310209 3310154 0 14:22 ? 00:00:00 nginx: worker process
openresty
工作进程的 PID 是 3310209
。然后我们像往常一样针对这个 PID 运行 OpenResty XRay 分析器。
$ orxray analyzer run ngx-cpu-hottest-hosts -p 3310209 -t 10
Start tracing...
...
Go to https://x5vrki.xray.openresty.com.cn/targets/68/history/600752 for charts
OpenResty XRay 还能够自动检测长时间运行的进程,并将其识别为特定类型的"应用"(如 “OpenResty”、“Python” 等)。
工具的实现方式
所有工具都是使用 Y 语言 实现的。OpenResty XRay 通过 Stap+1 或 eBPF2 后端执行这些工具,这两种后端都使用了 100% 非侵入式的动态追踪技术,基于 Linux 内核的 uprobes
和 kprobes
功能。YSQL 查询首先被编译成 Y 语言,然后进一步编译成可执行的动态追踪工具。
我们不需要目标应用和进程的任何配合。我们不使用也不需要任何日志数据或指标数据。我们直接以严格的只读方式分析运行中进程的进程空间。而且,我们绝不会向目标进程注入任何字节码或其他可执行代码。这是 100% 干净和安全的。
工具的开销
本教程中演示的动态追踪工具非常高效,适合在线执行。
当工具未运行且不进行主动采样时,对系统和目标进程的开销严格为零。我们从不向目标应用和进程注入任何额外的代码或插件;因此,不存在固有的开销。
在采样期间,在典型的服务器硬件上,请求延迟平均仅增加不到 1 微秒(us)。对于每个 CPU 核心每秒处理数万个请求的最快 OpenResty/Nginx 服务器来说,最大请求吞吐量的降低也仅约为 4%。
关于作者
章亦春是开源 OpenResty® 项目创始人兼 OpenResty Inc. 公司 CEO 和创始人。
章亦春(Github ID: agentzh),生于中国江苏,现定居美国湾区。他是中国早期开源技术和文化的倡导者和领军人物,曾供职于多家国际知名的高科技企业,如 Cloudflare、雅虎、阿里巴巴, 是 “边缘计算“、”动态追踪 “和 “机器编程 “的先驱,拥有超过 22 年的编程及 16 年的开源经验。作为拥有超过 4000 万全球域名用户的开源项目的领导者。他基于其 OpenResty® 开源项目打造的高科技企业 OpenResty Inc. 位于美国硅谷中心。其主打的两个产品 OpenResty XRay(利用动态追踪技术的非侵入式的故障剖析和排除工具)和 OpenResty Edge(最适合微服务和分布式流量的全能型网关软件),广受全球众多上市及大型企业青睐。在 OpenResty 以外,章亦春为多个开源项目贡献了累计超过百万行代码,其中包括,Linux 内核、Nginx、LuaJIT、GDB、SystemTap、LLVM、Perl 等,并编写过 60 多个开源软件库。
关注我们
如果您喜欢本文,欢迎关注我们 OpenResty Inc. 公司的博客网站 。也欢迎扫码关注我们的微信公众号:
翻译
我们提供了英文版原文和中译版(本文)。我们也欢迎读者提供其他语言的翻译版本,只要是全文翻译不带省略,我们都将会考虑采用,非常感谢!
-
这实际上是 OpenResty Inc 大幅增强的 eBPF 实现,称为 ORBPF。 ↩︎