用SkyWalking做分布式追踪和应用性能监控系统

2次阅读

共计 5403 个字符,预计需要花费 14 分钟才能阅读完成。

【转载请注明出处】:https://segmentfault.com/a/1190000023089382

SkyWalking 是观察性分析平台和应用性能管理系统。提供分布式追踪、服务网格遥测分析、度量聚合和可视化一体化解决方案。

特性:

  • 多种监控手段,语言探针和 service mesh
  • 多语言自动探针,Java,.NET Core 和 Node.JS
  • 轻量高效,不需要大数据
  • 模块化,UI、存储、集群管理多种机制可选
  • 支持告警
  • 优秀的可视化方案

Skywalking 技术架构

整个系统分为三部分:

  • agent:采集 tracing(调用链数据)和 metric(指标)信息并上报
  • OAP:收集 tracing 和 metric 信息通过 analysis core 模块将数据放入持久化容器中(ES,H2(内存数据库),mysql 等等),并进行二次统计和监控告警
  • webapp:前后端分离,前端负责呈现,并将查询请求封装为 graphQL 提交给后端,后端通过 ribbon 做负载均衡转发给 OAP 集群,再将查询结果渲染展示

Skywalking 也提供了其他的一些特性:

  • 配置重载:支持通过 jvm 参数覆写默认配置,支持动态配置管理
  • 集群管理:这个主要体现在 OAP,通过集群部署分担数据上报的流量压力和二次计算的计算压力,同时集群也可以通过配置切换角色,分别面向数据采集(collector)和计算(aggregator,alarm),需要注意的是 agent 目前不支持多 collector 负载均衡,而是随机从集群中选择一个实例进行数据上报
  • 支持 k8s 和 mesh
  • 支持数据容器的扩展,例如官方主推是 ES,通过扩展接口,也可以实现插件去 - – 支持其他的数据容器
  • 支持数据上报 receiver 的扩展,例如目前主要是支持 gRPC 接受 agent 的上报,但是也可以实现插件支持其他类型的数据上报(官方默认实现了对 Zipkin,telemetry 和 envoy 的支持)
  • 支持客户端采样和服务端采样,不过服务端采样最有意义
  • 官方制定了一个数据查询脚本规范:OAL(Observability Analysis Language),语法类似 Linq,以简化数据查询扩展的工作量
  • 支持监控预警,通过 OAL 获取数据指标和阈值进行对比来触发告警,支持 webhook 扩展告警方式,支持统计周期的自定义,以及告警静默防止重复告警
数据容器

由于 Skywalking 并没有自己定制的数据容器或者使用多种数据容器增加复杂度,而是主要使用 ElasticSearch(当然开源的基本上都是这样来保持简洁,例如 Pinpoint 也只使用了 HBase),所以数据容器的特性以及自己数据结构基本上就限制了业务的上限,以 ES 为例:

  • ES 查询功能异常强大,在数据筛选方面碾压其他所有容器,在数据筛选潜力巨大(Skywalking 默认的查询维度就比使用 HBase 的 Pinpoint 强很多)
  • 支持 sharding 分片和 replicas 数据备份,在高可用 / 高性能 / 大数据支持都非常好
  • 支持批量插入,高并发下的插入性能大大增强
  • 数据密度低,源于 ES 会提前构建大量的索引来优化搜索查询,这是查询功能强大和性能好的代价,但是链路跟踪往往有非常多的上下文需要记录,所以 Skywalking 把这些上下文二进制化然后通过 Base64 编码放入 data_binary 字段并且将字段标记为 not_analyzed 来避免进行预处理建立查询索引

总体来说,Skywalking 尽量使用 ES 在大数据和查询方面的优势,同时尽量减少 ES 数据密度低的劣势带来的影响,从目前来看,ES 在调用链跟踪方面是不二的数据容器,而在数据指标方面,ES 也能中规中矩的完成业务,虽然和时序数据库相比要弱一些,但在 PB 级以下的数据支持也不会有太大问题。

数据结构

