写点什么

面向 Java 应用网络流的非侵入可观测指标采集联合方案 – Sermant & Gopher

作者:华为云开源
  • 2024-01-03
    广东
  • 本文字数:6999 字

    阅读完需:约 23 分钟

面向Java应用网络流的非侵入可观测指标采集联合方案 – Sermant & Gopher

 作者:杨奕 华为云技术规划专家 | 殷森道 华为云高级软件工程师 | 张豪鹏 华为云高级软件工程师

摘要

随着 2022 年来 eBPF 的技术大火,该技术以其非侵入的优点在可观测领域开始大放异彩。我们基于 eBPF 技术也做了许多实践,总的来看,eBPF 在网络运维的四层网络客观性方面具备得天独厚的优势,然而在七层(应用流)监控领域仍有力不从心的“死角”,比如对 Java 应用的观测。我们基于这个场景,结合 gala-gopher 和 Sermant 两大利器,通过 eBPF 对四层的观测,以及 Sermant 对 Java 应用七层的观测能力,互相补充网络、应用两个层面的运维能力,真正做到了应用侧/网络侧问题的快速界定。本文亦给出了一个实践案例来呈现效果。

一、 背景

随着 2022 年来 eBPF 的技术大火,该技术以其非侵入的优点在可观测领域开始大放异彩。eBPF 是一个能够在内核中运行沙箱程序的技术,提供安全的注入代码的机制,通过注入 eBPF 代码可以安全、高性能地访问整个系统的运行状态。其在 OS 内核中运行有一个验证器(eBPF Verifier),对注入内核的 eBPF 代码进行严格把关,保证不侵害内核安全。并且提供了 JIT 即时编译器,对注入内核的 eBPF 代码进行即时编译运行。对用户态提供 eBPF MAP 机制做数据交互。同时,eBPF 在用户空间提供了丰富的 SDK,支持 C/C++、Golang、Rust 等编程语言的接口,使不同领域的用户都能使用这门技术,在网络、安全、可观测性和追踪领域都有所建树。eBPF 技术全景图如下图[1]所示。


图 1-1 eBPF 技术架构及其生态

eBPF 由于其可通过内核埋入观测点的技术原理,在面向网络流的应用监控方面有着以下天然优势:

1) 面向 4 层的网络监控,其本身具备对内核“编程”的能力,天然可在内核协议栈中添加观测点;

2) 对于一些 7 层的网络协议,可简单通过捕获原始报文,在监控程序处进行协议解码,还原业务信息,并进行 metrics 统计。

但是针对一些比较复杂的 7 层协议,用 eBPF 做监控就比较繁琐,甚至力不从心。设想以下几个典型场景:

1) 应用流之间的加密访问场景,比如 Java 应用默认使用 JDK 中的 JSSE,OS 上的 OpenSSL,GoSSL,RustSSL 等;

2) 复杂 RPC 协议,如 gRPC, Dubbo 3.0 等场景下容易导致采集的数据(非业务面数据)丢失。

为了解决以上问题,业界的通用做法是采用 eBPF + uprobe 方案,去截取带有业务语义的应用层数据。例如,Deepflow、Pixie 针对 gRPC 协议数据采集的做法。

但是基于 uprobe 的做法同样缺点很明显。主要体现在:

1) 实现比较复杂,要基于三方库协议一个个适配,还要找准 uprobe 的观测点;

2) 版本兼容难度高,业务层如果有函数改动,则需要重新适配;

3) 如果以上两点开发者还算都能克服,第三点则算是硬伤了,就是针对国内政企行业面向业务开发使用最主流的编程语言——Java,暂时没有比较好的解决方案。

所谓白猫、黑猫,抓到老鼠就是好猫。对于面向应用流的可观测技术,只要能做到非侵入就是好技术。在 Java 领域,很自然地,有一个技术能补充 Java 应用场景的可观测性能力,那就是基于字节码增强技术的 JavaAgent 方案。

这个方案其实在 Java 领域已经相对比较成熟了,其核心就是利用 JavaAgent 通过运行时的字节码增强,在 Java 函数埋入可观测点。在业界也有很多优秀的开源实践,如 Skywalking, OpenTelemetry 等。


图 1-2 JavaAgent 技术生态

