你可以访问 Pugi - GitHub 获取源代码。项目提供了 Makefile,你需要自己编译可执行文件。用于非 Linux 系统构建支持的 Containerfile 正在测试中。
Pugi 基于 eBPF,因此对 Linux Kernel 有一定版本要求。
笔者前些日子排查了一个问题。RC 环境测试通过的接口上线后,同样的请求却因为非法字符触发了 Bad Request。最可疑的区别是 RC 用的 NGINX,生产用的网关。
一般来说排查问题有好几种方法,例如
- 看看网关的日志,发出去了什么东西
- 看看服务的日志,收到了什么东西
不过网关没有记录发出去什么了,而服务端还没执行到记录收到了什么(没有在校验前打点,是我草率了)。
要想知道发生了什么,最简单的方法就是打印出服务收到的原始 HTTP 报文。
2023 年我写过一个小工具 Corgi,就是为了这种场景准备的 —— 在网关和服务之间插一个 TCP 代理,把原始报文打印出来。
但现在这个服务通过 Eureka 发布自身端口,中间插不进去(当然后来看了文档,似乎端口也可以自己声明)。
2024 年也有网友问:「能不能绑定在已经占用的端口上?」,当时的我虽然知道 eBPF 这种东西,对其威力并没有什么理解(当然现在也是),因此按照自己的所学给出了否定的答案。
这个回答也不能说错,搞代理确实需要端口。但是如果说我们其实不需要代理呢?比如看看系统调用。
进程收发的每一个字节都要经过 read、write、recvfrom、sendto 这些系统调用,大多数 eBPF 的介绍材料中的经典例子就是捕获并展示这些数据。
感谢 LLM,现在我知道为什么之前写的 demo 没有成功了,因为我没有监听足够多的 tracepoint。
Pugi 用 eBPF 挂接了 8 个系统调用的进入和退出,在内核态捕获数据,在用户态重组出完整的 HTTP 报文。
sudo pugi --pid 1234 --status 400
……
[IN] 14:23:11.201 pid=1234 fd=32
POST /api/v1/order?key=hello\x00world HTTP/1.1
Host: production-svc:8080
Content-Type: application/json
Content-Length: 256
……
回到那个问题场景,直接通过过滤器定位到产生问题的原始请求报文 —— 原来是生产网关触发了边界条件,转发时进行了两次转义。
你可以怎么用 Pugi
把它指向一个进程的 PID,它就能在终端里实时打印出这个进程收发的 HTTP 请求和响应:
- 支持路径前缀、方法、Header、正文关键字过滤
- 支持按响应状态码过滤
- 支持按流量方向(入站/出站)筛选展示
笔者目前工作中接触到的系统一般是 Anolis OS 8 和 Kylin Server V10,这两个操作系统的 Linux Kernel 最低版本是 4.18,因此本项目不考虑支持更早的版本。