deepflowio / deepflow

eBPF Observability - Distributed Tracing and Profiling
https://deepflow.io
Apache License 2.0
2.95k stars 333 forks source link

[BUG] wasm插件获取不到http2 payload #7691

Open Fancyki1 opened 3 months ago

Fancyki1 commented 3 months ago

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

const (
    EbpfTypeTracePoint        EbpfType = 0
    EbpfTypeTlsUprobe         EbpfType = 1
    EbpfTypeGoHttp2Uprobe     EbpfType = 2
    EbpfTypeGoHttp2UprobeDATA EbpfType = 5
    EbpfTypeNone              EbpfType = 255
)

通过打印日志只能获取到以下类型

测试案例1: 测试官方的go_http2_uprobe的example

const GO_HTTP2_EBPF_PROTOCOL = 1
func (p parser) HookIn() []sdk.HookBitmap {
    return []sdk.HookBitmap{
        sdk.HOOK_POINT_PAYLOAD_PARSE,
    }
}
func (p httpHook) OnParsePayload(baseCtx *sdk.ParseCtx) sdk.Action {
    if baseCtx.L7 != GO_HTTP2_EBPF_PROTOCOL {
                // 开启打印baseCtx中的值
                // sdk.Warn("OnParsePayload", baseCtx.EbpfType, baseCtx.Direction, baseCtx.L7)
                // 上面log不会有任何的输出 
        return sdk.ActionNext()
    }
        // 服务中有grpc调用也执行不到下面的log输出
        // 这里的问题昨天已经测试出来了,因为没有在OnCheckPayload中调用GetPayload(),所以流程没走到这里
    sdk.Warn("OnParsePayload", baseCtx.EbpfType, baseCtx.Direction, baseCtx.L7)
    return sdk.ActionNext()
}
func (p httpHook) OnCheckPayload(baseCtx *sdk.ParseCtx) (uint8, string) {
        /*
[2024-08-06 11:50:33.199875 +08:00] WARN [src/plugin/wasm/abi_import.rs:64] wasm log: OnCheckPayload%!(EXTRA sdk.EbpfType=0, sdk.Direction=0, uint8=0)
[2024-08-06 11:50:33.374074 +08:00] WARN [src/plugin/wasm/abi_import.rs:64] wasm log: OnCheckPayload%!(EXTRA sdk.EbpfType=255, sdk.Direction=1, uint8=0)
       */
        // EbpfType的值只有0和255,Direction的值有0或1,L7的值一直都0
    sdk.Warn("OnCheckPayload", baseCtx.EbpfType, baseCtx.Direction, baseCtx.L7)
    return 1, "ebpf_go_http2"
}

测试案例2: 在解析http协议的req和resp发现http2的流量,感觉有点奇怪,所以进一步测试 image HTTP2=(HTTP2 + gRPC)

// agent\crates\public\src\l7_protocol.rs
pub enum L7Protocol {
    #[num_enum(default)]
    Unknown = 0,

    // HTTP
    Http1 = 20,
    Http2 = 21,

    // RPC
    Dubbo = 40,
    Grpc = 41,
    SofaRPC = 43,

    FastCGI = 44,
    Brpc = 45,

    // SQL
    MySQL = 60,
    PostgreSQL = 61,
    Oracle = 62,

    // NoSQL
    Redis = 80,
    MongoDB = 81,

    // MQ
    Kafka = 100,
    MQTT = 101,
    AMQP = 102,
    OpenWire = 103,
    NATS = 104,
    Pulsar = 105,
    ZMTP = 106,
    // INFRA
    DNS = 120,
    TLS = 121,
    Custom = 127,
    Max = 255,
}

按照官方文档和配置,个人的理解,如果要解析gRPC,那就等于解析HTTP2

func (p httpHook) HookIn() []sdk.HookBitmap {
    return []sdk.HookBitmap{
        sdk.HOOK_POINT_HTTP_REQ,
        sdk.HOOK_POINT_HTTP_RESP,
    }
}
const ProtocolGrpc uint8 = 41
func (p httpHook) OnHttpReq(ctx *sdk.HttpReqCtx) sdk.Action {
    if ctx.BaseCtx.L7 == ProtocolGrpc {
                // 可以打印输出捕获grpc协议
                // [2024-08-06 12:35:58.627301 +08:00] WARN [src/plugin/wasm/abi_import.rs:64] wasm log: OnHttpReq find L7:%!(EXTRA uint8=41)
                // 如果不加== ProtocolGrpc的限制,这里可以获取 L7 =(41,20,21),这几个值,没看懂如果解析grpc就是解析http2,那应该在http的example中?而不是在go_http2_uprobe?
        sdk.Warn("OnHttpReq find L7:", ctx.BaseCtx.L7)
    }
    if ctx.BaseCtx.EbpfType != sdk.EbpfTypeTracePoint && ctx.BaseCtx.EbpfType != sdk.EbpfTypeTlsUprobe && ctx.BaseCtx.EbpfType != sdk.EbpfTypeNone {
                // 打印不出来内容,代码走不到这里
        sdk.Warn("OnHttpReq find ebpfType:", ctx.BaseCtx.EbpfType)
    }

    return sdk.ActionNext()
}

func (p httpHook) OnHttpResp(ctx *sdk.HttpRespCtx) sdk.Action {
    return sdk.ActionNext()
}

测试案例3: 解析http 1.0/1.1 协议功能正常

const (
    Http1 = 20
)

func main() {
    sdk.Info("on http status rewrite wasm plugin init")
    sdk.SetParser(parser{})
}

type parser struct {
    sdk.DefaultParser
}