与 eBPF 相比,JavaAgent 对 Java 的 L7 业务层的观测具备更加完备的能力的和优势,同时亦能收到更好的监控效果。但是针对在内核层才能观测到的 4 层网络指标,JavaAgent 则没有太好的解决方案。然而,一个完整应用流的监控产品,不仅需要 L7 指标,同时也需要 L4 指标。这样当业务出现问题时,才能第一时间定界到底是应用侧的问题 (L7 异常,L4 正常), 还是网络侧的问题 (L7, L4 均异常)。

为此,针对应用流的可观测场景,能否将 JavaAgent 和 eBPF 二者相结合,就成了解决问题的关键。以下则是我们解决这个问题做的一次实践——结合 Sermant + gala-gopher 的数据采集方案。

二、 Sermant + gala-gopher 的数据采集方案

2.1 gala-gopher

gala-gopher 是一款 C/S 架构、基于 AI 的操作系统亚健康诊断工具。其基于 eBPF + Java Agent 无侵入观测技术,并以 AI 技术辅助,实现亚健康故障(比如性能抖动、错误率提升、系统卡顿等问题现象)分钟级诊断,简化 IT 基础设施的运维过程。架构图如下图[2]所示:


图 2-1 gala 技术框架

gala 的整个架构由 gala-gopher、gala-ops 两个软件组成,gala-ops 安装在管理节点内, gala-gopher 安装在生产节点内。

gala-gopher 是 gala 项目内负责数据采集的组件,其为 gala 项目提供 Metrics、Event、Perf 等数据,便于 gala 项目完成系统拓扑的绘制和故障根因的定位。其是一款结合 eBPF、Java Agent 等非侵入可观测技术的观测平台,探针是 gala-gopher 用于观测和采集数据的主要工具,通过探针式架构 gala-gopher 可以轻松实现增加、减少探针。gala-gopher 应用 drill-down 观测,将 Linux 操作系统层、基础设施层、容器、应用、业务等多层次采集到的数据结合起来,避免形成数据孤岛,从而达到更好的可观测效果。其覆盖了业界主流的开源应用协议的观测,使用非侵入式、零修改的能力使得用户能够安全、便捷地使用,并且基于插件化的架构,也能让用户可以定制化自己的可观测探针,如下图所示:


图 2-2 gala-gopher 的能力框架


图 2-3 gala-gopher 能力架构及支持场景

gala-gopher 主要包括探针框架和探针程序两部分,探针有 Native Probe、Extend Probe 两类。Native Probe 只能使用 C 语言实现,Extend Probe 不限定编程语言。



图 2-4 gala-gopher 技术框架

如图所示[3],gala-gopher 框架进程负责探针的生命周期管理(探针启停、保活等),收集并处理探针采集到的数据,并提供 kafka、prometheus 等输出方式。

框架进程中,API 模块提供了用户动态配置接口,用户可以自定义开启的探针及每个探针的监控范围;probe-mng 模块负责探针的声明周期管理和调度;ipc 模块负责与每个探针交互,动态下发监控配置等;ingress 负责接收探针,并缓存到 imdb 模块中;imdb 模块负责数据缓存处理;egress 负责将结构化数据通过 kafka、prometheus 等接口形式输出。

而探针分为 native probes 和 extend probes,native probe 内嵌于 gala-gopher 框架进程中,非独立进程运行,且不支持扩展;extend probes 则是扩展探针,其以独立的子进程形式运行,在 gala-gopher 框架下可以自定义扩展。

整体的运行流程为:用户通过访问 gala-gopher 框架进程提供的动态配置 API,向 gopher 下发探针配置及启动命令;框架进程的 probe-mng 模块处理配置信息,并通过 IPC 机制向探针进程下发配置参数等信息,探针根据这些信息动态调整采集行为和监控范围等,最后将采集到的数据通过上报 PIPE 文件的方式传递给框架进程的 ingress。ingress 将数据结构化后缓存到 imdb 中,最终由 egress 统一输出。

2.1.1 gala-gopher 探针扩展机制开发框架

1) 在 gopher 探针框架中新增扩展的探针类型、配置 api、探针子功能(采集子项)

2) 定义 meta 文件,用于探针框架与探针之间的数据上报的协议约束

