通过 DeepFlow 查询函数在 CPU 上消耗的时间(CPU 性能剖析)

前言

本文主要介绍 DeepFlow 的 AutoProfiling(on-cpu)持续性能剖析功能的配置与使用。该能力与 DeepFlow 的部署方式无关。自 v6.6.3 起,deepflow-agent 的配置格式有较大调整,本文使用新版本配置格式,建议使用 v7.0 LTS 或后续 LTS 版本。

CPU 剖析的配置与应用

前置条件

需要内核版本支持 eBPF 能力,DeepFlow 中开启 eBPF 能力(默认开启)所需内核版本要求如下:

体系架构 发行版 内核版本 kprobe [1] Golang uprobe OpenSSL uprobe perf
X86 CentOS 7.9 3.10.0-940+ [2] Y Y [3] Y [3] Y
RedHat 7.6 3.10.0-940+ [2] Y Y [3] Y [3] Y
* 4.14 [4] Y Y [3] - Y
* 4.15 Y Y [3] - Y
* 4.16 Y Y - Y
* 4.17+ Y Y Y Y
SUSE 12 SP5 4.12 [5] Y Y - Y
ARM CentOS 8 4.18 Y Y Y Y
EulerOS 5.10+ Y Y Y Y
麒麟 KylinOS V10 SP1 4.19.90-23 [6] Y Y Y Y
麒麟 KylinOS V10 SP2 4.19.90-25.24+ [7] Y Y Y Y
麒麟 KylinOS V10 SP3 4.19.90-52.24+ Y Y Y Y
其他发行版 5.8+ Y Y Y Y

对内核版本的补充说明:

  • [1]:在 Linux 启用了 BTF(BPF Type Format)的情况下,X86 架构内核版本大于等于 5.5、ARM 架构内核版本大于等于 6.0 时,agent 将自动使用 fentry/fexit 替代 kprobe/kretprobe,可获得约 15% 的性能提升。
  • [2]:CentOS 7.9、RedHat 7.6 向 3.10 内核中移植了一部分 eBPF 能力
    • 在这两个发行版中,DeepFlow 支持的详细内核版本如下(依赖的 Hook 点):
      • 3.10.0-957.el7.x86_64
      • 3.10.0-1062.el7.x86_64
      • 3.10.0-1127.el7.x86_64
      • 3.10.0-1160.el7.x86_64
    • 注意 RedHat 的声明:

      The eBPF in Red Hat Enterprise Linux 7.6 is provided as Tech Preview and thus doesn't come with full support and is not suitable for deployment in production. It is provided with the primary goal to gain wider exposure, and potentially move to full support in the future. eBPF in Red Hat Enterprise Linux 7.6 is enabled only for tracing purposes, which allows attaching eBPF programs to probes, tracepoints and perf events.

  • [3]:容器内的 Golang/OpenSSL 进程不支持。
  • [4]:在内核 4.14 中,一个 tracepoint 不能被多个 eBPF program attach(例如不能同时运行两个或多个 deepflow-agent)。其他版本不存在该问题。
  • [5]:目前支持 SUSE 12 SP5 4.12.14,但 Linux 社区的 4.12 版本依然不支持。
  • [6]:KylinOS V10 SP1 部分内核(例如 4.19.90-23.48.v2101.ky10.aarch64)可正常运行,但不确保 KylinOS V10 SP1 所有 aarch64 架构内核都能正常运行 deepflow-agent。
  • [7]:KylinOS V10 SP2 某些内核(如 4.19.90-24.4.v2101.ky10.aarch64)由于不支持 bpf_probe_read_user(),无法读取用户态数据,因此不支持 AutoTracing;但支持持续剖析和文件读写追踪功能。

支持语言

类型 支持语言/库 社区版 企业版
on-cpu Java
C/C++
Rust
Golang
Python ***
CUDA
Lua *
off-cpu Java
C/C++
Rust
Golang
Python ***
CUDA
Lua *
on-gpu CUDA *
mem-alloc Java **
Rust
Golang *
Python * ***
mem-inuse Rust
hbm-alloc CUDA *
hbm-inuse CUDA *
rdma C/C++ *