func (p parser) OnHttpReq(ctx *sdk.HttpReqCtx) sdk.Action {
    if ctx.BaseCtx.L7 != Http1 {
        return sdk.ActionNext()
    }
        // ....省略解析payload代码
        payload, err := baseCtx.GetPayload()    
        // payload 也可以正常解析
        // ....省略代码
        return sdk.HttpReqActionAbortWithResult(sdkReq, trace, nil)
}
func (p parser) OnHttpResp(ctx *sdk.HttpRespCtx) sdk.Action {
        // 功能正常,省略代码
}

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

TomatoMr commented 3 months ago

@Fancyki1 您好,

  1. 问题:打印的 EbpfType 日志中只看到 EbpfTypeTracePoint, EbpfTypeTlsUprobe, EbpfTypeNone 答:这应该是没有配置 static_config.ebpf.uprobe-process-name-regexs.golang,因此不会上报 uprobe 获取的 golang 程序的数据,所以 wasm 插件没有打印到 EbpfTypeGoHttp2Uprobe, EbpfTypeGoHttp2UprobeDATA 类型
  2. 问题:“在解析http协议的req和resp发现http2的流量” 答:这是因为 deepflow-agent 在解析 http 和 http2 协议时给留的 wasm hook 是同一个,代码出处
  3. 问题:“还发现 OnCheckPayload 中必须调用 GetPayload(),如果不调用,流程就到不了下一层 OnParsePayload” 答:并不是一定要在 OnCheckPayload 中必须调用 GetPayload() 才能继续 OnParsePayload,而是 OnCheckPayload 需要返回一个大于 0 的协议号和协议名称(均为可自定义的内容),才会继续后续的流程。OnCheckPayload 即校验流量是否为要解析的类型,而 OnParsePayload 是做实际的流量内容的解析
  4. 问题:“能否提供一下排障或者 gRPC 的 example 进行参考” 答:go_http2_uprobe.go 提供的是针对 uprobe 上报的 golang 数据进行解析的例子,您可以参考一个解析私有化协议的例子,例如 dns.go 就是一个例子,只要实现 OnCheckPayloadOnParsePayload 方法即可。此外,由于 deepflow-agent 在解析 http 和 http2/grpc 协议时,预留的 wasm hook 是同一个,所以也可以实现 OnHttpReqOnHttpResp,在这里面做协议解析,尽管在 OnHttpReqOnHttpResp 解析看起来方便些,但是 sdk.HOOK_POINT_HTTP_REQ, sdk.HOOK_POINT_HTTP_RESP 这两个 hook 点要比 sdk.HOOK_POINT_PAYLOAD_PARSE 在流程上靠后,所以从性能上考虑的话,这种情况应该 hook sdk.HOOK_POINT_PAYLOAD_PARSE 会更好些
  5. 另外:OnCheckPayload 中的判断 if ctx.EbpfType != sdk.EbpfTypeGoHttp2Uprobe && ctx.EbpfType != sdk.EbpfTypeGoHttp2UprobeDATA {} 是为了过滤掉那些我们不想解析的流量,如果不加这个判断,就能继续走下面的流程,但是所有流量都会走下面的逻辑,这是我们不想看到的,因为会损失性能,具体这个判断条件怎么加,需要视情况而定,例如可以根据端口号,ip 地址过滤。
Fancyki1 commented 3 months ago

@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。

TomatoMr commented 3 months ago

EbpfTypeGoHttp2Uprobe

@Fancyki1 您好,针对您的进展,有以下几个问题和回答:

  1. 问题: 没有 EbpfTypeGoHttp2UprobeDATA 数据 答: 这个问题,我不知道是不是您的代码中的省略部分有做什么过滤,导致在前面就过滤掉了,当然也不能排除是我们数据源侧没有传数据到 wasm 插件中,因为信息量比较少,我暂时不能判断
  2. 问题: 关于对 EbpfTypeGoHttp2UprobeEbpfTypeGoHttp2UprobeDATA 的理解 答: 可以看这里的说明:EbpfType,以及可以参考这几个链接,了解 golang uprobe 的 payload 的格式是如何的:parseHeader, parseHeader, check_http2_go_uprobe。对于 golang uprobe 的数据,同一个请求可能会上报多次 ebpf 数据,每次只带一个头,即同一个请求可能经过多次 wasm 插件,可以理解为 EbpfTypeGoHttp2UprobeEbpfTypeGoHttp2UprobeDATA 是没有组包的,而其他类型是组好包的
  3. 问题: 针对已经存在的问题 1 和问题 2,还有什么办法实现 grpc 内容的解析 答: 其实如果不是必须要用到 ebpf 的 uprobe 功能的话,可以不用配置 static_config.ebpf.uprobe-process-name-regexs.golang,我之前的回答,是解释为什么没有 EbpfTypeGoHttp2UprobeEbpfTypeGoHttp2UprobeDATA 这两种类型,如果您已经通过 cbpf 已经抓到了流量了,是不需要开启这个功能也可以解析 grpc 的内容,判断 ctx.EbpfType 是什么类型,仅是一种过滤流量的方法而已。不过从您的实际情况来看,可能用 uprobe 会比较合适,后面会提到
  4. 问题: 微服务众多,且使用的端口可能一致,流量过滤的特征比较少,该怎么办 答: 尽量利用 ParseCtx, HttpReqCtx, HttpRespCtx 等提供的字段,结合流量特征进行过滤,例如端口号,地址,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 进行流量解析
Fancyki1 commented 3 months ago

@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的部分codemessage内容插入到header(metadata),然后通过新的特性自动采集,优点:官方功能支持,效果快,应用改动成本低 缺点: 返回内容请求会变大,有些服务的resp的data是需要保留的,特殊隧道协议链路断开,自定义的头需要从body中采集,改header的方案不行。 综合采用改header的方案,对于有差异的再研究wasm去适配私有协议。