3) 编写扩展探针程序

    a) 与探针框架建立 IPC 通道,获取 IPC 消息,并使能配置

    b) 完成自身探针的采集功能

    c) 通过 PIPE 将采集到的数据上送给探针框架

4) 如果探针涉及编译,需要提供 build.sh 脚本负责本探针的编译(python 探针无需提供)

2.2 Sermant

Sermant 是基于 Java 字节码增强技术的无代理的服务网格技术。其利用 JavaAgent 为宿主应用程序提供增强的服务治理功能,以解决大规模微服务场景中的服务治理问题。Sermant 架构如下所示:


图 2-5 Sermant 架构

Sermant 中 JavaAgent 主要包含两层功能,框架核心层和插件服务层。框架核心层提供 Sermant 的基本框架功能,以简化插件开发,包括心跳、数据传输、动态配置等。插件服务层为宿主应用提供实际的治理服务。

2.2.1 Sermant 的动态插件机制

Sermant 的动态插件机制是指 Sermant 可以在宿主服务运行过程中进行插件的安装和卸载。Sermant 支持通过指令的方式进行插件的动态安装和卸载,如下图所示:



图 2-6 Sermant 动态插件机制

Sermant 的动态插件机制主要基于 Java Agent 的 agentmain 模式实现,agentmain 是 Java Agent 的入口方法,它可以在 Java 应用程序启动后动态地加载并运行 Java Agent。和 premain 模式不同,agentmain 模式不能通过添加启动参数的方式来连接 agent 和主程序,需要使用 VirtualMachine 的 attach 机制来将 agent 加载到主程序中, 使用者可以通过 Attach 参数下发指令来控制 Sermant 的安装和卸载生命周期。如下所示:



图 2-7 Sermant 动态插件机制实现方式

2.3 Sermant 和 gala-gopher 的联合方案

Sermant 和 gala-gopher 的联合方案主要是通过 gala-gopher 的探针扩展机制,将 Sermant 作为 gala-gopher 的探针的一部分来进行指标采集和上报,架构图如下所示:


图 2-8 gala-gopher 和 Sermant 联合方案的技术架构

从架构图可以看出,Sermant 通过 JavaAgent 技术采集主程序的指标信息,并通过 Java Agent 技术来解决针对复杂协议 eBPF 无法进行协议解码、还原业务信息并进行 metrics 统计的问题。

在扩展探针中,Sermant Probe 会接受到 gala-gopher 下发的配置,Sermant Probe 接收到 gala-gopher 下发的配置之后,会通过 IPC 进程间通信机制和 Sermant 的动态安装机制将 Sermant 安装到 Java 主程序上,Sermant 安装到 Java 主程序上之后会自动开始采集 Java 主程序的指标信息。当 Sermant Probe 接收到 Sermant 采集的指标信息之后,会根据 Meta 文件将采集的指标信息解析并通过上报 PIPE 文件的方式传递给框架进程的 ingress。ingress 将数据结构化后缓存到 imdb 中,最终由 egress 统一输出。

三、联合方案使用案例

以上方案为了引入 Sermant 对 Java 应用的 Dubbo 协议调用性能指标采集的能力,在 gala-gopher 的基础上扩展了 Serment Probe 探针。本案例中,分别部署了 Dubbo 协议的客户端和服务端,作为受观测对象,以此来验证本方案的效果。

3.1 操作指南

3.1.1 编译 docker 镜像

1) 取包

Sermant 取包地址:https://github.com/huaweicloud/Sermant/releases/download/v-cooperate-with-gala-gopher-alpha/sermant--cooperate-with-gala-gopher-alpha.tar.gz


图 3-1 Sermant 取包位置

gopher 编译出包或 release 取包指南:https://gitee.com/openeuler/gala-gopher/tree/dev/#rpm%E6%96%B9%E5%BC%8F%E9%83%A8%E7%BD%B2

2) 构建 Docker 容器镜像,并手动分发到所有 K8S 负载节点上。

    a) 以上步骤得到 Sermant 软件包及 gala-gopher 的 RPM 包,将其放在同一个目录下。

    b) 解压 Sermant 压缩包,将其中的 agent 整个目录放入当前目录的 sermant/目录下。

    c) 从 gala-gopher 源码中取出 entrypoint.sh 脚本,放到当前目录下,内容如下:

