原创文章,转载请注明出处。由于本人水平有限,文中错漏之处在所难免,希望大家多多批评指正。本文的内容过多,我们分成四篇聊,此为第二篇。
通过前面的分析与对比,我们最终选择了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台机器,只有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 -
结合实例数量和每个实例正常工作大概需要的内存量,可以看到集群和单机的差别还是很大的。考虑到集群版就算可以正常启动,在后面的读写压力下性能也不会太好,内存溢出的概率也很大,所以对这次的情况来说单机版更适合。不过有转机的是,如果大家再仔细的想下,这些组件当中只有Elasticsearch(下文简称ES)对内存的要求比较高,最耗内存,而在平时的使用中我们已经验证过ES集群的读写性能和稳定性了,这次其实可以忽略ES的测试,主要验证SW集群的问题。这样我们就可以采用SW集群 + ES单机混合搭配的方式,减少虚拟机的内存压力,实现集群部署。通过后面的试验,我们发现这种混合的方式是行得通的,而且也很好的满足了我们本次的需求。最终采用的部署配置如下:
集群 + 单机混合配置 skywalking-ui skywalking-oap-server ZooKeeper Elasticsearch 共计 实例数量 2 2 3 1 8 -
从零部署的过程中实际操作有很多,这里省略虚拟机的网络配置、防火墙配置、处理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
# 6.3.0版本的SW只能兼容6.3.2或者更高版本的ES,如图3所示。 # 因为运维同学使用的是6.4.2版本,我们最好和运维保持一致。 macos$ docker pull elasticsearch:6.4.2
# 下载完成后,应该可以看到如图4所示的四个镜像文件。 macos$ docker images
-
使用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
# 查看容器是否启动成功,如图6所示。 macos$ docker ps
-
到这里SW集群就部署完毕了。有些同学可能会发现在集群启动的过程中oap1和oap2两个容器会经常的重启,别慌,不是你做错了什么,这个是正常现象,等一会就稳定了。
- 提前下载好镜像文件(这步可以省略)
部署SkyWalking Agent
-
推荐到SkyWalking官网下载Agent的安装包,网速还是不错的。请根据各自的操作系统下载对应的安装包,如图7所示。
-
我们下载的安装包是完整的All-In-One安装包,解压后我们会看到安装包内有Agent,OAP Server,Web UI三个组件。由于我们只需要Agent,所以只提取agent文件夹就可以了,如图8所示。
-
拿到了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来演示。这里我们最主要来理解都需要做什么,具体使用什么实现都可以的。
要实现前文说的整个接入过程无任何代码侵入,只需要简单修改镜像的配置就可以完成接入的这个目标,我们需要将Agent做到基础镜像里面,这样只需要修改发布系统依赖的基础镜像就可以使用Agent了。常规做法一般是将Agent文件夹压缩成tar包或者zip包,上传,然后在镜像内解压就可以了。可是tard只支持添加War包,怎么办?这是我们遇到的一个问题,请大家思考一下再向下阅读。
-
tard只支持添加War包怎么办?很简单,那就打成War包喽。很多人会觉得有点别扭,有点反常规,尤其是有技术洁癖的同学。我觉得别想太多,能解决问题就是好办法,规则已经制定了,无法打破我们就想想怎么利用规则解决问题吧。既然是研发熟悉的War包,那我们走常规的研发流程就可以了。
考虑到大概率上只会做一次基础镜像,我们就尽量简单点,不需要走申请创建新代码仓库的审批流程,最好可以利用已有的Web系统,复用原有的打包配置。这样我们从现有系统的git代码仓库拉出来了一个分支,命名为sw-agent。
-
保留Maven中使用maven-assembly-plugin插件打包的配置,删除其他无关代码。将SW安装包中的agent文件夹添加到项目中,commit、push到仓库,如图9所示。
-
新建分支打包配置,指定要编译、打包的分支与War包生成路径,后面再结合tard的界面化操作,可以实现一键完成编译、打包、上传操作。
-
我们能够拿到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
-
修改发布系统的基础镜像,然后再将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
-
现在我们可以让系统跑起来,验证下接入是否成功了。
-
在系统启动之前,我们需要先为上面留的两个运行时变量赋值。我个人更喜欢以环境变量的方式配置系统,简单、改动更加方便,具体的配置如下所示。
# 指定被追踪的系统的名称,不可重复。 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上可能会出现一些问题的雷。我们都一一标记、收集整理了,打算在后面总结的时候我们再集中一一拆解,本节只是试验与探雷,至于排雷后面再聊。不过有一些雷可能会导致接入出现问题,我们可以先简单说一下,以免影响大家使用。
- 问题表现:SW首页没有数据,包括当前服务、端点、实例都没有数据,可以说接入失败了。
大概率是OAP服务挂了,可以检查一下。
如果是6.3以下的版本,在OAP服务重启以后可能还是没有数据的。因为低版本的SW要求在OAP重启以后,所有的agent端都要重启,重新接入,否则是没有数据的。这是一个比较坑的问题,建议SW官网能够加上这个使用说明,这个问题带来的后果就是如果OAP服务挂了,重启,那么要通知所有的接入方重启自己的系统。一个是很麻烦,一个是可能会影响我们的服务。还好的是在6.3版本升级了相关功能,解决了这个问题,所以强烈建议使用或者升级到6.3以上版本。
-
问题表现:SW首页没有数据,并且在agent日志中可以看到如图12所示的报错。
-
很大的可能是我们配置的环境变量SW_AGENT_COLLECTOR_BACKEND_SERVICES有问题。一些同学会想的很全面,他们考虑到了负载均衡的问题,使用的是OAP集群的域名,考虑的很好很充分,只不过SW不支持。我们通过查看SW的源码可以发现,SW是不支持域名的,只支持 ip1:port1,ip2:port2 这种格式,如图13所示。
至于OAP集群的负载均衡问题,我们不需要自建类似Nginx+OAP这种的反向代理集群。查看源码可以发现Agent采用的是客户端软负载均衡,默认使用随机算法选择需要连接的OAP服务实例。
-
-
问题表现:SW页面上有很多的区域都没有数据,或者说响应很慢。打开Chrome浏览器的开发者工具可以发现,SW页面发出的很多接口请求都超时失败了,超时时间大约为10秒。查看ui1和ui2的日志可看到如图14所示的错误。
这个问题一般是因为连接、请求超时,主要问题在于OAP服务或者ES性能不足,造成连接超时、响应超时甚至无响应。从根本上解决这个问题的办法,一般是调整优化OAP服务或者ES的性能。
-
我们也可以使用一个临时方案,UI的时间不够,那我们就延长下UI的时间。可以修改UI的超时配置,默认为10秒,测试和生产环境都可以适当的延长这个值,我们将ReadTimeout延长到了30秒。同时增加ConnectTimeout配置,也设置为30秒,如图15所示。
-
问题表现:在追踪页面里,我们可以看到所有被追踪接口的调用链路。我们可以再前进一步,打开某一个接口的调用链路详情,我们希望可以拿到这个接口的入参和出参。不过可惜的是,大多时候我们是拿不到这个接口的入参和出参的,如图16所示。
- 因为SW的默认实现在很多情况下是不会记录这些数据的,需要定制开发,具体如何做我们后面再聊。
总结
在整个的安装、使用、测试过程中,我们遇到了一些问题,不过幸运的是通过查阅官方文档以及阅读SW源码,我们发现问题是可控的,是可以解决的。这次也让我们尝到了使用熟悉的技术带给我们的好处,尤其是在这种时间紧迫的情况下,让我们可以做到对项目更可控,更有信心。在验证完SW的适用性、稳定性和读写性能以后,我们将针对SW做一定的开发与扩展以满足我们的业务需要,这些内容让我们慢慢再聊。
参考资料
Tard使用文档
SkyWalking官方文档