如果说数据容器决定了上限,那么数据结构则决定了实际到达的高度。Skywalking 的数据结构主要为:

  • 数据维度(ES 索引为 skywalking_*_inventory)

    1. service:服务
    2. instance:实例
    3. endpoint:接口
    4. network_adress:外部依赖
  • 数据内容

    1. 原始数据

      • 调用链跟踪数据(调用链的 trace 信息,ES 索引为 skywalking_segment,Skywalking 主要的数据消耗都在这里)
      • 指标(主要是 jvm 或者 envoy 的运行时指标,例如 ES 索引 skywalking_instance_jvm_cpu)
    2. 二次统计指标

      • 指标(按维度 / 时间二次统计出来的例如 pxx、sla 等指标,例如 ES 索引 skywalking_database_access_p75_month)
      • 数据库慢查询记录(数据库索引:skywalking_top_n_database_statement)
    3. 关联关系(维度 / 指标之间的关联关系,ES 索引为 skywalking_relation)
    4. 特别记录

      • 告警信息(ES 索引为 skywalking_alarm_record)
      • 并发控制(ES 索引为 skywalking_register_lock)

其中数量占比最大的就是调用链跟踪数据和各种指标,而这些数据均可以通过 OAP 设置过期时间,以降低历史数据的对磁盘占用和查询效率的影响。

调用链跟踪数据

作为 Skywalking 的核心数据,调用链跟踪数据(skywalking_segment)基本上奠定了整个系统的基础,而如果要详细的了解调用链跟踪的话,就不得不提到 openTracing。

openTracing 基本上是目前开源调用链跟踪系统的一个事实标准,它制定了调用链跟踪的基本流程和基本的数据结构,同时也提供了各个语言的实现。如果用一张图来表现 openTracing,则是如下:

其中:

  • SpanContext:一个类似于 MDC(Slfj)或者 ThreadLocal 的组件,负责整个调用链数据采集过程中的上下文保持和传递
  • Trace:一次调用的完整记录

    • Span:一次调用中的某个节点 / 步骤,类似于一层堆栈信息,Trace 是由多个 Span 组成,Span 和 Span 之间也有父子或者并列的关系来标志这个节点 / 步骤在整个调用中的位置

      • Tag:节点 / 步骤中的关键信息
      • Log:节点 / 步骤中的详细记录,例如异常时的异常堆栈
    • Baggage:和 SpanContext 一样并不属于数据结构而是一种机制,主要用于跨 Span 或者跨实例的上下文传递,Baggage 的数据更多是用于运行时,而不会进行持久化

以一个 Trace 为例:

首先是外部请求调用 A,然后 A 依次同步调用了 B 和 C,而 B 被调用时会去同步调用 D,C 被调用的时候会依次同步调用 E 和 F,F 被调用的时候会通过异步调用 G,G 则会异步调用 H,最终完成一次调用。

上图是通过 Span 之间的依赖关系来表现一个 Trace,而在时间线上,则可以有如下的表达:

当然,如果是同步调用的话,父 Span 的时间占用是包括子 Span 的时间消耗的。

而落地到 Skywalking 中,我们以一条 skywalking_segment 的记录为例:

{
    "trace_id": "52.70.15530767312125341",
    "endpoint_name": "Mysql/JDBI/Connection/commit",
    "latency": 0,
    "end_time": 1553076731212,
    "endpoint_id": 96142,
    "service_instance_id": 52,
    "version": 2,
    "start_time": 1553076731212,
    "data_binary": "CgwKCjRGnPvp5eikyxsSXhD///////////8BGMz62NSZLSDM+tjUmS0wju8FQChQAVgBYCF6DgoHZGIudHlwZRIDc3FsehcKC2RiLmluc3RhbmNlEghyaXNrZGF0YXoOCgxkYi5zdGF0ZW1lbnQYAiA0",
    "service_id": 2,
    "time_bucket": 20190320181211,
    "is_error": 0,
    "segment_id": "52.70.15530767312125340"
}

其中:

  • trace_id:本次调用的唯一 id,通过 snowflake 模式生成
  • endpoint_name:被调用的接口
  • latency:耗时
  • end_time:结束时间戳
  • endpoint_id:被调用的接口的唯一 id
  • service_instance_id:被调用的实例的唯一 id
  • version:本数据结构的版本号
  • start_time:开始时间戳
  • data_binary:里面保存了本次调用的所有 Span 的数据,序列化并用 Base64 编码,不会进行分析和用于查询
  • service_id:服务的唯一 id
  • time_bucket:调用所处的时段
  • is_error:是否失败
  • segment_id:数据本身的唯一 id,类似于主键,通过 snowflake 模式生成