[root@master gopher-sermant-docker]# ll  总用量 952  -rw-r--r--. 1 root root   1121 12月 25 19:54 Dockerfile  -rw-r--r--. 1 root root   2188 12月 25 19:54 entrypoint.sh  -rw-r--r--. 1 root root 961605 12月 25 19:54 gala-gopher-1.0.2-3.x86_64.rpm  drwxr-xr-x. 3 root root   4096 12月 25 19:54 sermant  
复制代码

d) 编写 Dockerfile,将上述两个软件打包到同一个 Docker 镜像中。

# 基础镜像使用OpenEuler-22.03-LTS-SP1  FROM hub.oepkgs.net/openeuler/openeuler_x86_64:22.03-lts-sp1  MAINTAINER GALA  # 指定容器工作目录  WORKDIR /gala-gopher  COPY gala-gopher-1.0.2-3.x86_64.rpm .  # 将当前宿主机目录(gopher的rpm包和Sermant软件包所在目录)下的所有内容都添加到容器的/gala-gopher/目录下  ADD . /gala-gopher  # 赋予entrypoint.sh脚本执行权限  RUN chmod +x /entrypoint.sh  # 在容器中安装gopher  RUN yum localinstall -y gala-gopher-1.0.2-3.x86_64.rpm  # 将Sermant拷贝到容器的/opt/目录下  COPY sermant/ /opt/sermant  # 启动容器进程入口,启动容器  ENTRYPOINT [ "/entrypoint.sh" ]  CMD [ "/usr/bin/gala-gopher" ]
复制代码

e) 构建 Docker 镜像,并打上 tag 为 gala-gopher:1.0.2。

1. $ docker build -f Dockerfile -t gala-gopher:1.0.2 .  

f) Docker 镜像导出到指定目录(本例导出到/tmp/目录下),image_id 为上一步构建好的 docker 镜像的 id。

 $ docker save {image_id}> /tmp/gala-gopher.docker.tar  
复制代码

g) gDocker 镜像导入所有 K8S 负载节点(Slave),并打上 tag,image_id 为镜像 id。

 $ docker load -i /tmp/gala-gopher.docker.tar   $ docker tag {image_id} gala-gopher:1.0.2  
复制代码

3) 运行 gopher + Sermant 容器并打开四七层探针。

# 使用docker run命令启动容器,以host模式运行,并开启特权模式  $ docker run -d --name gala-gopher --privileged --pid=host --network=host -v /:/host -v /etc/localtime:/etc/localtime:ro -v /sys:/sys -v /usr/lib/debug:/usr/lib/debug -v /var/lib/docker:/var/lib/docker -e GOPHER_HOST_PATH=/host gala-gopher:1.0.2  # 在本机节点上访问gopher的动态配置接口,以开启相关探针(baseinfo、tcpprobe、endpoint、l7probe、Sermant_probe)  $ curl -X PUT http://localhost:9999/baseinfo --data-urlencode json='{"cmd":{"probe":["cpu","mem","nic","disk","net","fs","proc","host"]},"snoopers":{"proc_name":[{"comm":"java","cmdline":"dubbo"}]},"state":"running","params":{"report_period":60}}'  $ curl -X PUT http://localhost:9999/tcp --data-urlencode json='{"cmd":{"probe":["tcp_abnormal","tcp_rtt","tcp_windows","tcp_rate","tcp_rtt","tcp_sockbuf","tcp_stats"]},"snoopers":{"proc_name":[{"comm":"java","cmdline":"dubbo"}]},"state":"running","params":{"report_period":60,"l7_protocol":["http"]}}'  $ curl -X PUT http://localhost:9999/socket --data-urlencode json='{"cmd":{"probe":["tcp_socket"]},"snoopers":{"proc_name":[{"comm":"java","cmdline":"dubbo"}]},"state":"running","params":{"report_period":60,"l7_protocol":["http"]}}'  $ curl -X PUT http://localhost:9999/sermant --data-urlencode json='{"cmd":{"probe":["l7_bytes_metrics","l7_rpc_metrics","l7_rpc_trace"]},"snoopers":{"proc_name":[{"name":"sermantTest","comm":"java","cmdline":"dubbo"}]},"state":"running","params":{"report_period":60,"l7_protocol":["http"]}}'
复制代码

