ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Grafana Alloy eBPF 自动注入剖析:在 Kubernetes 中无侵入采集 Pyroscope 性能画像

Grafana Alloy eBPF 自动注入剖析:在 Kubernetes 中无侵入采集 Pyroscope 性能画像 Grafana Alloy eBPF 自动注入剖析在 Kubernetes 中无侵入采集 Pyroscope 性能画像【免费下载链接】pyroscopeContinuous Profiling Platform. Debug performance issues down to a single line of code项目地址: https://gitcode.com/GitHub_Trending/py/pyroscope本篇技术指南以仓库中的 ebpf/kubernetes 示例 为骨架讲解如何用 Grafana Alloy 的discovery.kubernetes、discovery.relabel、pyroscope.ebpf、pyroscope.write四个组件在 Kubernetes 集群中实现对 Go、Python 等应用的免埋点auto-instrumentation持续剖析并把性能数据写入 Pyroscope 服务器、在 Grafana Profiles Drilldown 中可视化。读完本文你将能够从零部署一套完整的 Kubernetes eBPF 剖析环境理解 Alloy 配置中每条规则的语义并掌握pyroscope.ebpf组件全部核心参数与常见故障的排查方法。示例的整体架构四个组件一条链路该示例的核心理念是零代码改动应用无需引入任何 SDK 或埋点代码Grafana Alloy 直接以 eBPF 方式从内核侧采集进程栈。整个链路由四个 Alloy 组件串联而成见 README组件职责discovery.kubernetes发现 Kubernetes Pod产出候选目标targetsdiscovery.relabel检测并过滤目标进程、设置标签labelspyroscope.ebpf对指定应用启用 eBPF 剖析pyroscope.write把剖析数据写入远程端点Pyroscope 服务器数据流为discovery.kubernetes发现本节点 Pod →discovery.relabel重打标签并过滤出需要剖析的服务 →pyroscope.ebpf按目标采集栈 →pyroscope.write推送至 Pyroscope 服务本示例为http://pyroscope.pyroscope-ebpf.svc.cluster.local.:4040。仓库中的 配置参考文档 指出pyroscope.ebpf运行在宿主机上采集当前主机上运行的进程的栈踪迹targets可以来自discovery.kubernetes、discovery.docker、discovery.dockerswarm或discovery.process等发现组件而discovery.relabel则用来对发现的目标重打标签。eBPF 剖析的原理与适用边界eBPFenhanced Berkeley Packet Filter是内嵌于 Linux 内核的先进技术允许在内核空间安全运行沙箱化代码广泛应用于网络、安全与性能监控且无需修改内核代码或加载额外模块见 eBPF 概述文档。优势效率高、对应用性能开销极小能够动态向生产系统注入监控能力非常适合持续、实时的性能剖析。限制决定你是否应该选用本方案不适合剖析不受支持语言编写的应用无法剖析不运行在 Linux 上的应用不支持全部 profile 类型例如内存剖析、锁/阻塞剖析需要宿主机 root 权限在某些环境中可能受限。内核要求eBPF 剖析器要求 Linux 内核版本 4.9依赖BPF_PROG_TYPE_PERF_EVENT一种可挂载到硬件/软件事件上的 eBPF 程序类型。检查集群各节点内核版本kubectl get nodes -o jsonpath{range .items[*]}{.metadata.name}{\t}{.status.nodeInfo.kernelVersion}{\n}{end}支持的语言依据 supported-languages-ebpf.md原生编译语言C/C、Go、Rust、Zig。无需 frame pointers——剖析器直接使用.eh_frame数据做栈展开高层语言JavaHotspot JVM、.NET、Python、Ruby、PHP、Node.js、Perl每种语言可在pyroscope.ebpf组件配置中单独启用或禁用例如本示例中的python_enabled true。示例环境全景四个 Manifest 各司其职示例目录 ebpf/kubernetes 下共四个 YAML 文件构成一套完整闭环文件内容作用alloy.yamlNamespace RBAC DaemonSet ConfigMap部署 Grafana Alloy 采集器并注入完整 Alloy 配置pyroscope.yamlDeployment Service部署 Pyroscope 服务器端口 4040grafana.yamlDeployment Service Provisioning ConfigMap部署 Grafana预装 Profiles Drilldown 应用python-fast-slow.yamlConfigMap Deployment一个快慢函数对比的 Python 3.11 演示应用Grafana Alloy以 DaemonSet 形态覆盖每个节点eBPF 剖析是按节点工作的因此 alloy.yaml 使用 DaemonSet 在每个节点上各跑一个 Alloy 实例。三个关键部署细节RBACClusterRole授予 Pod 的get/list/watch权限——discovery.kubernetes组件需要它来发现 Pod源码注释明确写着 needed for the discovery.kubernetes alloy component并通过ClusterRoleBinding绑定到grafana-alloyServiceAccount。特权与 PID 命名空间容器以privileged: true、runAsUser: 0、runAsGroup: 0运行且 Pod 设置hostPID: true。配置参考文档明确指出必须以 root 身份并处于宿主 PID 命名空间内pyroscope.ebpf才能工作。节点自识别通过fieldRef: spec.nodeName把节点名注入HOSTNAME环境变量供 Alloy 配置中的env(HOSTNAME)使用从而让每个节点只剖析自己身上的 Pod。Pyroscope 服务器与 Grafanapyroscope.yaml 很简单一个grafana/pyroscope:latest的 Deployment容器端口 4040加一个同名 Service。Alloy 的pyroscope.write指向pyroscope.pyroscope-ebpf.svc.cluster.local.:4040。grafana.yaml 使用环境变量预装并启用 Profiles 相关插件GF_PLUGINS_PREINSTALL_SYNCgrafana-pyroscope-app预装 Pyroscope 应用插件GF_AUTH_ANONYMOUS_ENABLEDtrue与GF_AUTH_ANONYMOUS_ORG_ROLEAdmin免登录访问演示环境Provisioning ConfigMap 同时配置了数据源type: grafana-pyroscope-datasource指向http://pyroscope:4040和应用插件grafana-pyroscope-appbackendUrl: http://pyroscope:4040集群内即可开箱即用。演示应用python-fast-slowpython-fast-slow.yaml 通过 ConfigMap 注入一段 Python 脚本python:3.11镜像直接运行脚本中fast_function循环 2 万次、slow_function循环 8 万次交替执行。它被discovery.relabel的 keep 规则选中专门用来验证 eBPF 能否区分出快与慢两种热路径——这是验证剖析正确性的最直观用例。Alloy 配置逐段精解核心逻辑集中在 alloy.yaml 的 ConfigMapconfig.alloy中采用 Alloy 的 River 配置语言。逐段拆解如下。日志与节点级 Pod 发现logging { level debug format logfmt } discovery.kubernetes local_pods { selectors { field spec.nodeName env(HOSTNAME) // Note: this assume HOSTNAME is set to the node name role pod } role pod }discovery.kubernetes通过field选择器把发现范围收敛到当前节点spec.nodeNameHOSTNAME与 DaemonSet 的节点亲和设计呼应。调试期间将日志级别设为debug有助于观察目标发现情况。重打标签与目标过滤discovery.relabel specific_pods对发现结果做四类处理丢弃非运行中 Podaction dropsource_labels [__meta_kubernetes_pod_phase]regex Succeeded|Failed|Completed——只剖析运行中的 Pod语义化标签把__meta_kubernetes_namespace、__meta_kubernetes_pod_name、__meta_kubernetes_pod_node_name、__meta_kubernetes_pod_container_name分别映射为namespace、pod、node、container组装 service_name默认情况下service_name会被设为{namespace}/{container}示例通过两个 source label 拼接出namespace/container形式保证服务命名可读rule { action replace regex (.*)(.*) replacement ${1}/${2} separator source_labels [__meta_kubernetes_namespace, __meta_kubernetes_pod_container_name] target_label service_name }过滤剖析目标action keepregex (.*alloy|.*pyroscope|.*fast-slow)——只保留名称匹配 alloy、pyroscope、fast-slow 的服务。这也是控制剖析谁的推荐做法避免对集群中所有 Pod 做无差别剖析。eBPF 采集与数据写入pyroscope.ebpf instance { forward_to [pyroscope.write.endpoint.receiver] targets discovery.relabel.specific_pods.output python_enabled true } pyroscope.write endpoint { endpoint { url http://pyroscope.pyroscope-ebpf.svc.cluster.local.:4040 // url Grafana Cloud URL // basic_auth { // username Grafana Cloud User // password Grafana Cloud Password // } } }pyroscope.ebpf instance启用 eBPF 剖析。targets指向 relabel 输出python_enabled true开启 Python 支持本例演示应用正是 Pythonforward_to指向pyroscope.write组件的接收端。pyroscope.write endpoint定义写入端点。示例默认写入集群内的 Pyroscope 服务注释中预留了 Grafana Cloud 写法——若使用 Grafana Cloud取消注释basic_auth并填入用户名与密码即可配置参考 中给出了用环境变量GC_URL、GC_USER、GC_PASSWORD的等价写法。如果使用自建 Pyroscope 服务器可整体省略basic_auth。pyroscope.ebpf 组件配置参考在 配置参考文档 中pyroscope.ebpf的全部参数如下参数类型说明默认值必填targetslist(map(string))按容器 ID 分组的剖析目标列表—是forward_tolist(ProfilesReceiver)收集到的 profile 发送到的接收器列表—是collect_intervalduration采集 profile 的频率15s否sample_rateint每秒采集 profile 样本的次数97否pid_cache_sizeintpid → proc 符号表 LRU 缓存大小32否build_id_cache_sizeintELF build id → 符号表 LRU 缓存大小64否same_file_cache_sizeintELF 文件 → 符号表 LRU 缓存大小8否container_id_cache_sizeintpid → 容器 ID 表 LRU 缓存大小1024否collect_user_profilebool是否采集用户态 profiletrue否collect_kernel_profilebool是否采集内核态 profiletrue否demanglestringC demangle 模式none、simplified、templates、fullnone否自动注入的标签若未自定义以下标签会被自动注入采集结果用于定位剖析目标标签说明service_namePyroscope 服务名。尽量从发现元标签自动选择否则默认为unspecified__name__Pyroscope 指标名默认为process_cpu__container_id__从 target 推导出的容器 IDtargets 的特殊标签与匹配规则每个 target必须包含下列特殊标签之一用于把进程关联到被剖析的容器或进程__container_id__容器 ID__meta_docker_container_idDocker 容器 ID__meta_kubernetes_pod_container_idKubernetes Pod 容器 ID__process_pid__进程 ID。匹配逻辑从源码文档可确认若进程的容器 ID 命中某个 target 的容器 ID 标签则按容器 ID 聚合栈若进程 PID 命中 target 的 PID 标签则按 PID 聚合否则该进程不被剖析。service_name 的推断顺序特殊标签service_name必须始终存在。若未显式指定pyroscope.ebpf会依次尝试从以下来源推断__meta_kubernetes_pod_annotation_pyroscope_io_service_name即pyroscope.io/service_namePod 注解__meta_kubernetes_namespace__meta_kubernetes_pod_container_name__meta_docker_container_name__meta_dockerswarm_container_label_service_name与__meta_dockerswarm_service_name。全部失败则回退为unspecified。暴露的 Prometheus 指标pyroscope.ebpf组件自身暴露以下指标可用于观测采集健康状况pyroscope_fanout_latencyhistogram向直接/间接组件发送的写入延迟pyroscope_ebpf_active_targetsgauge组件跟踪的活动目标数pyroscope_ebpf_profiling_sessions_totalcounter已完成的剖析会话数pyroscope_ebpf_profiling_sessions_failing_totalcounter失败的剖析会话数pyroscope_ebpf_pprofs_totalcountereBPF 组件收集到的 pprof 数量。部署与验证完整操作步骤以下步骤完整继承自 README可直接照做。1. 准备本地集群使用 Kind 或类似工具搭建本地 Kubernetes 集群并确保 kubectl 可用。建议先按上文命令确认节点内核 4.9。2. 部署全部 Manifest在示例目录下执行kubectl apply -f alloy.yaml -f grafana.yaml -f pyroscope.yaml -f python-fast-slow.yaml依次会创建pyroscope-ebpf命名空间、Alloy 的 RBAC 与 DaemonSet、Pyroscope 服务器、Grafana以及 Python 演示应用。3. 端口转发访问 Grafanakubectl port-forward -n pyroscope-ebpf service/grafana 3000:30004. 打开 Profiles Drilldown浏览器访问http://localhost:3000/a/grafana-pyroscope-app/explore。部署就绪后Alloy 即通过pyroscope.ebpf组件剖析 Go 与 Python 应用。在 Drilldown 中按 profile 类型和服务选择python-fast-slow即可看到fast_function与slow_function的 CPU 占比差异直观验证剖析结果。另一种部署方式Helm仓库文档 setup-kubernetes.md 还提供 Helm 方式添加 Grafana Helm 仓库后用values.yaml注入 Alloy 配置核心是discovery.kubernetespyroscope.ebpfpyroscope.write三件套外加privileged安全上下文与hostPID: true再执行helm install pyroscope-ebpf grafana/alloy -f values.yaml。若目标是把数据发往 Grafana Cloud则需在pyroscope.write中配置 HTTP Basic 认证USERNAME/PASSWORD。容器内剖析的四个注意事项setup-kubernetes.md 专门提醒了在容器中剖析 Python 等应用时需注意四点内核版本宿主机内核须 4.9这是 eBPF 工作的前提容器特权剖析器需要访问内核特性的权限须以特权模式运行或调整安全上下文宿主 PID 命名空间hostPID必须设为true否则剖析器无法正确挂接进程网络访问容器需能访问 Pyroscope 服务器必要时配置网络策略或 ServiceAccount。常见问题排查故障排查文档 覆盖了实践中最高频的四类问题1. 出现 unknown symbols未知符号说明剖析器无法访问栈地址对应的 ELF 文件。常见原因进程已退出导致 ELF 不可访问ELF 损坏或不被识别/proc/pid/maps中没有对应条目。符号本身来自 ELF 的.symtab/.dynsym段、debug ELF 的对应段以及 Go ELF 的.gopclntab段debug 文件查找遵循 gdb 的 separate debug files 算法。2. 只能看到模块名、看不到函数名通常是二进制被 strip无.symtab/.dynsym/.gopclntab或 debug 文件缺失。可用objcopy分离调试信息后重新生成objcopy --only-keep-debug elf elf.debug strip elf -o elf.stripped objcopy --add-gnu-debuglinkelf.debug elf.stripped elf.debuglink系统库可安装调试符号例如 Ubuntu 下apt install libc6-dbg。3. 栈太浅通常只有 1-2 层二进制可能未启用 frame pointers 编译。编译时加上-fno-omit-frame-pointer标志不过 Go/Rust/C/C 场景下剖析器已能用.eh_frame展开此问题更多见于部分语言或旧二进制。4. Python 报pyperf get python process data failed: missing symbols pyRuntimeAddr autoTLSkeyAddr通常是 Python 库命名不符合标准如libpython3-custom.10.so.1.0剖析器期待libpython3.10.so.1.0这类标准命名。确保 Python 库遵循标准命名约定即可。另外对于 Ruby、JavaScript 这类 JIT 编译方法不在 ELF 文件中的解释型语言本实现会显示解释器函数名而非真实函数名剖析效果不理想建议优先用 Go、Python、Java、.NET 等受支持语言。小结本示例用最少的组件展示了一条完整的 Kubernetes eBPF 持续剖析链路discovery.kubernetes找目标 →discovery.relabel打标签过滤 →pyroscope.ebpf无侵入采集 →pyroscope.write上送 Pyroscope最后由 Grafana Profiles Drilldown 呈现。对生产环境而言结合 Helm 部署文档、配置参考 与 故障排查 三份文档即可把 eBPF 剖析推广到真实集群并对符号缺失、浅栈等问题做出准确诊断。【免费下载链接】pyroscopeContinuous Profiling Platform. Debug performance issues down to a single line of code项目地址: https://gitcode.com/GitHub_Trending/py/pyroscope创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表