这里可以看到,目前 Skywalking 虽然相较于 Pinpoint 来说查询的维度要多一些,但是也很有限,而且除了 endPoint,并没有和业务有关联的字段,只能通过时间 / 服务 / 实例 / 接口 / 成功标志 / 耗时来进行非业务相关的查询,如果后续要增强业务相关的搜索查询的话,应该还需要增加一些用于保存动态内容(如 messageId,orderId 等业务关键字)的字段用于快速定位

指标

指标数据相对于 Tracing 则要简单得多了,一般来说就是指标标志、时间戳、指标值,而 Skywalking 中的指标有两种:一种是采集的原始指标值,例如 jvm 的各种运行时指标(例如 cpu 消耗、内存结构、GC 信息等);一种是各种二次统计指标(例如 tp 性能指标、SLA 等,当然也有为了便于查询的更高时间维度的指标,例如基于分钟、小时、天、周、月)

例如以下是索引 skywalking_endpoint_cpm_hour 中的一条记录,用于标志一个小时内某个接口的 cpm 指标:

{
    "total": 8900,
    "service_id": 5,
    "time_bucket": 2019031816,
    "service_instance_id": 5,
    "entity_id": "7",
    "value": 148
}

各个字段的释义如下:

  • total:一分钟内的调用总量
  • service_id:所属服务的唯一 id
  • time_bucket:统计的时段
  • service_instance_id:所属实例的唯一 id
  • entity_id:接口(endpoint)的唯一 id
  • value:cpm 的指标值(cpm=call per minute,即 total/60)
agent

agent(apm-sniffer)是 Skywalking 的 Java 探针实现,主要负责:

  • 采集应用实例的 jvm 指标
  • 通过切向编程进行数据埋点,采集调用链数据
  • 通过 RPC 将采集的数据上报

当然,agent 还实现了客户端采样,不过在 APM 监控系统里进行客户端数据采样都是没有灵魂的,所以这里就不再赘述了。

首先,agent 通过 org.apache.skywalking.apm.agent.core.boot.BootService 实现了整体的插件化,agent 启动会加载所有的 BootService 实现,并通过 ServiceManager 来管理这些插件的生命周期,采集 jvm 指标、gRPC 连接管理、调用链数据维护、数据上报 OAP 这些服务均是通过这种方式扩展。

然后,agent 还通过 bytebuddy 以 javaagent 的模式,通过字节码增强的机制来构造 AOP 环境,再提供 PluginDefine 的规范方便探针的开发,最终实现非侵入性的数据埋点,采集调用链数据。

OAP

同 agent 类似,OAP 作为 Skywalking 最核心的模块,也实现了自己的扩展机制,不过在这里叫做 Module,具体可以参考 library-module,在 module 的机制下,Skywalking 实现了自己必须核心组件:

  • core:整个 OAP 核心业务(remoting、cluster、storage、analysis、query、alarm)的规范和接口
  • cluster:集群管理的具体实现
  • storage:数据容器的具体实现
  • query:为前端提供的查询接口的具体实现
  • receiver:接收探针上报数据的接收器的具体实现
  • alarm:监控告警的具体实现

以及一个可选组件:

  • telemetry:用于监控 OAP 自身的健康状况

而前面提到的 OAP 的高扩展性则体现在核心业务的规范均定义在了 core 中,如果有需要自己扩展的,只需要自己单独做自己的实现,而不需要做侵入式的改动,最典型的示例则是官方支持的 storage,不仅支持单机 demo 的内存数据库 H2 和经典的 ES,连目前开源的 Tidb 都可以接入。

安装
  1. 下载最新的安装包
  2. 解压,并进入 bin 目录执行 startup.sh 启动
  3. 访问http://localhost:8080/ 即可看到面板
  4. 启动服务
    添加如下 VM 参数:
-javaagent:${agent_home}/agent/skywalking-agent.jar -Dskywalking.agent.service_name=${service_name}

【转载请注明出处】:https://segmentfault.com/a/1190000023089382

正文完
 0