Open Fancyki1 opened 3 months ago
@Fancyki1 您好,
EbpfTypeTracePoint, EbpfTypeTlsUprobe, EbpfTypeNone
答:这应该是没有配置 static_config.ebpf.uprobe-process-name-regexs.golang
,因此不会上报 uprobe 获取的 golang 程序的数据,所以 wasm 插件没有打印到 EbpfTypeGoHttp2Uprobe, EbpfTypeGoHttp2UprobeDATA
类型OnCheckPayload
中必须调用 GetPayload()
才能继续 OnParsePayload
,而是 OnCheckPayload
需要返回一个大于 0 的协议号和协议名称(均为可自定义的内容),才会继续后续的流程。OnCheckPayload
即校验流量是否为要解析的类型,而 OnParsePayload
是做实际的流量内容的解析go_http2_uprobe.go
提供的是针对 uprobe 上报的 golang 数据进行解析的例子,您可以参考一个解析私有化协议的例子,例如 dns.go 就是一个例子,只要实现 OnCheckPayload
和 OnParsePayload
方法即可。此外,由于 deepflow-agent
在解析 http 和 http2/grpc 协议时,预留的 wasm hook 是同一个,所以也可以实现 OnHttpReq
和 OnHttpResp
,在这里面做协议解析,尽管在 OnHttpReq
和 OnHttpResp
解析看起来方便些,但是 sdk.HOOK_POINT_HTTP_REQ, sdk.HOOK_POINT_HTTP_RESP
这两个 hook 点要比 sdk.HOOK_POINT_PAYLOAD_PARSE
在流程上靠后,所以从性能上考虑的话,这种情况应该 hook sdk.HOOK_POINT_PAYLOAD_PARSE
会更好些OnCheckPayload
中的判断 if ctx.EbpfType != sdk.EbpfTypeGoHttp2Uprobe && ctx.EbpfType != sdk.EbpfTypeGoHttp2UprobeDATA {}
是为了过滤掉那些我们不想解析的流量,如果不加这个判断,就能继续走下面的流程,但是所有流量都会走下面的逻辑,这是我们不想看到的,因为会损失性能,具体这个判断条件怎么加,需要视情况而定,例如可以根据端口号,ip 地址过滤。@TomatoMr 您好,感谢您的回复,我按照你上述说的内容,重新进行了尝试,有一些进展但又遇到了新的问题。
先总结一下之前的问题: 问题1: 打开了golang的uprobe,有EbpfTypeGoHttp2Uprobe类型的数据,但是没有EbpfTypeGoHttp2UprobeDATA的数据,后面有详细流程。 问题2,3,4: 需求是解析grpc的body,因为proto文件太多几百个,通过OnCheckPayload无法区分使用哪个proto文件去UnmarshalVT解析payload,然后通过uprobe后的payload数据和不uprobe的payload数据不一样,不是很了解这里的区别差异,后面会有详细内容。 问题5:通过ip和端口过滤payload是一个办法,我们实际场景会存在端口相同的微服务(已吐槽设计不规范),所以也很难有效过滤,不过也是一个好办法。
新的进展:启用uprobe,稍微修改go_http2_uprobe.go代码,执行。
agent_group_id: g-b085d39249
wasm_plugins:
- wasm
# agent-group-config 中加入了golang的uprobe配置
static_config:
ebpf:
uprobe-process-name-regexs:
golang: "^show.*|^devopssvr.*" # 正则过滤规则
agent端生效:golang uprobe生效log,日志太多,截取一个服务的完整log
[2024-08-07 15:22:43.222525 +08:00] INFO [src/ebpf/mod.rs:707] [eBPF] INFO Uprobe [/proc/3994678/root/data/showroutingsvr] pid:3994678 go1.19.0 entry:0x43ba0 size:372 symname:runtime.execute probe_func:df_U_runtime_execute rets_count:0
[2024-08-07 15:22:43.224691 +08:00] INFO [src/ebpf/mod.rs:707] [eBPF] INFO Uprobe [/proc/3994678/root/data/showroutingsvr] pid:3994678 go1.19.0 entry:0x475c0 size:953 symname:runtime.newproc1 probe_func:df_U_enter_runtime_newproc1 rets_count:0
[2024-08-07 15:22:43.226247 +08:00] INFO [src/ebpf/mod.rs:707] [eBPF] INFO Uprobe [/proc/3994678/root/data/showroutingsvr] pid:3994678 go1.19.0 entry:0x475c0 size:953 symname:runtime.newproc1 probe_func:df_U_exit_runtime_newproc1 rets_count:1
[2024-08-07 15:22:43.227645 +08:00] INFO [src/ebpf/mod.rs:707] [eBPF] INFO Uprobe [/proc/3994678/root/data/showroutingsvr] pid:3994678 go1.19.0 entry:0x24d7e0 size:1957 symname:crypto/tls.(*Conn).Write probe_func:df_U_go_tls_write_enter rets_count:0
[2024-08-07 15:22:43.229194 +08:00] INFO [src/ebpf/mod.rs:707] [eBPF] INFO Uprobe [/proc/3994678/root/data/showroutingsvr] pid:3994678 go1.19.0 entry:0x24d7e0 size:1957 symname:crypto/tls.(*Conn).Write probe_func:df_U_go_tls_write_exit rets_count:8
[2024-08-07 15:22:43.230545 +08:00] INFO [src/ebpf/mod.rs:707] [eBPF] INFO Uprobe [/proc/3994678/root/data/showroutingsvr] pid:3994678 go1.19.0 entry:0x24eca0 size:1022 symname:crypto/tls.(*Conn).Read probe_func:df_U_go_tls_read_enter rets_count:0
[2024-08-07 15:22:43.232107 +08:00] INFO [src/ebpf/mod.rs:707] [eBPF] INFO Uprobe [/proc/3994678/root/data/showroutingsvr] pid:3994678 go1.19.0 entry:0x24eca0 size:1022 symname:crypto/tls.(*Conn).Read probe_func:df_U_go_tls_read_exit rets_count:7
[2024-08-07 15:22:43.233450 +08:00] INFO [src/ebpf/mod.rs:707] [eBPF] INFO Uprobe [/proc/3994678/root/data/showroutingsvr] pid:3994678 go1.19.0 entry:0x2d7c20 size:622 symname:net/http.(*http2serverConn).writeHeaders probe_func:df_U_go_http2serverConn_writeHeaders rets_count:0
[2024-08-07 15:22:43.241690 +08:00] INFO [src/ebpf/mod.rs:707] [eBPF] INFO Uprobe [/proc/3994678/root/data/showroutingsvr] pid:3994678 go1.19.0 entry:0x2d4ce0 size:1719 symname:net/http.(*http2serverConn).processHeaders probe_func:df_U_go_http2serverConn_processHeaders rets_count:0
[2024-08-07 15:22:43.249747 +08:00] INFO [src/ebpf/mod.rs:707] [eBPF] INFO Uprobe [/proc/3994678/root/data/showroutingsvr] pid:3994678 go1.19.0 entry:0x2e7920 size:3441 symname:net/http.(*http2clientConnReadLoop).handleResponse probe_func:df_U_go_http2clientConnReadLoop_handleResponse rets_count:0
[2024-08-07 15:22:43.257645 +08:00] INFO [src/ebpf/mod.rs:707] [eBPF] INFO Uprobe [/proc/3994678/root/data/showroutingsvr] pid:3994678 go1.19.0 entry:0x2e5a00 size:369 symname:net/http.(*http2ClientConn).writeHeader probe_func:df_U_go_http2ClientConn_writeHeader rets_count:0
[2024-08-07 15:22:43.266167 +08:00] INFO [src/ebpf/mod.rs:707] [eBPF] INFO Uprobe [/proc/3994678/root/data/showroutingsvr] pid:3994678 go1.19.0 entry:0x2e3000 size:543 symname:net/http.(*http2ClientConn).writeHeaders probe_func:df_U_go_http2ClientConn_writeHeaders rets_count:0
[2024-08-07 15:22:43.276418 +08:00] INFO [src/ebpf/mod.rs:707] [eBPF] INFO Uprobe [/proc/3994678/root/data/showroutingsvr] pid:3994678 go1.19.0 entry:0xa8c280 size:1106 symname:google.golang.org/grpc/internal/transport.(*loopyWriter).writeHeader probe_func:df_U_go_loopyWriter_writeHeader rets_count:0
[2024-08-07 15:22:43.279863 +08:00] INFO [src/ebpf/mod.rs:707] [eBPF] INFO Uprobe [/proc/3994678/root/data/showroutingsvr] pid:3994678 go1.19.0 entry:0xa9de80 size:9172 symname:google.golang.org/grpc/internal/transport.(*http2Client).operateHeaders probe_func:df_U_go_http2Client_operateHeaders rets_count:0
[2024-08-07 15:22:43.283184 +08:00] INFO [src/ebpf/mod.rs:707] [eBPF] INFO Uprobe [/proc/3994678/root/data/showroutingsvr] pid:3994678 go1.19.0 entry:0xaa3de0 size:12158 symname:google.golang.org/grpc/internal/transport.(*http2Server).operateHeaders probe_func:df_U_go_http2Server_operateHeaders rets_count:0
[2024-08-07 15:22:43.286897 +08:00] INFO [src/ebpf/mod.rs:707] [eBPF] INFO Uprobe [/proc/3994678/root/data/showroutingsvr] pid:3994678 go1.19.0 entry:0xa7e2c0 size:1275 symname:golang.org/x/net/http2.(*Framer).checkFrameOrder probe_func:df_U_golang_org_x_net_http2_Framer_checkFrameOrder rets_count:0
[2024-08-07 15:22:43.290550 +08:00] INFO [src/ebpf/mod.rs:707] [eBPF] INFO Uprobe [/proc/3994678/root/data/showroutingsvr] pid:3994678 go1.19.0 entry:0xa7eb60 size:176 symname:golang.org/x/net/http2.(*Framer).WriteDataPadded probe_func:df_U_golang_org_x_net_http2_Framer_WriteDataPadded rets_count:0
原本的代码不改,只在关键位置打印log输出
func (p parser) OnParsePayload(ctx *sdk.ParseCtx) sdk.Action {
// ...省略代码
defaultStatus := sdk.RespStatusOk
switch ctx.EbpfType {
case sdk.EbpfTypeGoHttp2Uprobe:
// ...打印有输出,执行到了这里
sdk.Warn("EbpfTypeGoHttp2Uprobe","1111")
case sdk.EbpfTypeGoHttp2UprobeDATA:
// ...打印没有输出,没有执行到这里
sdk.Warn("EbpfTypeGoHttp2UprobeDATA","2222")
switch ctx.Direction {
case sdk.DirectionResponse:
// 没有执行
case sdk.DirectionRequest:
// 没有执行
}
}
EbpfTypeGoHttp2Uprobe的payload打印header结果正常,但是没有EbpfTypeGoHttp2UprobeDATA的payload, 这个地方可能是一个问题,我不确定,因为demo中的proto解析是放到EbpfTypeGoHttp2UprobeDATA事件里面的,区分了DirectionResponse和DirectionRequest,我是否可以这样理解,EbpfTypeGoHttp2Uprobe是请求头header部分,EbpfTypeGoHttp2UprobeDATA是请求内容。
sdk.Warn("parseHeader", streamID, string(payload[16:16+keyLen]), string(payload[16+keyLen:16+keyLen+valLen]))
[2024-08-08 16:49:17.493448 +08:00] WARN [src/plugin/wasm/abi_import.rs:64] wasm log: parseHeader%!(EXTRA uint32=7983, string=:method, string=POST)
[2024-08-08 16:49:17.493604 +08:00] WARN [src/plugin/wasm/abi_import.rs:64] wasm log: parseHeader%!(EXTRA uint32=7983, string=:scheme, string=http)
[2024-08-08 16:49:17.493679 +08:00] WARN [src/plugin/wasm/abi_import.rs:64] wasm log: parseHeader%!(EXTRA uint32=7983, string=:path, string=/b_info.BInfo/GetBrDeviceList)
[2024-08-08 16:49:17.493749 +08:00] WARN [src/plugin/wasm/abi_import.rs:64] wasm log: parseHeader%!(EXTRA uint32=7983, string=:authority, string=show-br-svc.platform:21014)
[2024-08-08 16:49:17.493862 +08:00] WARN [src/plugin/wasm/abi_import.rs:64] wasm log: parseHeader%!(EXTRA uint32=7983, string=content-type, string=application/grpc)
[2024-08-08 16:49:17.494187 +08:00] WARN [src/plugin/wasm/abi_import.rs:64] wasm log: parseHeader%!(EXTRA uint32=7983, string=user-agent, string=grpc-go/1.53.0)
[2024-08-08 16:49:17.494643 +08:00] WARN [src/plugin/wasm/abi_import.rs:64] wasm log: parseHeader%!(EXTRA uint32=7983, string=te, string=trailers)
[2024-08-08 16:49:17.494772 +08:00] WARN [src/plugin/wasm/abi_import.rs:64] wasm log: parseHeader%!(EXTRA uint32=7983, string=grpc-accept-encoding, string=gzip)
[2024-08-08 16:49:17.494855 +08:00] WARN [src/plugin/wasm/abi_import.rs:64] wasm log: parseHeader%!(EXTRA uint32=7983, string=grpc-timeout, string=19999958u)
[2024-08-08 16:49:17.494942 +08:00] WARN [src/plugin/wasm/abi_import.rs:64] wasm log: parseHeader%!(EXTRA uint32=7983, string=, string=)
[2024-08-08 16:49:17.495067 +08:00] WARN [src/plugin/wasm/abi_import.rs:64] wasm log: parseHeader%!(EXTRA uint32=7983, string=:status, string=200)
[2024-08-08 16:49:17.501661 +08:00] WARN [src/plugin/wasm/abi_import.rs:64] wasm log: parseHeader%!(EXTRA uint32=7983, string=content-type, string=application/grpc)
[2024-08-08 16:49:17.501789 +08:00] WARN [src/plugin/wasm/abi_import.rs:64] wasm log: parseHeader%!(EXTRA uint32=7983, string=, string=)
如上发现是没有组包还是什么的,数据是一条一条的?因为没有EbpfTypeGoHttp2UprobeDATA事件,然后转换思路hook HOOK_POINT_HTTP_REQ
HOOK_POINT_HTTP_RESP
, 在 OnHttpResp
中过滤 ctx.BaseCtx.L7 == ProtocolGrpc
的payload,打印对比EbpfTypeGoHttp2Uprobe的payload
# EbpfTypeGoHttp2Uprobe的payload
[2024-08-08 16:52:22.827371 +08:00] WARN [src/plugin/wasm/abi_import.rs:64] wasm log: payload%!(EXTRA []uint8=[51 0 0 0 55 125 0 0 10 0 0 0 30 0 0 0 58 97 117 116 104 111 114 105 116 121 115 104 111 119 45 98 114 97 110 99 104 45 115 118 99 46 112 108 97 116 102 111 114 109 58 50 49 48 49 48])
[2024-08-08 16:52:22.827469 +08:00] WARN [src/plugin/wasm/abi_import.rs:64] wasm log: parseHeader%!(EXTRA uint32=32055, string=content-type, string=application/grpc)
# 在 `OnHttpResp` 中过滤 `ctx.BaseCtx.L7 == ProtocolGrpc` 的payload
[2024-08-08 15:20:48.697826 +08:00] WARN [src/plugin/wasm/abi_import.rs:64] wasm log: content: %!(EXTRA []uint8=[0 0 2 1 4 0 0 40 89 136 192 0 0 67 0 0 0 0 40 89 0 0 0 0 62 10 36 100 55 99 102 54 102 97 48 45 57 102 50 101 45 52 48 56 100 45 57 97 55 102 45 55 52 97 98 48 48 55 56 48 101 54 50 16])
[2024-08-08 15:20:49.799646 +08:00] WARN [src/plugin/wasm/abi_import.rs:64] wasm log: content: %!(EXTRA []uint8=[0 0 2 1 4 0 0 40 89 136 192 0 0 67 0 0 0 0 40 89 0 0 0 0 62 10 36 100 55 99 102 54 102 97 48 45 57 102 50 101 45 52 48 56 100 45 57 97 55 102 45 55 52 97 98 48 48 55 56 48 101 54 50 16])
#
看着没有任何头绪~ 因为我需要从grpc的response的payload中body内容,现在就非常难解析,一个是要从request中找到path然后再匹配出对应的proto然后再解析,现在response中不知道如何判断请求的path,也无法匹配到对应的proto。
EbpfTypeGoHttp2Uprobe
@Fancyki1 您好,针对您的进展,有以下几个问题和回答:
static_config.ebpf.uprobe-process-name-regexs.golang
,我之前的回答,是解释为什么没有 EbpfTypeGoHttp2Uprobe 和 EbpfTypeGoHttp2UprobeDATA 这两种类型,如果您已经通过 cbpf 已经抓到了流量了,是不需要开启这个功能也可以解析 grpc 的内容,判断 ctx.EbpfType
是什么类型,仅是一种过滤流量的方法而已。不过从您的实际情况来看,可能用 uprobe 会比较合适,后面会提到Path
等,以及根据这些条件,判断这些流量属于哪个服务的,应该用什么 proto 去解析。另外,目前的情况是,request 和 response 确实之间的关联是不足的,hook sdk.HOOK_POINT_HTTP_REQ,sdk.HOOK_POINT_HTTP_RESP
恐怕是不能很好地达到匹配 proto
的目的或者实现麻烦,如果以上字段还是没能匹配 proto 进行解析的话,我的建议是开启 golang 的 uprobe 功能,然后 hook sdk.HOOK_POINT_PAYLOAD_PARSE
,实现 OnCheckPayload(), OnParsePayload()
,再根据 ParseCtx.ProcName
去匹配 proto 进行流量解析 @TomatoMr 感谢您的回复,我又做了一些新的尝试
回复上面内容
问题1:这个非常有可能是一个官方的bug,受限时间有限,我看看后续能否补充一下
问题2,3:相对于使用uprobe解包,我翻看了之前腾讯游戏的实践文章,给了我启发,将关键内容放到gRPC的header中,然后再通过新版本的特性,static_config.l7-protocol-advanced-features.extra-log-fields.http2
去进行解析,而且可以不使用wasm的方式采集成功,并且在ck表中可以查到自定义的内容,但是此时有遇到了奇怪的问题,我又开启了uprobe wasm插件,但却无法捕获到header自定义头,tcpdump在pod上抓包分析是有的,这里很奇怪,目前看来也可能是官方的一个bug。
问题4: 这有点难度,尽管wasm没有报错,目前我使用vtprotobuf还没有办法成功解析内容,message内容是空的,code一直都是0(int变量默认值),很明显解析没成功,这个地方也不是很好排障定位到底代码哪里写的有问题,后续有时间计划弄个脱敏的服务在k8s上验证,这样可以开源出来给你们验证。
新的想法和思路:
1.解析gRPC:换成将body的部分code
和message
内容插入到header(metadata),然后通过新的特性自动采集,优点:官方功能支持,效果快,应用改动成本低 缺点: 返回内容请求会变大,有些服务的resp的data是需要保留的,特殊隧道协议链路断开,自定义的头需要从body中采集,改header的方案不行。 综合采用改header的方案,对于有差异的再研究wasm去适配私有协议。
Search before asking
DeepFlow Component
Agent
What you expected to happen
需求: 基于grpc的私有协议解析请求中的body内容,返回body中的trace_id给deepflow做链路追踪增强关联 结果: 测试了go_http2_uprobe.go无法获取到http2的payload,自己测试了http的HOOK_POINT反倒是能有http2的流量,具体如下 验证过程: 查看定义的ebpfType
通过打印日志只能获取到以下类型
测试案例1: 测试官方的go_http2_uprobe的example
测试案例2: 在解析http协议的req和resp发现http2的流量,感觉有点奇怪,所以进一步测试 HTTP2=(HTTP2 + gRPC)
按照官方文档和配置,个人的理解,如果要解析gRPC,那就等于解析HTTP2
测试案例3: 解析http 1.0/1.1 协议功能正常
1.发现OnCheckPayload中必须调用GetPayload(),如果不调用,流程就到不了下一层OnParsePayload 2.ebpfType的值获取不到2,5,无法判断和过滤不是http2的请求 还没时间去仔细看源代码wasm的实现原理,目前gRPC/http2协议解析获取payload存在问题,能否提供一下排障或者gRPC的example进行参考?
How to reproduce
No response
DeepFlow version
Name: deepflow-server community edition Branch: v6.5 CommitID: 8555e64f065b19a98a207ee99dd46880c978ab6e RevCount: 10792 Compiler: go version go1.21.12 linux/amd64 CompileTime: 2024-08-01 15:36:18
Defaulted container "deepflow-agent" out of: deepflow-agent, configure-sysctl (init) 10793-74069ad272adad8fb53259b8d973830c0fdf7a58 Name: deepflow-agent community edition Branch: v6.5 CommitId: 74069ad272adad8fb53259b8d973830c0fdf7a58 RevCount: 10793 Compiler: rustc 1.77.1 (7cf61ebde 2024-03-27) CompileTime: 2024-08-02 03:12:26
DeepFlow agent list
ID NAME TYPE CTRL_IP CTRL_MAC STATE GROUP EXCEPTIONS REVISION UPGRADE_REVISION 1 master-c57a7-0-V1 K8S_VM 10.107.19.118 fe:fc:fe:a4:5a:c0 NORMAL default v6.5 10793 2 master-15d62-0-V4 K8S_VM 10.2.24.51 fe:fc:fe:97:6d:f0 NORMAL default v6.5 10793 3 master-b17e6-2-V3 K8S_VM 10.2.24.53 fe:fc:fe:2c:18:f9 NORMAL default v6.5 10793 4 master-41874-1-V2 K8S_VM 10.2.24.52 fe:fc:fe:0c:2a:26 NORMAL default v6.5 10793
Kubernetes CNI
NAMESPACE NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE calico-system calico-node 1 1 1 1 1 kubernetes.io/os=linux 12d kube-system kube-proxy 1 1 1 1 1 kubernetes.io/os=linux 12d kube-system open-local-agent 1 1 1 1 1 12d
[root@master-15d62-0 fancy]# calicoctl version Client Version: v3.26.1 Git commit: b1d192c95 Cluster Version: v3.26.1 Cluster Type: typha,kdd,k8s,operator,bgp,kubeadm
Operation-System/Kernel version
4.18.0-372.32.1.90.po1.x86_64
Anything else
No response
Are you willing to submit a PR?
Code of Conduct