说明:

  • *:开发中的功能(尚未正式发布)
  • **:运行 Java 程序的 JVM 必须包含符号表,参考下文的 JVM 符号表检查
  • ***:当前支持版本为 Python 3.10
  • 语言分类:
    • 编译为 ELF 格式可执行文件的语言:Golang、Rust、C/C++
    • 使用 JVM 虚拟机的语言:Java
    • 解释型语言:Python

获取 Profiling 数据需满足两个前提条件:

  • 应用进程需要开启 Frame Pointer,或启用 Agent 的 DWARF 栈回溯能力
    • 应用进程开启 Frame Pointer(帧指针寄存器):
      • 编译 C/C++:gcc -fno-omit-frame-pointer
      • 编译 Rust:RUSTFLAGS="-C force-frame-pointers=yes"
      • 编译 Golang:默认开启,无需额外编译参数
      • 运行 Java:-XX:+PreserveFramePointer
        • 开启该参数会禁用某些编译器优化,不过根据 NetflixBrendan Gregg 的实测结果,通常只会引入 <1% 的性能损耗。Netflix 早在 2015 年起已在生产环境大规模使用,以支撑其 Java 程序的日常性能分析。
    • 启用 Agent 的 DWARF 栈回溯能力请参考文档
  • 对于编译型语言的应用进程,编译时需要注意保留符号表

JVM 符号表检查

# 1) 查找需要进行内存剖析的 Java 进程号,记为 $pid

# 2) 查看进程加载的 libjvm.so 所在位置,记为 $path
grep libjvm.so /proc/$pid/maps

# 3) 检查该文件是否包含符号表
readelf -WS $path | grep symtab

配置方式

配置前需要注意:deepflow-agent 的 ConfigMap 仅用于 agent 向 server 注册;而下方的 agent 自身配置需要通过 deepflow-ctl 下发。

注:CPU 剖析还有一些未出现在下方示例中的参数。如果你不确定修改后会产生什么影响,建议保持默认值。

inputs:
  proc:
    enabled: true
    process_matcher:
      # 功能列表,默认开关参考链接中的详细描述:
      # https://www.deepflow.io/docs/zh/configuration/agent/#inputs.proc.process_matcher.enabled_features
      - enabled_features:
          - ebpf.profile.on_cpu
          - proc.gprocess_info
        # Rust 正则规则:
        # https://regex101.com/
        match_regex: \bjava( +\S+)* +-jar +(\S*/)*([^ /]+\.jar)
        # 匹配形式,参考详细描述:
        # https://www.deepflow.io/docs/zh/configuration/agent/#inputs.proc.process_matcher
        match_type: cmdline_with_args
        # 是否仅匹配容器内的进程:
        # https://www.deepflow.io/docs/zh/configuration/agent/#inputs.proc.process_matcher.only_in_container
        only_in_container: false
        # 将匹配到的第三段 ([^ /]+\.jar) 内容作为名称(一般为 jar 包名)
        # 例如:shop-web-0.0.1-SNAPSHOT.jar
        rewrite_name: $3
      - enabled_features:
          - ebpf.profile.on_cpu
          - proc.gprocess_info
          - proc.socket_list
        match_regex: ^(cartservice|checkoutservice|shippingservice|coredns|mysqld|deepflow-server|deepflow-agent|stress-ng|cpu-demo)
        only_in_container: false

