全链路监控
方案调研/实现过程
一.全链路监控的背景
- 随着微服务的普及,服务按照不同的维度进行拆分,一次请求往往需要涉及到多个服务。互联网应用构建在不同的软件模块集上,这些软件模块,有可能是由不同的团队开发、可能使用不同的编程语言来实现、有可能布在了几千台服务器,横跨多个不同的数据中心。因此,就需要一些可以帮助理解系统行为、用于分析性能问题的工具,以便发生故障的时候,能够快速定位和解决问题。
2. 内存选型,首先排除内存,内存的存储量小且珍贵;NoSQL数据库储在内存中的直接不考虑,Pika的数据虽然落入磁盘但不支持复杂查询也不考虑,Cassandra支持复杂查询但缺少对海量数据分析运算的能力;ES适用于海量、简单稳定的数据,是调用链追踪方面的不二容器。
2.3.2 调用链监控组件
- 业界主流组件:
● Google:Dapper,可以通过论文以及文章窥探一二,未开源。
● Google:OpenCensus,Google开源的系统,采用的是Agent/Collector模式。
● 阿里:EagleEye,脱胎于Dapper论文,数据存储于HBase,未开源。
● 点评:Cat,基于Java开发,支持Client多语言,未开源,停止维护。
● 亚马逊:X-Ray, 与EC2结合使用。
● Istio:Envoy埋点,调用链信息导入到Zipkin或者Jaeger中进行展示。
● Twitter:Zipkin,出来比较早,广泛使用。
● Uber:Jaeger,后起之秀,应用广泛。
在以上各位大大百花齐放之时,渐渐形成了事实上的标准Opentracing。
| pinpoint | zipkin | jaeger | skywalking |
OpenTracing兼容 | 否 | 是 | 是 | 是 |
客户端支持语言 | Java/php | java,c#,go,php等 | Java/c#/go/php等 | Java/.NET/ NodeJS/PHP |
存储 | hbase | ES/mysql/Cassandra/内存 | ES/Cassandra/内存 | ES/H2/mysql/TIDB/sharding sphere |
传输协议支持 | thrift | http/MQ | udp/http | gRPC |
ui丰富程度 | 高 | 低 | 中 | 中 |
侵入性 | 字节码注入,无侵入 | 拦截请求,侵入 | 拦截请求,侵入 | 字节码注入,无侵入 |
扩展性 | 低 | 高 | 高 | 中 |
trace查询 | 不支持 | 支持 | 支持 | 支持 |
告警支持 | 支持 | 不支持 | 不支持 | 支持 |
jvm监控 | 支持 | 不支持 | 不支持 | 支持 |
性能损失 | 高 | 中 | 中 | 低 |
2.Jaeger 和Zipkin组件对比
| Zipkin(Twiter) | Jaeger(Uber) |
开发语言 | Java | Golang |
opentracing兼容 | 部分支持 | 完全支持 |
支持语言 | C/C++/Java/Javascript/Python/Scala,/Go等
| C++/Java/Go/Node.js/Python等
|
数据传输方式 | http/MQ/scribe | udp/http(grpc)/MQ |
存储 | ES/mysql/Cassandra/内存 | ES/Cassandra/内存
|
查询 | trace查询 | trace查询 |
告警支持 | 不支持 | 不支持 |
UI丰富程度 | 中 | 高 |
查询界面 |
|
|
架构 |
|
|
C++/Client仓库 | https://github.com/openzipkin/b3-propagation
| https://github.com/jaegertracing/jaeger-client-cpp
|
3.Jaeger与Zipkin
- 语言支持
| Zipkin | Jaeger
|
C++ | 非官方 | 官方 |
Go | 官方 | 官方 |
C# | 官方 | 官方 |
Java | 官方 | 官方 |
Python | 非官方 | 官方 |
Ruby | 官方 | 非官方 |
Node.js | 官方 | 官方 |
PHP | 非官方 | 非官方 |
即使是官方维护的库也不敢保证百分百完美支持组件,如果选择非官方维护的版本一定要小心。而内部使用较多的C++和Golang,Jaeger都支持的很好。
- 架构区别
- 相同:两者架构方面很像,都有埋点、收集、上报的过程。collector向DB中写入数据,同时提供带有完整UI的Trace查询系统,也都支持ES和Cassandra。两者都可以选择Kafka异步写入数据,在高吞吐和高可用方面无需担心,正因为支持MQ方式,使得数据一次上报、多处消费。
- 不同:Jaeger被选入CNCF计划,因此Kubernetes是首选的部署平台。提供了用于部署收集,收集器,查询API和UI的镜像,当然也可通过二进制的可执行文件部署。现在Envoy原生支持Jaeger,可以更轻松地跨容器跟踪调用。Zipkin也提供了Docker和Java程序,而且收集,收集器,查询API和UI都打包在一起,部署比Jaeger简单的多。实际上Jaeger是分布式系统,这需要维护不同的集群;Zipkin和Jaeger都建议使用Elasticsearch作为数据存储,会比Cassandra更适合数据收集,也可以给Prometheus提供指标进行监控。总的来说Jaeger在17年首次公布,Zipkin更早一些在12年就已经推向社区,所以Zipkin的使用会更多一些,Jaeger有后发优势。
Jaeger架构
Zipkin架构
2.3.3 方案
| 方案一 | 方案二 | 方案三 |
描述 | 在envoy中打造基于Opentracing规范的全链路监控。在envoy中埋点上报,在ES中聚合查询。 | 复用envoy原生的链路追踪能力,配合业内主流的Jaeger或Zipkin插件(推荐Jaeger,界面展示更完善)。 | 打造trace组件,在函数里面埋点上报给Jaeger或Zipkin组件。 |
优点 | 无侵入,支持私有协议链路追踪,可维护性和定制性会更高。 | 无侵入,原生支持采样、染色,性能损耗低,符合opentracing标准。 | 原生支持采样、染色,性能损耗低,符合opentracing标准。 |
缺点 | 虽然可以引用Opentracing开源库埋点,但染色、采样、error上报等需要自己打造,UI界面需要和前端同学对接。 | 原生支持HTTP协议,私有协议暂不支持,需要打通。 | 侵入,推动阻力大,无法保证埋点准确性。 |
方案二:
1.envoy原生支持如下trace特性:
- 生成Request Id,填充HTTP的header字段x-request-id
- 外部跟踪服务集成,如支持LightStep, Zipkin或任何Zipkin兼容后端(如Jaeger)
- 添加Client trace ID。
Envoy分布式监控逻辑:
2.Envoy内部都做了什么:
- Inbound 流量:对于经过 Sidecar 流入应用程序的流量,如果经过 Sidecar 时 Header 中没有任何跟踪相关的信息,则会在创建一个根 Span,TraceId 就是这个 SpanId,然后再将请求传递给业务容器的服务;如果请求中包含 Trace 相关的信息,则 Sidecar 从中提取 Trace 的上下文信息并发给应用程序。
- Outbound 流量:对于经过 Sidecar 流出的流量,如果经过 Sidecar 时 Header 中没有任何跟踪相关的信息,则会创建根 Span,并将该跟 Span 相关上下文信息放在请求头中传递给下一个调用的服务;当存在 Trace 信息时,Sidecar 从 Header 中提取 Span 相关信息,并基于这个 Span 创建子 Span,并将新的 Span 信息加在请求头中传递。
- 默认由Envoy上报trace,可以经过Mixer上报。
3.Envoy源码中具体上报流程:
- B3头:
在envoy的源码中我们可以看到,基于zipkin的采集所需要的字段:
Header | 长度 | 用途 | 示例 |
x-b3-traceid
| 64-bit
| 链路唯一ID | 463ac35c9f6413ad48485a3953bb6124
|
x-b3-spanid | 64-bit | 确定链路调用关系 | a2fb4a1d1a96d312
|
x-b3-parentspanid
| 64-bit | 和x-b3-spanid一起确定链路调用关系 | 0020000000000001
|
x-b3-sampled | 0/1 | 采样 | 1 |
x-b3-flags | 0/1 | 染色 | 1 |
注:b3头标准
- B3头解析:
从上下文中解析出B3头,从这也可以看到B3头都是用uint64_t存储。
- envoy埋点:
先从上下文中获取B3头,如果没有获取到创建root span;如果获取到了创建chilrd span。
- span上报:
在流对象销毁的时候会调用finalizeDownstreamSpan()函数,finalizeDownstreamSpan调用了finishSpan()上报span。
4.业务端为什么需要改动?
- 业务代码处理请求的时候解析Header里面的字段,发出请求的时候再带上传给envoy。(如果没有这一步,envoy会认为这是一条新的请求线,会重新创建spanId、TraceId。)
- 这个服务如果是叶子服务就不需要做任何的操作;如果这个服务还需要调用其他服务,则必须转发请求追踪头。
5.整体方案架构
- 基于Envoy内部原生的zipkin-client作探针上报trace。
- Jaeger-collection收集trace信息,写入ES。
- ES集群存储Jaeger收集的链路数据(支持的ES版本:5.x,6.x,7.x)。
- 最后通过Jeger-query查询展示链路追踪;Kibana查询ES中具体的数据。
6.Jaeger提供的二进制可执行文件