4) 在 Prometheus 上配置定期拉取数据的任务。

$ vim /etc/prometheus/prometheus.yml  # 在scrape_configs下添加如下内容,8888端口为gopher的数据端口,可从该端口获取指标数据,{K8S-slave-1_ip}为slave-1节点的ip,slave-2同理  	  - job_name: "k8s-Slave1-{K8S-slave-1_ip}"  	    static_configs:  	      - targets: ["{K8S-slave-1_ip}:8888"]  	  - job_name: "k8s-Slave2-{K8S-slave-2_ip}"  	    static_configs:  	      - targets: ["{K8S-slave-1_ip}:8888"] 
复制代码

5) 在 Grafana 界面上配置好各个指标的 panel 以呈现。

3.2 验证实验

为了验证以上效果,本案例设计了 Dubbo 应用客户端到服务端的调用链,将其部署在 K8S 上,并开启一个定期打流的任务,定时给 consumer-dubbo 服务打流,触发调用链。本案例实验的调用链应用以及 gala-gopher、Sermant 的部署形态如下图所示:



图 3-2 实验案例部署视图

在 consumer-dubbo、provider-dubbo 以及 gala-gopher 都已正常运行时,访问 gala-gopher 的动态配置接口下发观测配置,即告知 gopher 要开启 Sermant probe 来观测 dubbo 应用指标,此时可以观察到在 gopher 的 docker 容器内开启了 Sermant probe 探针子进程。随即 Sermant probe 会将 sermant 的 Java Agent 通过动态 attach 的方式注入到指定的 dubbo 应用进程中,从而 Sermant 的 java-agent 不停地采集应用调用产生的指标数据,并即时地上报给 Sermant Probe。Sermant Probe 再转而上报给 gala-gopher 主进程进行“临时归档”。

本案例采用 prometheus+grafana 的方式进行呈现,在 prometheus 配置了从每个 gala-gopher 实例处定时拉取指标数据的策略,并在 Grafana 界面上配置了 Dubbo 协议指标相关的 panel 进行呈现。

3.3 实验效果

实验效果如下图 3-3 ~ 图 3-6 所示,在 Grafana 界面上呈现了当前实践案例中 Dubbo 客户端、服务端的时延、吞吐量、错误率等性能指标的变化曲线。



图 3-3 Dubbo 客户端/服务端请求/响应吞吐量、错误率变化曲线



图 3-4 Dubbo 客户端/服务端请求/响应吞吐量、错误率变化曲线



图 3-5 TCP 层观测指标变化曲线(时延、丢包、OOM、未处理字节数)



图 3-6 TCP 层观测指标变化曲线(重传、丢包、收发(错误)字节数)

3.4 实验总结

我们通过在 gala-gopher 的基础上扩展了 Sermant_probe 的能力,通过 Sermant 探针采集 dubbo 应用的性能指标,并反馈给 gala-gopher 进行统一上报。在 prometheus+grafana 的可视化框架下呈现 dubbo 协议调用以及网络 tcp 层面相关的指标变化,可以让用户在界面上直观地观测到四层(网络视角)/七层(应用视角)两个层面的性能指标变化,从而提升运维效率。

 

参考资料:

[1] eBPF 官方文档: https://ebpf.io/what-is-ebpf/

[2] gala 项目社区官方文档:https://gitee.com/openeuler/gala-docs/tree/master

[3] gopher 探针开发指南:https://gitee.com/openeuler/gala-gopher/blob/dev/doc/how_to_add_probe.md#%E5%BC%80%E5%8F%91%E6%8E%A2%E9%92%88%E5%8A%9F%E8%83%BD


Sermant 作为专注于服务治理领域的字节码增强框架,致力于提供高性能、可扩展、易接入、功能丰富的服务治理体验,并会在每个版本中做好性能、功能、体验的看护,广泛欢迎大家的加入。



用户头像

华为云开源官方博客--携手共建云原生根社区 2023-03-13 加入

还未添加个人简介

评论

发布
暂无评论
面向Java应用网络流的非侵入可观测指标采集联合方案 – Sermant & Gopher_微服务_华为云开源_InfoQ写作社区