搭建调用链路监控系统

现在微服务非常盛行的年代,在公司内,有各种各样的微服务相关的实践。今天我们不展开微服务具体是什么,只需要了解在一个互联网服务系统内部可能是由N多个子服务组成,每个子服务独立开发、部署、维护,通过API/RPC与其他服务通信,而不是直接共同操作相同的数据库、表来实现数据交互。在这样的架构之下,服务间的调用链路关系可能是这样的:

a -> b -> d -> e
a -> c -> d -> b -> f

以这个单向的调用为例,实际场景可能是更加复杂的网状形式。比如 a 是对用户提供服务,其内部的2个功能分别会有2个不同的调用链路。这时如果 a 检测到一个错误发生,那么怎么确定是 a 自身异常导致的,还是由于上游服务 b/d/e 异常导致的。如果不能很好的、快速的定位最根本的异常产生在哪里,那么势必会影响故障恢复时间,进而影响到服务的可用性。

那么,有这样的问题,如何解决呢?那就是调用链路监控系统该上台了。目前,有2个比较流行的开源系统 zipkin/ pinpoint . 看到“左耳朵耗子”推荐 zipkin,所以我们就直接使用zipkin,所以下面我们以zipkin实践为主题展开介绍。

zipkin 主要包括两部分功能,第一,提供调用日志收集接口,第二,提供展示界面。
zipkin 官网:https://zipkin.io/
zipkin GitHub: https://github.com/openzipkin/zipkin/
  
