deepflowio / deepflow

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

[FR] 优化agent ebpf map内存占用 #8028

Closed qyzhaoxun closed 2 months ago

qyzhaoxun commented 2 months ago

Search before asking

Description

deepflow-agent ebpf collect启动后会初始化大量ebpf map,在不同配置下有些map可能并不会使用到,另外有些map初始化的size较大,在使用sidecar模式下会占用大量内存。这里基于6.6.4实际测试了没有部署和实际部署前后的内存占用区别,内存相差135M,其中agent内存占用大概60M,ebpf map初始化占用70-80M

企业微信截图_d732099e-ec13-4350-b1af-dcd398dce4f5 企业微信截图_2e11115c-cd39-46ff-9692-3b8f70f121dd

Use case

看到deepflow中大量采用了array和hash map,希望能针对sidecar模式降低array size,并且针对hash map支持非预占用模式,希望内存占用初始值可以在20M以内

Related issues

No response

Are you willing to submit a PR?

Code of Conduct

qyzhaoxun commented 2 months ago

https://github.com/qyzhaoxun/deepflow/commit/47d08d0fd9054598cbfad87a9648a7826a42eb72

这块测试将hash map size调小后内存占用确实可以有进一步减少,减少了大概35M,另外看当前agent也有配置可以减少ebpf map占用情况,max-socket-entries,socket-map-max-reclaim,max-trace-entries,不过这里只能配置大于100000,不知道这里限制的原因是什么,也不清楚ebpf map size缩小后会有什么影响

yinjiping commented 2 months ago

@qyzhaoxun max-socket-entries,socket-map-max-reclaim,max-trace-entries,这些是支持的最大链接(socket)数量的配置项。 如果ebpf map size缩小,而当前节点的实际业务流量(链接数)大于ebpf map size的大小,这时候ebpf map 时间比较老的链接(socket)就会被刷掉,这样以来本来同一条数据流的数据,就会分成两个或多个不同的数据流的数据,极端情况下可能请求响应无法聚合。

我们希望尽量避免上面情况发生,所以把范围设置大了一些,但是确实没有考虑到kernel的内存占用过多的情况,这个我们会进行下调放宽这个范围。

感谢您提的这个问题,我们会做个整体优化在map方面。

另外,也可以关闭on-cpu-profile功能(如果不需要的话),来降低一下map的内存占用。

ebpf:
    on-cpu-profile:
      disabled: true

我在这里做了下评论: https://github.com/qyzhaoxun/deepflow/commit/47d08d0fd9054598cbfad87a9648a7826a42eb72

qyzhaoxun commented 2 months ago

这里hash_map如果换成lru_hash_map会不会更好些?另外size缩小后会有什么风险吗?

yinjiping commented 2 months ago

这里hash_map如果换成lru_hash_map会不会更好些?另外size缩小后会有什么风险吗?

lru_hash_map 在hash map中性能比较差,我们没有选用。目前的socket 和 trace hash map有回收机制 https://github.com/deepflowio/deepflow/blob/main/agent/src/ebpf/user/socket.c#L1193

size缩小后,如果你的环境流量很大, 可能会频繁触发回收机制,挤出比较老的socket记录,严重可能会使得新链接无法加入到socket hash中,还是需要根据实际环境的流量酌情设置这个值。

qyzhaoxun commented 2 months ago

这块咱们有计划优化下 ebpf map 的内存占用吗?

yinjiping commented 2 months ago

@qyzhaoxun 已在TODO里面,预计在月底(9月份)提交优化。

qyzhaoxun commented 2 months ago

这块优化的方案是咋样的,可以大致分享下吗?

yinjiping commented 2 months ago

可能会在下面几点进行优化:

上面是大致思路

yinjiping commented 2 months ago

目前做了下面的优化:

测试情况:

Memory usage of maps corresponding to eBPF capabilities in DeepFlow:

Uprobe Trace [1] Continuous Profile Pre-allocating Memory Dwarf Unwinding [1] Memory used (Bytes)
Disable Disable Disable Disable 13,674,008
Enable Disable Disable Disable 30,909,632
Disable Enable Disable Disable 31,731,736
Disable Disable Enable Disable 59,557,832
Disable Enable Disable Enable [2] 182,281,992
Disable Enable Enable Enable [2] 228,165,816
Enable Enable Enable Enable [2] 263,095,728
Disable Enable Enable Disable 77,615,560 [3]

Additional note:

Optimization Results:

In total, this optimization reduces memory usage by 63,119,448MB compared to the previous version.

qyzhaoxun commented 1 month ago

deepflow-mem-status.log

这里另外发现了两个问题,麻烦确认下

  1. 针对ebpf agent会先挂载,再卸载,最后再挂载,这个导致刚开始起来的时候内存占用会较高,等到最后的挂载完成后内存会稳定下来,如果是在内存较少导致熔断场景,agent频繁重启会对整机内存产生较大压力
  2. 这里agent一直在尝试修改daemonset,但是rbac我们没有配置,这里为什么要一直patch daemonset,这里的逻辑是否有必要,可以干掉吗?
yinjiping commented 1 month ago

deepflow-mem-status.log

这里另外发现了两个问题,麻烦确认下

  1. 针对ebpf agent会先挂载,再卸载,最后再挂载,这个导致刚开始起来的时候内存占用会较高,等到最后的挂载完成后内存会稳定下来,如果是在内存较少导致熔断场景,agent频繁重启会对整机内存产生较大压力
  2. 这里agent一直在尝试修改daemonset,但是rbac我们没有配置,这里为什么要一直patch daemonset,这里的逻辑是否有必要,可以干掉吗?

第一个问题:ebpf agent初始化的时候会先挂载主要目的是实现内核适配工作,获取依赖的结构偏移值(BTF开启的话,实际上可以优化这部分);然后执行卸载使得ebpf功能模块部分在(eBPF trace)整个初始化完成后处于关闭状态,此时对内核接口的hook全部关闭,等待上层发出启动信号。对于“在内存较少导致熔断场景”,可能要考虑要进行优化一下(初始化的时候直接挂载一次)。

第二个问题:这部分日志,看上去是在更改 k8s pod 资源限制,我们现在默认是以 agent-group-config 里面配置的限制为主,如果 agent-group-config 和 k8s yaml 设置的不一样,agent 会自动把 k8s yaml 里的改为 agent-group-config 的,要是没有 rbac 的话,就失败了,这个逻辑不能直接去掉。