校验结果

  1. 查看当前的 Java Demo PID

    root@ce-demo-1:~# kubectl get pods -n deepflow-otel-spring-demo web-shop-7c48fd68dc-szchh -o wide
    NAME                        READY   STATUS    RESTARTS   AGE   IP               NODE        NOMINATED NODE   READINESS GATES
    web-shop-7c48fd68dc-szchh   1/1     Running   0          83d   10.244.228.165   ce-demo-2   <none>           <none>
    
    root@ce-demo-1:~# kubectl exec -n deepflow-otel-spring-demo web-shop-7c48fd68dc-szchh -c web-shop -- ps aux
    PID   USER     TIME  COMMAND
      1   root     0:00  sh /home/docker-entrypoint.sh -javaagent:/sidecar/agent/opentelemetry-javaagent.jar -Dotel.resource.attributes=service.name=shop-web -Dotel.traces.exporter=otlp -Dotel.metrics.exporter=none -jar /home/shop-web-0.0.1-SNAPSHOT.jar
      7   root     1d13  java -javaagent:/sidecar/agent/opentelemetry-javaagent.jar -Dotel.resource.attributes=service.name=shop-web -Dotel.traces.exporter=otlp -Dotel.metrics.exporter=none -jar /home/shop-web-0.0.1-SNAPSHOT.jar
    
    root@ce-demo-1:~# ssh ce-demo-2 'ps aux | grep shop-web-0.0.1-SNAPSHOT.jar'
    root     2684642  2.0  2.4 8348252 406656 ?      Sl    2025 2430:21 java -javaagent:/sidecar/agent/opentelemetry-javaagent.jar -Dotel.resource.attributes=service.name=shop-web -Dotel.traces.exporter=otlp -Dotel.metrics.exporter=none -jar /home/shop-web-0.0.1-SNAPSHOT.jar
    
  2. 在 ClickHouse 中通过上一步的 PID 查询 DeepFlow 标签信息

    root@ce-demo-1:~# kubectl exec -it -n deepflow deepflow-clickhouse-0 -c clickhouse -- bash
    root@deepflow-clickhouse-0:/# clickhouse client
    ClickHouse client version 23.10.4.25 (official build).
    Connecting to localhost:9000 as user default.
    Connected to ClickHouse server version 23.10.4 revision 54466.
    
    deepflow-clickhouse-0.deepflow-clickhouse-headless.deepflow.svc.cluster.local :)
    
    -- 通过 PID 查询对应进程在 DeepFlow 中的 tag 信息
    SELECT gprocess_id, app_service
    FROM `profile`.`in_process`
    WHERE process_id = 2684642
    LIMIT 1;
    
    ┌─gprocess_id─┬─app_service─┐
    │         123 │ java        │
    └─────────────┴─────────────┘
    
    -- 通过 gprocess_id 查询 rewrite_name 是否生效
    SELECT *
    FROM `flow_tag`.`gprocess_map`
    WHERE id = 123
    LIMIT 1;
    
    ┌──id─┬─name────────────────────────┬─icon_id─┬─chost_id─┬─l3_epc_id─┬─team_id─┬─domain_id─┬─sub_domain_id─┐
    │ 123 │ shop-web-0.0.1-SNAPSHOT.jar │       0 │        2 │         1 │       1 │         1 │             0 │
    └─────┴─────────────────────────────┴─────────┴──────────┴───────────┴─────────┴───────────┴───────────────┘
    
  3. 查看 Continuous Profiling Dashboard 中对应 Demo 的剖析效果

    img_v3_02uh_57e28e17-0461-40f0-80ff-04c08ff61a2g

    字段说明:

    • auto_service:举例说明,目前业务部署方式为 Deployment,此处依据 k8s svc name > deployment name 填写
  4. 函数类型对应关系

    Function Type 含义 Profile Event Type 特征
    O 对象类型 mem-* Memory Profile 的叶子节点
    H 云主机 * 等于 Total 的根节点
    P 进程 * [p] 开头,且不等于 Total 的根节点
    T 线程 * [t] 开头
    K 内核函数 * [k] 开头
    C CUDA 驱动函数 * [c] 开头
    L 动态链接库函数 * [l] 开头
    ? 未知函数 * 其他以 [ 开头
    A 应用函数 * 除以上之外的函数
  5. 通过 API 查询数据

    参考 DeepFlow 官方文档中 API 的使用方式。

posted @ 2026-02-03 05:07  怎么还在写代码  阅读(0)  评论(0)    收藏  举报