我们的web服务是PHP语言,我们希望记录、上报应用间的调用信息,并且是无侵入、异步高性能的。无侵入是指不修改现有应用程序代码,异步高性能是指不能因为记录、上报调用日志信息而影响主业务响应速度。zipkin.io 中提到了好多语言的实现,多数都是无侵入式的,这一点很好。我们使用php语言,因此就选择了Molten (https://github.com/chuan-yun/Molten) ,这个是一个PHP扩展,安装过程也比较简单,对应配置项在github中有说明,说明不是特别详细,酌情理解。它提供4中方式来记录、上报调用日志信息,第一,记录本地文件,第二,输出到标准输出,第三,输出到Syslog,第四,直接通过zipkin的接口上报。

第四种方式,当zipkin接口服务挂掉后,会直接影响正常业务访问,这种方式与zipkin形成了一种强依赖性,所以我们没采用。前三种方式其实都是记录日志,并不上报给 zipkin,需要我们自己想办法完成上报。为了更加简单、依赖更少,我们选择记录本地文件,也就是第一种方式。在实战过好多项目后,发现写本地文件还是最可靠的。

这里我们放下我们的 molten 相关的配置:
[molten]
extension=molten.so
molten.enable=1
molten.output_type=1
molten.sampling_type=1
molten.sampling_rate=64
molten.sampling_request=1000
molten.sink_type=1
molten.sink_log_path=/letv/logs/phpzipkin/
molten.span_format=zipkin
molten.report_interval=60
molten.report_limit=1000
sink_log_path 如果要指定目录,最后需要加上 / ,同时要确保这个目录权限可写。如果指定为文件前缀,最终生产的文件是这样的 {prefix}tracing-20180502.log ,所以要保证上级目录有写权限。

在同一台服务器上可能部署多个服务,因此服务名称需要再各个项目中指定
// 调用链监控分析,服务名称
ini_set('molten.service_name', 'odp');
当然,如果单台机器部署单个项目,可以把service_name 定义在php.ini中

接着就是要把这些日志上报给zipkin,基于我们已有的 filebeat+kafka+ELK架构(可以参看“搭建ELK日志监控平台那些事”),我们就直接集成到现有的架构上,形成这样的:
 
molten + filebeat + kafka + zipkin-collector + zipkin + elasticsearch

看上去这样是完美的解决方案,好像我们啥也不用做,并且也实现了我们最开始提到的2点要求。但是最终还是遇到了一些问题,好在都一一攻破了。

1. filebeat 要收集多种日志,该如何配置?

在同一台web服务器上我们需要收集的日志各种各样,在ELK架构下,这些日志最终会经过 logstash 分析处理,最终保存到 elasticsearch 中。那么调用链路日志信息是不需要经过logstash的,因为molten生成的格式就是zipkin所需要的,同时为了有更好的隔离,我们需要把这些不同的日志收集到Kafka的不同 Topic中。

我们通过2个input_log 段,添加一些自定义字段实现,fields.topic 这个定义了不同的 Topic 名称,之后通过在 output.kafka中配置这个变量即可。
filebeat.prospectors:
- input_type: log
  paths:
    - /letv/logs/odp.my.le.com.access.log
    - /letv/logs/benefit.my.le.com.access.log
    - /letv/logs/phpmessages
  fields:
    topic: odp_log
- input_type: log
  paths:
    - /letv/logs/phpzipkin/*
  fields:
    topic: lefan_tracing_log
tags: ["service-lefan", "web-tier"]
output.kafka:
  enabled: true
  hosts: ["10.183.97.52:9092", "10.183.97.62:9092", "10.183.97.63:9092"]
  topic: "%{[fields][topic]}"
logging.level: error

2. zipkin-collector 无法正确解析 filebeat 收集的调用日志信息 

filebeat 收集的所有日志信息,全部都会在外层加一个结构,然后把日志信息,放入 message 字段中。但是这样一来,经过包装的这个结构,最终给到 zipkin-collector 时就会出错。

这当中有想过通过 logstash 把这个日志信息从kafka取出,除去这层外衣后,再录入kafka 中,然后让 zipkin-collector 接收。但是这样一来,在流程中加入了 logstash,会让这个流程更加复杂,也需要在logstash中加入一些过滤规则。另外一个解决办法是,logstash 处理完之后,不会kafka,直接通过logstash的另外插件,上报给zipkin。

但是这两种方法,我们都没有采用。原因很简单:第一种方案,太绕,架构流程更加复杂了;第二种方案,需要安装一个新的 logstash 插件,其稳定性,可用性并没有得到验证,所以还是不能用。

最后,我们决定对zipkin-collector 这个收集数据的工具,进行小小的调整,能够解析filebeat增加的这层 json外衣,然后取其中的message字段即可。后来,在github提交了 pull request ,但是 zipkin 项目维护者感觉这不属于 zipkin 的职责,是数据输入前需要考虑的问题,因此就暂时还没有合并到 master。

github:https://github.com/WiFeng/zipkin
jar包:kafka10.zip 解压后,取出其中的kafka10.jar

有了这些之后,我们需要启动zipkin,并且需要加一些启动参数:
zipkin.sh
#!/bin/bash


zipkin=`ps aux|grep zipkin.jar|grep -v grep`


if [ -z "$zipkin" ]; then
    #java -jar zipkin.jar --logging.level.zipkin=DEBUG 2>&1 >/dev/null &


    export STORAGE_TYPE=elasticsearch
    export ES_HOSTS=http://10.183.97.33:9200,http://10.183.97.34:9200,http://10.183.97.51:9200
    export ES_INDEX=zipkin


    java \
    -Dloader.path='kafka10.jar,kafka10.jar!/lib' \
    -Dspring.profiles.active=kafka \
    -Dzipkin.collector.kafka.bootstrap-servers=10.183.97.52:9092,10.183.97.62:9092,10.183.97.63:9092 \
    -Dzipkin.collector.kafka.topic=lefan_tracing_log \
    -cp zipkin.jar \
    org.springframework.boot.loader.PropertiesLauncher \
    --logging.level.zipkin=ERROR 2>&1 >>/letv/logs/zipkin/zipkin.log &


    sleep 10
fi

在 zipkin.sh 的同目录下会一个 kafka10.jar 这个包,才可以启动从kafka获取日志信息。在这里使用上面修改过的包 kafka10.jar ,然后重新启动即可。

zipkin 的调用链路数据,我们最终保存在了 elasticsearch 中,以方便查看最近一段时间的数据。上面也提供了对应的配置项。把对应IP/PORT 修改即可。

启动就直接执行这个 zipkin.sh 就行。
[liuweifeng@dfs-zb20-node1 zipkin]$ cd /usr/local/zipkin/
[liuweifeng@dfs-zb20-node1 zipkin]$ ./zipkin.sh 

如果启动成功,我们可以访问其web界面,默认监听 9411 端口,例如:http://10.112.43.1:9411/zipkin/
最终我们的监控系统,输入侧都是 filebeat , web图形化系统分别为 zipkin / kibana ,中间组件都共用,非常清晰。

标签: zipkin, 监控

5
Jun 2018
AUTHOR WiFeng
CATEGORY Web
COMMENTS 5 Comments

已有 5 条评论 »

  1. silkcut silkcut

    哥们加个联系方式呗,咱们聊聊关于全链路监控这里的东西
    邮箱:silkcutbeta@gmail.com

  2. silkcut silkcut

    哥们加个联系方式呗,咱们聊聊关于全链路监控这里的东西
    邮箱:silkcutbeta@gmail.com

  3. golang golang

    用了你的kafka10
    报错无法启动大神

    Cannot load configuration class: zipkin.autoconfigure.collector.kafka10.ZipkinKafka10CollectorAutoConfiguration

    1. WiFeng WiFeng

      @golang
      有没有更加详细的错误信息?

  4. golang golang

    弟兄报错啊咋整
    Cannot load configuration class: zipkin.autoconfigure.collector.kafka10.ZipkinKafka10CollectorAutoConfiguration

添加新评论 »

   点击刷新验证码