链路监控探索实践二 初步试验与先行探雷篇

   原创文章,转载请注明出处。由于本人水平有限,文中错漏之处在所难免,希望大家多多批评指正。本文的内容过多,我们分成四篇聊,此为第二篇。

   通过前面的分析与对比,我们最终选择了SkyWalking(下文简称SW)来做链路监控。考虑到SW是开源的系统,从以往的经历来看,开源的并且达到了系统级别的,一般都是有雷的,不注意的话偷偷爆个雷,所有关联的系统可能都会受到影响,严重的时候会让你很后悔使用了这个系统。而如果使用的是分布式追踪系统的话,在暴雷时会更加的严重,因为它不是独立部署的,是要集成到每一个系统实例的容器中,出问题的时候会直接牵连到容器内的其他服务。所以本着小心求证的原则,在正式上线使用之前,我们先在测试环境简单部署一套SW,初步尝试下系统接入,既有助于对数据体量、集群配置和工作人力的预估,也可以探探雷,提前发现问题,实际验证下是否适合我们,方便我们后续工作的风险把控。整个部署过程我们使用MacOS Mojave操作系统进行演示,可能与Linux会存在细微差别。

先说说背景
  • 机器:因为资源紧张,审批从严,只拿到了1台虚拟机。
    • 硬件[虚拟机]:4C/8GB/50GB[HDD]/100Mbps[LAN]
    • 软件[64-Bit]:CentOS[7.4]/JDK[1.8.0_91]/Docker[18.09.7]/Docker Compose[1.18.0]
  • CD工具:公司自研的系统,全界面化操作,这里就叫他tard吧。提供了容器管理,docker镜像配置,环境变量配置,打包,上下线等功能。
  • SkyWalking:使用Apache官方Docker镜像,版本号6.3.0。友情提示,推荐大家使用6.3.0或者更高的版本,因为低版本有个雷,后面再详细聊。

   提到需要简单、快速的部署,我们首先想到的就是使用Docker,而且SW是有官方Docker镜像的,可以放心的拿来使用,省去了我们自己做镜像的时间。这次我们主要使用Docker和Docker Compose部署SW,包括服务编排,容器发布与管理。读者需要对基本的Docker命令有所了解,如果没有使用过,这里强推一波Docker。Docker真的是个好东西啊,超级好用,属于那种一旦用过就离不开的程序,也有助于我们的思维扩展,强烈建议大家学习一下。

部署SkyWalking服务
  1. 我们只有1台机器,只有8个G的内存,初步猜测只能部署单机版,不过我们还是想以集群的方式部署SW。因为集群最能真实的模拟线上的环境,也比单机版更容易暴露问题,而且1台机器也不是不可以部署集群,这样的话我们就再挣扎下,先统计集群版和单机版都需要部署多少个实例,看看有没有运行集群的可能性。

    最小集群配置 skywalking-ui skywalking-oap-server ZooKeeper Elasticsearch 共计
    实例数量 2 2 3 2 9
    最小单机配置 skywalking-ui skywalking-oap-server ZooKeeper Elasticsearch 共计
    实例数量 1 1 0 1 3
  2. 结合实例数量和每个实例正常工作大概需要的内存量,可以看到集群和单机的差别还是很大的。考虑到集群版就算可以正常启动,在后面的读写压力下性能也不会太好,内存溢出的概率也很大,所以对这次的情况来说单机版更适合。不过有转机的是,如果大家再仔细的想下,这些组件当中只有Elasticsearch(下文简称ES)对内存的要求比较高,最耗内存,而在平时的使用中我们已经验证过ES集群的读写性能和稳定性了,这次其实可以忽略ES的测试,主要验证SW集群的问题。这样我们就可以采用SW集群 + ES单机混合搭配的方式,减少虚拟机的内存压力,实现集群部署。通过后面的试验,我们发现这种混合的方式是行得通的,而且也很好的满足了我们本次的需求。最终采用的部署配置如下:

    集群 + 单机混合配置 skywalking-ui skywalking-oap-server ZooKeeper Elasticsearch 共计
    实例数量 2 2 3 1 8
  3. 从零部署的过程中实际操作有很多,这里省略虚拟机的网络配置、防火墙配置、处理FD限制等操作系统级别的操作,省略JDK、Docker、Docker Compose等软件的安装操作,请大家自行安装调试,我们直接上关键的部分。

    • 提前下载好镜像文件(这步可以省略)
      # 我们使用6.3.0版本的SW,下载过程如图1所示。
      macos$ docker pull apache/skywalking-ui:6.3.0
      

    macos$ docker pull apache/skywalking-oap-server:6.3.0

    ![图1 使用docker下载镜像](https://upload-images.jianshu.io/upload_images/8592349-0668f2d27562fcdb.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    
    ```bash
    # 思考下ZooKeeper使用什么版本?
    # skywalking-oap-server:6.3.0的镜像文件中有一个oap-lib目录,存放了所有的依赖。
    # 我们发现它使用的ZooKeeper客户端版本号是3.4.10,如图2所示。
    # 所以ZooKeeper服务最好也使用3.4.10版本
    macos$ docker pull zookeeper:3.4.10
    
    图2 skywalking-oap-server:6.3.0依赖的ZooKeeper版本
    # 6.3.0版本的SW只能兼容6.3.2或者更高版本的ES,如图3所示。
    # 因为运维同学使用的是6.4.2版本,我们最好和运维保持一致。
    macos$ docker pull elasticsearch:6.4.2
    
    图3 SkyWalking官网对ES的版本要求说明
    # 下载完成后,应该可以看到如图4所示的四个镜像文件。
    macos$ docker images
    
    图4 查看本地镜像
    • 使用Docker Compose编排服务

      • 我们使用的docker-compose.yml配置文件

        version: "3"
        services:
          elasticsearch:
            image: elasticsearch:6.4.2
            container_name: elasticsearch
            restart: always
            ports:
              - 9200:9200
              - 9300:9300
            ulimits:
              memlock:
                soft: -1
                hard: -1
            environment:
              - discovery.type=single-node
              - bootstrap.memory_lock=true
              - "ES_JAVA_OPTS=-Xms1536m -Xmx1536m -Xmn512m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m"
              - TZ=Asia/Shanghai
          oap1:
            image: apache/skywalking-oap-server:6.3.0
            container_name: oap1
            depends_on:
              - elasticsearch
              - zk1
              - zk2
              - zk3
            links:
              - elasticsearch
              - zk1
              - zk2
              - zk3
            restart: always
            ports:
              - 11801:11800
              - 12801:12800
            environment:
              SW_CLUSTER: zookeeper
              SW_CLUSTER_ZK_HOST_PORT: zk1:2181,zk2:2181,zk3:2181
              SW_STORAGE: elasticsearch
              SW_STORAGE_ES_CLUSTER_NODES: elasticsearch:9200
              SW_STORAGE_ES_INDEX_SHARDS_NUMBER: 1
              SW_STORAGE_ES_INDEX_REPLICAS_NUMBER: 0
              TZ: Asia/Shanghai
          oap2:
            image: apache/skywalking-oap-server:6.3.0
            container_name: oap2
            depends_on:
              - elasticsearch
              - zk1
              - zk2
              - zk3
            links:
              - elasticsearch
              - zk1
              - zk2
              - zk3
            restart: always
            ports:
              - 11802:11800
              - 12802:12800
            environment:
              SW_CLUSTER: zookeeper
              SW_CLUSTER_ZK_HOST_PORT: zk1:2181,zk2:2181,zk3:2181
              SW_STORAGE: elasticsearch
              SW_STORAGE_ES_CLUSTER_NODES: elasticsearch:9200
              SW_STORAGE_ES_INDEX_SHARDS_NUMBER: 1
              SW_STORAGE_ES_INDEX_REPLICAS_NUMBER: 0
              TZ: Asia/Shanghai
          ui1:
            image: apache/skywalking-ui:6.3.0
            container_name: ui1
            depends_on:
              - oap1
              - oap2
            links:
              - oap1
              - oap2
            restart: always
            ports:
              - 8081:8080
            environment:
              SW_OAP_ADDRESS: oap1:12800,oap2:12800
              TZ: Asia/Shanghai
          ui2:
            image: apache/skywalking-ui:6.3.0
            container_name: ui2
            depends_on:
              - oap1
              - oap2
            links:
              - oap1
              - oap2
            restart: always
            ports:
              - 8082:8080
            environment:
              SW_OAP_ADDRESS: oap1:12800,oap2:12800
              TZ: Asia/Shanghai
          zk1:
            image: zookeeper:3.4.10
            restart: always
            container_name: zk1
            ports:
              - 2181:2181
            environment:
              ZOO_MY_ID: 1
              ZOO_SERVERS: server.1=zk1:2888:3888 server.2=zk2:2888:3888 server.3=zk3:2888:3888
          zk2:
            image: zookeeper:3.4.10
            restart: always
            container_name: zk2
            ports:
              - 2182:2181
            environment:
              ZOO_MY_ID: 2
              ZOO_SERVERS: server.1=zk1:2888:3888 server.2=zk2:2888:3888 server.3=zk3:2888:3888
          zk3:
            image: zookeeper:3.4.10
            restart: always
            container_name: zk3
            ports:
              - 2183:2181
            environment:
              ZOO_MY_ID: 3
              ZOO_SERVERS: server.1=zk1:2888:3888 server.2=zk2:2888:3888 server.3=zk3:2888:3888
        
        


      • 创建容器,启动服务

        # 指定配置文件docker-compose.yml的路径,在后台启动所有服务。
        # 如图5所示,会显示容器的创建结果。
        macos$ docker-compose -f ./docker-compose.yml up -d
        
        图5 启动所有服务
        # 查看容器是否启动成功,如图6所示。
        macos$ docker ps
        
        图6 查看容器状态
    • 到这里SW集群就部署完毕了。有些同学可能会发现在集群启动的过程中oap1和oap2两个容器会经常的重启,别慌,不是你做错了什么,这个是正常现象,等一会就稳定了。

部署SkyWalking Agent
  1. 推荐到SkyWalking官网下载Agent的安装包,网速还是不错的。请根据各自的操作系统下载对应的安装包,如图7所示。

    图7 SW安装包,二选一

  2. 我们下载的安装包是完整的All-In-One安装包,解压后我们会看到安装包内有Agent,OAP Server,Web UI三个组件。由于我们只需要Agent,所以只提取agent文件夹就可以了,如图8所示。


    图8 从安装包中提取agent
  3. 拿到了Agent,接下来需要做的是把Agent和我们的系统部署到同一个容器中去,我们日常会使用一些CI工具,再结合CD工具tard将系统部署到私有云上。这次呢,我们还是使用tard演示Agent的部署过程,而不是使用原生的Docker。

    • 说到这里可能会有一部分同学觉得不太理解,尤其是很少使用Docker,对部署流程了解较少的同学,他们可能会疑惑:”读者原本是想参考文章中的代码、命令来尝试部署测试,tard是你们公司的内部系统,我们没法使用,如果用更加通用的Docker会不会更好?“。

    • 解释这个问题前先简单说下背景。和很多的公司一样,我们也是有一套标准的打包发布流程的,tard有一套自己的使用规范,关于这一点曾经与运维的同学简单了解过。出于安全的考虑,tard会要求所有的系统只能使用公司统一的基础镜像,或者以基础镜像为底做出来的其他镜像,tard也会限制很多的docker命令不可以使用。同时tard自身还有一些功能待完善,比如在创建docker镜像的时候,基础镜像只支持解压War包,暂不支持其他的压缩格式,所以tard只支持添加War包;容器启动时唯一可以执行的命令是运行Tomcat等等。(PS:tard为部分功能是留了口子的,如果项目组有什么特殊需求可以走运维部门的技术支持流程,由运维同学再细看是否可以处理。有些限制是可以解决的,有些暂时还是不可以,具体的细节没有深入了解。)

    • 所以从发布流程来说,我们是一定要使用tard来发布的,使用tard演示可以为公司的同事提供一些参考,也可以还原我们当时遇到的一些问题和解决思路,分享这些我觉得是很有意义的。同时在tard现有的限制下部署Agent还是有些难度的,可以说这也是一个雷,不过这些问题的存在也会促使我们思考,这也是让我觉得好玩的地方,可能是个人的恶趣味吧。而且tard和Docker的相似度很高,只要有一点Docker的基础就没什么问题,所以选择了tard来演示。这里我们最主要来理解都需要做什么,具体使用什么实现都可以的。

  4. 要实现前文说的整个接入过程无任何代码侵入,只需要简单修改镜像的配置就可以完成接入的这个目标,我们需要将Agent做到基础镜像里面,这样只需要修改发布系统依赖的基础镜像就可以使用Agent了。常规做法一般是将Agent文件夹压缩成tar包或者zip包,上传,然后在镜像内解压就可以了。可是tard只支持添加War包,怎么办?这是我们遇到的一个问题,请大家思考一下再向下阅读。

  5. tard只支持添加War包怎么办?很简单,那就打成War包喽。很多人会觉得有点别扭,有点反常规,尤其是有技术洁癖的同学。我觉得别想太多,能解决问题就是好办法,规则已经制定了,无法打破我们就想想怎么利用规则解决问题吧。既然是研发熟悉的War包,那我们走常规的研发流程就可以了。

    • 考虑到大概率上只会做一次基础镜像,我们就尽量简单点,不需要走申请创建新代码仓库的审批流程,最好可以利用已有的Web系统,复用原有的打包配置。这样我们从现有系统的git代码仓库拉出来了一个分支,命名为sw-agent。

    • 保留Maven中使用maven-assembly-plugin插件打包的配置,删除其他无关代码。将SW安装包中的agent文件夹添加到项目中,commit、push到仓库,如图9所示。


      图9 将sw的agent文件夹添加到分支中
    • 新建分支打包配置,指定要编译、打包的分支与War包生成路径,后面再结合tard的界面化操作,可以实现一键完成编译、打包、上传操作。


      图10 打包的分支配置、War包配置
  6. 我们能够拿到sw-agent.war,就可以开始做基础镜像了。

    # Dockerfile
    FROM 公司统一的基础镜像:JDK8+Tomcat8版本
    ADD sw-agent.war /opt
    WORKDIR /opt
    # 解压后删除War包多余的文件,最终的结果就和使用tar包、zip包一样了
    RUN jar xf sw-agent.war && rm -rf sw-agent.war META-INF
    
    #做好的镜像我们暂时命名为skywalking-agent-6.3:1.0
    
  7. 修改发布系统的基础镜像,然后再将agent加入到Tomcat的运行时参数里面。

    # 修改基础镜像,使用skywalking-agent-6.3:1.0
    FROM skywalking-agent-6.3:1.0
    
    省略......
    
    # 修改Tomcat的环境配置脚本setenv.sh
    省略......
    
    # 加入这一段脚本代码
    AGENT_BOOTSTRAP="/opt/skywalking/agent/skywalking-agent.jar"
    if [ -f $AGENT_BOOTSTRAP ]; then
        # SW_AGENT_NAME:我们要追踪的系统的名称。
        # SW_AGENT_COLLECTOR_BACKEND_SERVICE:SW OAP服务地址和端口号。
        CATALINA_OPTS="$CATALINA_OPTS -DSW_AGENT_NAME=${SW_AGENT_NAME} -DSW_AGENT_COLLECTOR_BACKEND_SERVICES=${SW_AGENT_COLLECTOR_BACKEND_SERVICES} -javaagent:$AGENT_BOOTSTRAP"
    fi
    
  8. 现在我们可以让系统跑起来,验证下接入是否成功了。

    • 在系统启动之前,我们需要先为上面留的两个运行时变量赋值。我个人更喜欢以环境变量的方式配置系统,简单、改动更加方便,具体的配置如下所示。

      # 指定被追踪的系统的名称,不可重复。
      SW_AGENT_NAME=ATS-WZ
      
      # Agent需要将生产的数据上报给我们部署的服务实例oap1,oap2。
      # 使用过程中在这里发现了一个雷,标记下。
      

    SW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11801,127.0.0.1:11802

    - 实例启动成功以后,打开SW的首页[ http://127.0.0.1:8081, http://127.0.0.1:8082 ],UI文本会默认使用英文显示,可以通过右下角的中英文切换功能切换语言。如果能在当前服务中看到我们配置的系统名称ATS-WZ,就表示接入成功了,如图11所示,至此SkyWalking Agent我们就部署完毕了。
    ![图11 SW可以识别被追踪的系统了](https://upload-images.jianshu.io/upload_images/8592349-618f451a818d3213.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    
    
简单说下探到的雷吧

   整个的接入、使用与测试过程中我们发现了几个雷,包括上面说的在低版本的SW上可能会出现一些问题的雷。我们都一一标记、收集整理了,打算在后面总结的时候我们再集中一一拆解,本节只是试验与探雷,至于排雷后面再聊。不过有一些雷可能会导致接入出现问题,我们可以先简单说一下,以免影响大家使用。

  1. 问题表现:SW首页没有数据,包括当前服务、端点、实例都没有数据,可以说接入失败了。
    • 大概率是OAP服务挂了,可以检查一下。

    • 如果是6.3以下的版本,在OAP服务重启以后可能还是没有数据的。因为低版本的SW要求在OAP重启以后,所有的agent端都要重启,重新接入,否则是没有数据的。这是一个比较坑的问题,建议SW官网能够加上这个使用说明,这个问题带来的后果就是如果OAP服务挂了,重启,那么要通知所有的接入方重启自己的系统。一个是很麻烦,一个是可能会影响我们的服务。还好的是在6.3版本升级了相关功能,解决了这个问题,所以强烈建议使用或者升级到6.3以上版本。

  2. 问题表现:SW首页没有数据,并且在agent日志中可以看到如图12所示的报错。


    图12 Agent日志报错
    • 很大的可能是我们配置的环境变量SW_AGENT_COLLECTOR_BACKEND_SERVICES有问题。一些同学会想的很全面,他们考虑到了负载均衡的问题,使用的是OAP集群的域名,考虑的很好很充分,只不过SW不支持。我们通过查看SW的源码可以发现,SW是不支持域名的,只支持 ip1:port1,ip2:port2 这种格式,如图13所示。


      图13 从源码中可以推断出OAP服务地址的配置格式
    • 至于OAP集群的负载均衡问题,我们不需要自建类似Nginx+OAP这种的反向代理集群。查看源码可以发现Agent采用的是客户端软负载均衡,默认使用随机算法选择需要连接的OAP服务实例。

  3. 问题表现:SW页面上有很多的区域都没有数据,或者说响应很慢。打开Chrome浏览器的开发者工具可以发现,SW页面发出的很多接口请求都超时失败了,超时时间大约为10秒。查看ui1和ui2的日志可看到如图14所示的错误。


    image

    图14 UI日志中的错误
    • 这个问题一般是因为连接、请求超时,主要问题在于OAP服务或者ES性能不足,造成连接超时、响应超时甚至无响应。从根本上解决这个问题的办法,一般是调整优化OAP服务或者ES的性能。

    • 我们也可以使用一个临时方案,UI的时间不够,那我们就延长下UI的时间。可以修改UI的超时配置,默认为10秒,测试和生产环境都可以适当的延长这个值,我们将ReadTimeout延长到了30秒。同时增加ConnectTimeout配置,也设置为30秒,如图15所示。


      图15 延长超时时间
  4. 问题表现:在追踪页面里,我们可以看到所有被追踪接口的调用链路。我们可以再前进一步,打开某一个接口的调用链路详情,我们希望可以拿到这个接口的入参和出参。不过可惜的是,大多时候我们是拿不到这个接口的入参和出参的,如图16所示。


    图16 在接口详情里面拿不到接口的入参和返参
    • 因为SW的默认实现在很多情况下是不会记录这些数据的,需要定制开发,具体如何做我们后面再聊。
总结

   在整个的安装、使用、测试过程中,我们遇到了一些问题,不过幸运的是通过查阅官方文档以及阅读SW源码,我们发现问题是可控的,是可以解决的。这次也让我们尝到了使用熟悉的技术带给我们的好处,尤其是在这种时间紧迫的情况下,让我们可以做到对项目更可控,更有信心。在验证完SW的适用性、稳定性和读写性能以后,我们将针对SW做一定的开发与扩展以满足我们的业务需要,这些内容让我们慢慢再聊。

参考资料

Tard使用文档
SkyWalking官方文档

你可能感兴趣的:(链路监控探索实践二 初步试验与先行探雷篇)