南山行者
作者南山行者·2019-03-11 15:48
系统工程师·某银行

Apollo+ES源码改造构建民生银行天眼ELK日志平台配置管理中心

字数 13332阅读 3386评论 1赞 5

随着中国民生银行的IT业务系统的迅速发展,主机、设备、系统、应用软件数量不断增多,业务资源访问、操作量不断增加,对于应用整体系统智能分析与处理的要求不断提高,在解决分布式大数据搜索、日志挖掘和可视化的过程中,需要考虑实现一套完整并适用于民生银行的日志文件智能分析与处理的解决方案。基于此天眼实时智能日志管理分析平台应运而生,通过日志的收集、传输、储存,对海量系统日志进行集中管理和实时搜索分析,帮助运维人员进行业务实时监控、故障定位及排除、业务趋势分析、安全与合规审计等工作,深度挖掘日志的大数据价值,提升了应用整体系统智能分析与处理效率,达到了汇总、检索、展示应用日志和串联事件、快速定位问题等全方位功能要求。

目前日志平台纳管的服务器超过1000台,覆盖了民生银行所有操作系统类型:SuSE Linux(11/12)、AIX(7)、HP_UX(11)和RedHat(5.5),除了应用日志以外,系统软件日志类型覆盖DB2、Oracle、Mysql、Redis、Weblogic、Activemq、Kafka、Tomcat等等,同时采集存储、操作系统、管理口相关指标日志。每天接入日志量在10T到20T之间,平均日接入量在15T左右。在如此量级的日志接入下,对平台本身吞吐量、端到端全链路的写入延迟、查询响应时间等都提出了比较高的要求,因此对于ES集群本身的参数调优成为了一项持续进行的长期性工作,这时ES物理节点过多导致的配置文件分散、角色参数差异、版本管理混乱、配置监控缺失等问题就集中暴露了出来。而使用脚本或者文件分发管理又不够直观和友好,出现问题排查困难,易用性较差,也不具备分布式和高可用功能。

为了能够界面化、集中化管理ES集群不同角色、不同类型的配置并且在配置修改后能够在ES中实时生效,所有配置信息的修改具备规范的权限、流程治理等特性,民生银行天眼ELK日志平台最终采用携程开源的Apollo(阿波罗)作为技术选型,本篇以民生银行天眼日志平台实际需求为中心,逐步展开介绍我们是如何通过Apollo+ES源码改造构建民生银行天眼ELK日志平台配置管理中心。

一、Apollo功能介绍

Apollo目前在github的Star数量超过10000,社区活跃度和版本更新效率都比较高,首先简单介绍一下Apollo本身的功能点和其在天眼ELK日志平台配置管理中心中的实际运用。
Apollo是携程的开源配置管理中心,可以从应用、环境、集群、命名空间4个维度集中的管理配置,并能够够实施的推送至客户端,优点如下:

1、统一管理不同环境、不同集群的配置

Apollo提供了一个统一界面集中式管理不同环境(environment)、不同集群(cluster)、不同命名空间(namespace)的配置。同一份代码部署在不同的集群,可以有不同的配置,通过命名空间(namespace)可以很方便地支持多个不同应用共享同一份配置,同时还允许应用对共享的配置进行覆盖。
由于民生银行开发、测试、生产环境均是网络隔离的,所以在进行Apollo部署时我们去掉了4个环境标识DEV、FAT、UAT、PRO中的后面3个,只保留了DEV环境,测试和生产均完整的部署一套Apollo环境。另外运用集群(cluster)实现了ES角色配置文件分离,运用namespace实现了ES不同种类配置文件分析,这些在后面Apollo+ES设计中会详细讲到。

2、配置修改实时生效(热发布)

用户在Apollo修改完配置并发布后,客户端能实时(1秒)接收到最新的配置,并通知到应用程序。
这个功能比较诱人,我们在其它项目中也使用到了热发布,但是在ES中没有配置,一方面是由于ES读取的很多配置的模式都是读且仅读一次,热配置无法生效。另一方面ES代码较复杂,其在读取配置文件前后都需要初始化很多参数,很多配置参数是扩散到整个项目代码中的,核心代码、插件代码可能都会有涉及,一些如监听端口配置修改需要重起相关线程模块,实现热配置风险太高,最后热配置在ES源码改造过程中我们只是用于配置参数变化的日志输出打印,完成配置生效还是需要重起节点进程。

3、版本发布管理

所有的配置发布都有版本概念,从而可以方便地支持配置的回滚。
这个是配置中心基本功能,在日志平台进行ES参数优化我们依赖Apollo进行版本管理,使用起来比较方便。

4、灰度发布

支持配置的灰度发布,比如点了发布后,只对部分应用实例生效,等观察一段时间没问题后再推给所有应用实例
在日志平台实际应用中我们经常使用灰度发布的功能去验证某些通用参数是否可以配置在角色节点或者数据节点可以单独生效,在default集群参数中让某些参数的发布推送到特定的master节点或者data节点,不过最后根据默认参数全部配置一致原则会将default中修改过的参数配置推送到全部的ES节点当中。

5、权限管理、发布审核、操作审计

应用和配置的管理都有完善的权限管理机制,对配置的管理还分为了编辑和发布两个环节,从而减少人为的错误。所有的操作都有审计日志,可以方便地追踪问题。
在权限控制上生产环境要求必须日志平台A岗负责人有最终的发布权限,ES参数修改对集群整个的稳定性影响比较大,必须将风险降到最低。另外目前日志平台配置管理中心也承担着一些项目组自己开发的工具的配置管理工作,所以单独也进行了权限的划分和处理。

6、客户端配置信息监控

可以在界面上方便地看到配置在被哪些实例使用。
这个功能在日志平台中某种程度上也起到了实例心跳检测的功能,同时根据角色的划分可以清楚地看到这个ES的逻辑节点配置位置,比如default参数肯定是所有节点都会读取,而单独的master、hot、warm、client节点只有相关的角色节点才会读取其配置。值得一提的是由于有缓存的功能,可以看到ES相关节点读取配置的时间实际上是参数改变后节点第一次读取配置的时间,如果ES节点单独重起但是相关参数没有发生变化和发布,那么界面上看到的配置参数读取时间是不会发生改变的。

7、提供Java和.Net原生客户端

提供了Java和.Net的原生客户端,方便应用集成,同时提供了Http接口,非Java和.Net应用也可以方便地使用。
ES本身就是通过Java语言编写的,所以通过修改ES源码可以比较方便地将Apollo客户端集成到ES中去。后续我们打算将Logstash处理的配置文件也通过Apollo纳管,由于Logstash是用ruby语言,所以可能就会用到其提供的Http接口。

8、提供开放平台API

Apollo自身提供了比较完善的统一配置管理界面,支持多环境、多数据中心配置管理、权限、流程治理等特性。不过Apollo出于通用性考虑,不会对配置的修改做过多限制,只要符合基本的格式就能保存,不会针对不同的配置值进行针对性的校验,如数据库用户名、密码,Redis服务地址等。对于这类应用配置,Apollo支持应用方通过开放平台API在Apollo进行配置的修改和发布,并且具备完善的授权和权限控制。
目前天眼ELK日志平台主要是还是使用Apollo本身提供的配置管理界面进行管理,不过后续可以考虑通过其API将部分功能在大数据集中的管控平台上去实现。

9、部署简单

配置中心作为基础服务,可用性要求非常高,这就要求Apollo对外部依赖尽可能地少,目前唯一的外部依赖是MySQL,所以部署非常简单,只要安装好Java和MySQL就可以让Apollo跑起来。Apollo还提供了打包脚本,一键就可以生成所有需要的安装包,并且支持自定义运行时参数。
日志平台基本按照官方文档进行标准部署,稍微有一点改动的地方是Apollo本身的日志路径存放地址是以进程号为目录层级的,不太直观,通过修改源码将其增加adminservice、configservice和portal目录,使得不同模块的日志路径比较清晰易区分。

二、Apollo架构解析

在日志平台生产实际使用Apollo之前,必须对其架构进行深入了解和剖析,在架构设计的过程中有一个基本点必须明确,那就是Apollo的某个服务异常不应该影响整个配置管理中心的服务,Apollo所有模块的异常不应该影响ES集群的正常运行。Apollo的基础模型如下:
6hxjfozbajw

6hxjfozbajw

如上图所示,用户在配置中心配置/修改并发布配置,然后Apollo通知客户端有配置更新,最后客户端向Apollo配置中心请求最新配置。总体设计如下图所示:
7lx04jeu7vf
7lx04jeu7vf

其中Apollo各模块的功能简单介绍如下:
(1)config Service:
提供配置获取接口,提供配置更新推送接口,接口服务对象为Apollo客户端;
(2)Admin Service:
提供配置管理接口,提供配置修改、发布等接口,接口服务对象为Portal;
(3)Meta Server:
Portal通过访问Meta Server获取Admin Service服务列表(IP+Port),Client通过访问Meta Server获取Config Service服务列表(IP+Port),Meta Server从Eureka获取Config Service和Admin Service的服务信息,相当于是一个Eureka Client,增设一个Meta Server的角色主要是为了封装服务发现的细节,对Portal和Client而言,永远通过一个Http接口获取Admin Service和Config Service的服务信息,而不需要关心背后实际的服务注册和发现组件,Meta Server只是一个逻辑角色,在部署时和Config Service是在一个JVM进程中的;
(4)Eureka:
基于Eureka和Spring Cloud Netflix提供服务注册和发现,Config Service和Admin Service会向Eureka注册服务,并保持心跳,为了简单起见,目前Eureka在部署时和Config Service是在一个JVM进程中的;
(5)Portal:
提供Web界面供用户管理配置,通过Meta Server获取Admin Service服务列表(IP+Port),通过IP+Port访问服务,在Portal侧做load balance、错误重试;
(6)Client:
Apollo提供的客户端程序,为应用提供配置获取、实时更新等功能,通过Meta Server获取Config Service服务列表(IP+Port),通过IP+Port访问服务,在Client侧做load balance、错误重试。

关于Apollo各个组件的相关作用和实现原理不是本文的重点,我就不在这里过多赘述了,感兴趣的同学可以去Apollo的github进行详细了解。在实际部署中我们是将Apollo部署在三台服务器上做高可用和负载均衡,每台服务器上都会启动独立的config Service、Admin Service和Portal三个模块进程,后端连接统一的mysql数据库,通过修改数据库中serverconfig字段使得Eureka注册服务构成三节点的集群,client端和portal统一配置3台服务ip地址进行连接。下面就开始介绍我们构建天眼ELK日志管理平台的两块核心工作:Apollo ES架构设计和ES源码改造。

三、Apollo ES架构设计

在Apollo ES架构设计这块其实包含了两部分工作,一部分工作是通过Apollo的集群分离功能将通过ES中的角色将配置进行拆分,方面统一管理;另一部分工作是根据实际需求将elasticsearch需要频繁修改的配置文件进行分类并分别实现客户端开发。这两部分工作都包含了一个共有的工作内容——页面配置设计实现以增加功能使用体验。

1、 ES角色配置拆分

日志平台目前在生产环境使用的ELK版本是5.5的,在进行ES集群设计部署时data节点采用了冷热数据分离技术,引入了SSD来提升ES的读写性能。单台ES存储有2块SD盘和若干SATA盘,所以每台ES server都启动了3个ES节点,2个hot节点和1个warm节点。Indexer中指配置了hot节点的端口,通过ES中的模板定义保证实时数据只写入hot节点。通过ES官方推荐的curator工具定时将数据从hot节点搬迁到warm节点,SSD数据保留周期为一周。同时本身ES集群我们启动了3个master节点和3个client节点,那么实际上ES集群中一共是4种:master,client,hot和warm,节点类型都是通过配置文件elasticsearch.yml的参数进行区分的。
我们知道ES主要的配置参数都是在elasticsearch.yml中进行定义的,最开始设计时我们计划是通过主机名的方式进行配置文件区分,Apollo本身也提供了通过hostname定义不同的namespace的内置方法,但是最后达到的效果非常不理想。日志平台生产集群物理节点目前是28个,总节点数为90个,当需要修改参数时需要每个节点的namespace配置都在界面上进行修改并发布,重复工作太大;另外namespace过多也导致管理页面特别冗长,下拉很久也拖不到页面底部,用户体验很差,当时就想这样还不如直接脚本分发修改配置文件来的快些,失去了日志平台配置管理中心建设的初衷。
于是就想能不能通过角色进行配置拆分,相同的角色的配置是否可以放到一个namespace中去处理。Apollo本身在DEV的生产模式下提供创建多集群的配置方式,由于在日志平台中不存在多集群的情况,于是我们就将集群模式的功能来区分ES中的节点角色。最终在角色中实际上创建了5种角色参数分类:default、hot、warm、master和client。
hftnqc138j9

hftnqc138j9

在ES的官方文档中,没有详细且特意的介绍elasticsearch.yml中哪些参数需要配置在master节点,哪些需要配置在data节点。我大概翻了一下,其中Local Gateway参数明确提到需要配置在master节点,Fielddata和Node Query Cache、Indexing Buffer类参数是需要每个datanode节点都部署的。实际上我个人认为ES也是努力使越来越多的参数可以动态修改,所以从5.x以后将很多参数都提供了API可以在模板中进行配置,配置文件中的参数越来越少。也简单翻了一下源码,基本上没有特意区分节点角色参数的逻辑处理。这就说明了基本上ES的elasticsearch,yml参数配置最好是所有节点全部配置,不管是什么角色,除非是真正区分角色的配置。这就正好适配了配置中心的意义,通过一次修改而使得全部节点配置统一生效。
最初就是按照ES集角色群设计了4个集群,但是我发现其实每种角色节点有很多参数配置也是重复的,参数变更修改要操作4次,所以最后抽象出来了一个default集群,default中配置的都是通用的参数,只有具有角色特征的参数才会存放在各自不同namespace中。那么问题来了,集群做参数优化的时候修改那个角色集群的参数呢?没错,只需要修改default中的即可,所有和优化相关配置参数都是在default集群中的。
wsd6275bjp
wsd6275bjp

在hot、warm、master、client中的配置实际上均是一些特有配置,如角色定义、日志路径、端口号等等,那么这里就有一个问题,如果在hot集群中和default集群中配置有冲突怎么办。实际上在这块设计中我们设置了优先级,也就是说hot集群中的配置优先级是大于default集群中的优先级的,当发生冲突时默认选择以角色集群中配置的参数为准。另外对于一些具有节点特征的参数,如节点名称、节点绑定ip、网卡等等,那么这类参数就保留在配置文件当中,不在Apollo中进行配置。所以最后配置的读取优先级是:角色配置>default配置>配置文件,这种设计一方面保证了有用的配置集中管理,层次划分清晰,另一方面可以使得配置迁移的安全性和健壮性,避免配置中心漏掉某些配置导致ES集群出现问题。
luub0kf0bkq
luub0kf0bkq

2、参数文件拆分

除了elasticsearch.yml配置文件以外,ES每个节点的jvm.options文件我们也经常需要修改,最基本的就是通过-Xms和-Xmx来调整ES节点分配的堆内存大小。根据冷热分离需求和节点角色的特性,hot、warm、master、client分配的内存都是不一样的,所以很自然的-Xms和-Xmx就单独放到了每个角色的专有属性当中,其他的配置参数都放到default集群中。
0vyxfz4qdhln

0vyxfz4qdhln

在Apollo页面配置设计上角色集群中我们没有在application中去加入配置参数,而是通过配置集群中的namespace名称作了一次链接操作。在每个角色的application中统一都配置4个key值来分别指向不同的namespace,public的key值都是default集群中的namespace名称,private都指向该角色集群下的namespace名称。default中为了保持统一和方便管理,在application中也配置了本地的namespace名称,也就是两个public的配置项,实际上这两个配置项是用不到的。这样设计的好处是当namespace名称需要改变时不需要更改ES中Apollo的代码而达到解耦的目的。
t03x1yd1nef
t03x1yd1nef

3ii1qcjdqg9
3ii1qcjdqg9

在实际运行的过程中,可以实时监测那些运行的ES节点读取到了当前配置,通过上图可以看到在default集群中实际上所有的节点都会进行读取其中的参数,但是和application中的配置无关,这个是用来保持统一方便管理的。而角色集群中的配置只有ES中的角色节点才会进行读取。这里需要注意的是Apollo管理页面显示的配置读取时间并不一定是ES节点最后一次重启的时间,因为Apollo客户端本身是由缓存机制的,其在本地会创建一个cache目录保留从Apollo中读取的配置,如果配置没有修改并发布过的话,那么就算ES节点重启它还是会显示的是你最后一次发布配置的时间。
最后值得一提的是Apollo本身提供了properties、xml、json、yml和yaml五种配置文件录入方式,需要在创建对应namespace时指定,虽然elasticsearch.yml本身是yml格式配置文件,但yml方式并没有带来客户端程序开发的优势,同时也不如properties在页面上看起来美观,最后我们还是进行了一下转换,统一使用properties格式进行录入,如下图所示Apollo支持文本方式进行批量导入。另外所有的namepace我们都设置为私有的,因为目前暂时没有和其他项目共享参数的需求。
7r7hk7hw86g
7r7hk7hw86g

四、ES源码改造

Apollo ES架构设计结束之后就可以进行ES源码改造这块的工作了,实际上在我们在ES源码改造这块的所花费的时间远远没有设计的时候多,一旦设计定型源码改造就比较容易。结合Apollo客户端进行ES源码改造主要分为两部分工作:elasticsearch配置源码改造和jvm配置功能开发。

1、 elasticsearch配置源码改造

u7kx2hf6rue

u7kx2hf6rue

上图简要描述了Apollo客户端的实现原理:
(1)客户端和服务端保持了一个长连接,从而能第一时间获得配置更新的推送。(通过Http Long Polling实现);
(2)客户端还会定时从Apollo配置中心服务端拉取应用的最新配置;
●这是一个fallback机制,为了防止推送机制失效导致配置不更新
●客户端定时拉取会上报本地版本,所以一般情况下,对于定时拉取的操作,服务端都会返回304 - Not Modified
●定时频率默认为每5分钟拉取一次,客户端也可以通过在运行时指定System Property: Apollo.refreshInterval来覆盖,单位为分钟
(3)客户端从Apollo配置中心服务端获取到应用的最新配置后,会保存在内存中;
(4)客户端会把从服务端获取到的配置在本地文件系统缓存一份;
●在遇到服务不可用,或网络不通的时候,依然能从本地恢复配置
(5)应用程序可以从Apollo客户端获取最新的配置、订阅配置更新通知。
ES的主要核心包在core包里,通过Bootstrap类去启动实例化的node节点,在node的构造参数中,可以找到配置文件的加载,我们就是通过修改InternalSettingsPreparer这个类来实现ES通过Apollo客户端读取elasticsearch.yml的相关配置。

0h4z343erzy

0h4z343erzy

在InternalSettingsPreparer中PrepareEnvironment方法构建了一个Settings实例来加载给定或某认路经下的elasticsearch.yml文件,加载的配置都存放到output中,所以按照设计只要读取Apollo替换output中的值即可,output底层实现的原理就是map。代码很简单,就是首先读取elasticsearch.yml配置文件中的值,然后读取Apollo中default集群中的值,相同的key值进行覆盖,最后读取Apollo每个角色配置值,相同的key值覆盖就可以了。如下就是增加的代码块:
yx66uxe81
yx66uxe81

注意ES中discovery.zen.ping.unicast.hosts这种参数的值是中括号形式括起来的,底层实际上是一个数组,ES在处理时会以参数+.数字的方式进行标识,所以需要特殊处理一下,最后要加上逻辑判断防止default中数组参数元素个数大于角色集群而造成的配置参赛个数溢出。另外ES参数中有些参数在elasticsearch.yml配置时需要加上双引号,这个双引号在ES真正加载时不会读取,所以在Apollo中配置时不要带双引号。我在配置http.cors.allow-origin曾经直接将双引号粘贴过去,结果导致了elasticsearch-head插件不可用。
修改源码集成了Apollo客户端的ES启动日志如下图所示,在新的日志中打印了读取配置的信息。从日志中可以清晰地看到首先ES会读取本地的配置文件,所有从本地配置文件读取的配置参数都记录为old参数,然后再从Apollo中读取配置,如果有同名参数则选择Apollo参数直接覆盖掉。
t9yi2grz67
t9yi2grz67

zbji4ex93ga
zbji4ex93ga

这里需要提醒的是在Apollo客户端集成到ES的过程中,客户端所依赖jar包有可能和ES的某些插件的包有冲突,如果有冲突需要提前将这些包替换掉保留一份即可。
高可用也是我们特别关注的功能点,必须保证哪怕Apollo所有服务都挂掉也不影响ES的正常启动和运行。Apollo客户端在启动后会在启动用户的根目录下创建一个config-cache缓存目录,目录中会存放所有Apollo的namespace配置文件,文件内容就是配置的key-value键值对,保证ES在遇到Apollo服务不可用,或者网络不通的时候依然能从本地恢复配置。
3frshkp1i4j
3frshkp1i4j

下表是Apollo官方提供的高可用说明,我们在实践中完全按照官方推荐的架构搭建了3节点的Apollo集群,后端使用的MySQL数据库是主备模式,同时额外通过优先级读取策略在最坏的情况也下会读取本地的配置文件实现了双保险(本地缓存文件失效也不影响ES)。
g27b1tbllln
g27b1tbllln

2、 jvm配置功能开发

ES读取jvm.options配置文件是在启动脚本中直接实现的,所以无法通过直接修改ES源码来进行Apollo客户端开发。如果直接修改elasticsearch启动脚本来读取jvm配置比较复杂,而且这样修改对原来的启动脚本改造较大,不是我们所想要的。但jvm参数本身对于ES来讲还是具有一定的调优价值,尤其是内存分配上,在单台物理机上启动多个不同角色节点可能需要频繁的调整。最后我们在实现上采用了单独开发一个jar包在elasticsearch启动脚本之前执行,读取Apollo配置如果有key值不同在文件中进行替换。Apollo客户端上的处理逻辑与elasticsearch.yml完全一样,优先级都是角色配置>default配置>配置文件,区别就是配置文件不一致就直接替换,最终落地还是以配置文件中的参数配置为准。代码就不粘了,启动命令如下:
java -jar /logger/Apollo/Apollo-jvm.jar -DApollo.cluster=$APOLLO /logger/elasticsearch/config$APOLLO_PATH/jvm.options
这里有几点需要特殊说明一下,首先jvm参数在jvm.options配置文件中是有两种格式的,一种就是传统的以冒号分隔的key-value键值对,这种在Apollo中直接properties方式处理就好,还有一种参数既是key值也是value值,如-Xms2g,这种类型参数我们想了好几种解决方案,如把key值设置成和value值一样、对数字进行特殊处理、对特殊参数单独命名等等,但是最后一旦多次修改就会产生一些逻辑bug,最终我们采取了一种折中的方式,首先如果是key-value键值对类型那么正常处理,如果是单一键值的话那么采用前缀字符串匹配的方式实现,也就是说比如-Xms2g这种参数实际上在Apollo配置的是-Xms:-Xms2g,当Apollo客户端进行文件扫描时如果是非key-value且符合字符串前缀的就进行参数值覆盖,这就要求这种配置参数在进行Apollo jvm录入时保证是唯一的,这种方案的优势是一方面jvm参数一般value值直接做key值或者前缀到数字之前就可以保证唯一性,另一方面这种方式避免了自己命名参数导致含义不清,前缀+注释的方式在页面上显示清晰,有较好的用户体验。
其次因为我们在一台服务器上实际上是启动了多个ES实例的,所以在Apollo启动命令中是通过环境变量来区分角色集群和jvm配置文件路径的,环境变量通过进程管理工具supervisord来进行传递的。
最后需要提到的就是在jvm的启动实例列表中经常显示为0,原因是我们单独开发的一个jar包在ES启动脚本前执行,执行后这个客户端就退出了,前面提过Apollo的客户端和服务端回保持一个长连接,这个长连接可以保证服务端获取客户端的心跳信息,一旦客户端退出了Apollo默认只展示最近一天访问过Apollo的实例,那么一天之后数据库就会清空对应字段,jvm启动实例列表就会显示为0。
ymdt1jh859

ymdt1jh859

3、Supervisord工具结合

进行ES Apollo客户端改造后我们发现了一个问题:在源码中按照ES的方式输出的日志无法打印到ES的系统日志中。这个问题困扰了我们很久也找不到原因,只能一点点去梳理源码。通过ES入口方法类找到了读取配置文件的EnvironmentAwareCommand中的执行方法,该方法是一个抽象方法,其对应的实现方法如下图:
3yqrwywgqqp

3yqrwywgqqp

由上图可以看到,该execute方法需要先执行createEnv方法,即需要执行InternalSettingsPreparer.prepareEnvironment(Settings.EMPTY, terminal, settings); (即红色箭头指向的1)该方法,而该方法就是Apollo的主要代码修改与存放区域。在该方法内部,会进行对环境的初始化,即对yml配置文件参数的加载。但该方法区并没有对log4j2.properties配置文件进行加载和其他处理。然后其执行一个新的execute方法(即红色箭头所标识的2)。该抽象方法所对应的实现方法如下:
33lvgvi0kfv
33lvgvi0kfv

由上图可以看出,在执行execute的过程中,elasticsearch开始对BootStrap这个类进行初始化。
8k1ttrhm34w
8k1ttrhm34w

从上图可以看出,因为我们修改的ES源码是在InternalSettingsPreparer类中,prepareEnvironment(Settings input, Terminal terminal, Map<String, String> properties)方法加载配置yml文件的时候,此时log4j2.properties中的配置还没有进行加载,在ES对yml文件加载完成后,才开始对日志的配置文件进行的加载。故而出现了在InternalSettingsPreparer类中修改prepareEnvironment方法,添加的日志提示信息没有打印在elasticsearch的logs目录下的日志文件中的现象。如果想将我们手动添加的日志打印信息打印在elasticsearch的logs目录下的日志文件中,只能在BootStrap加载之后的文件中添加才可以实现,所以ES本身的日志在BootStrap加载之前是没有任何日志记录的。
找到了问题原因之后,基本可以确定在ES的Apollo客户端中如果需要打印日志需要引入第三方工具,同时逻辑上配置参数的读取是有优先级的,所以在日志中打印相关信息确定最终读取配置值这个需求是客观存在的。基于此我们在实际生产中引入了Supervisord进程管理工具,一方面可以批量的维护ES的运行状态,另一方面通过Supervisord提供的重定向功能将ES的屏幕输出到一个单独的文件当中,这样在ES的Apollo客户端只需要将需要打印的日志信息直接print出来即可。Supervisord的配置样里如下

[program:elkwarm]
environment=ES_JVM_OPTIONS=%(ENV_ELK_WARM_JVM_OPTIONS)s,APOLLO=%(ENV_APOLLO_WARM)s,APOLLO_PATH=%(ENV_APOLLO_WARM_ONE)s
command =  /logger/elasticsearch/bin/elasticsearch -Epath.conf=/logger/elasticsearch/configwarm
username = logger
autostart=true
autorestart=false
startsecs=3
priority=1003
stdout_logfile=/loggerfiles/elasticsearch/log/warm/cmbc_elk_warm.log
stdout_logfile_maxbytes=10MB
stdout_logfile_backuups=10
stdout_events_enabled=true

如上所示,在配置中需要将elasticsearch和jvm的Apollo角色环境变量传递进去,这样才能区分出同一台服务器不同ES实例的集群角色和配置文件路径,并且由于hot节点单台服务器上有两个实例环境变量还需要数字来区分一下。
此外,为了方便管理我们还使用Supervisord-monitor开源工具将ES集群所有服务器集成到一个页面上进行统一管理,除了具备启停功能之外还可以通过页面以tail -f的方式来查看我们单独生成的cmbc_elk_warm.log日志文件。
q6jpobc4dt

q6jpobc4dt

waptgvz3uis
waptgvz3uis

五、总结与展望

这是我们民生银行大数据团队对Apollo和ES两种开源产品的一次深入学习和探索,在业界没有相关案例的前提下,我们团队摸着石头过河,没有盲目地为了使用而使用,而是根据在生产环境中ES集群运行的实际情况有针对性的进行了Apollo架构设计和ES源码改造。在构建天眼ELK日志平台配置管理中心的整个过程当中,Apollo架构设计实际上要比ES源码改造花的时间多的多,无乱多么优秀的开源产品、多么牛逼的技术框架都不能脱离实际应用场景而存在,设计的合理必然会导致开发模块内部的高聚合和模块之间的低耦合,从而提高程序开发的效率,减少改造风险和增加落地的可行性。后续我们一方面会推进日志平台本身配置集中工作,设计出比较合理的模式接入日志平台其他组件配置信息,如Logstash,另一方面大数据产品均有服务器数量多、配置文件多难以集中管理的问题,如何让其满足更多的大数据产品是我们下一步所面临的挑战,最终目标是天眼ELK日志平台配置管理中心能够推广成为整个大数据平台的集中配置管理中心。

文章转自微信公众号“民生运维”
作者:生产运营大数据产品组
编辑:民生运维文化建设小组

如果觉得我的文章对您有用,请点赞。您的支持将鼓励我继续创作!

5

添加新评论1 条评论

michael1983michael1983联盟成员技术总监某证券
2019-03-17 17:31
这个必须赞!
Ctrl+Enter 发表

作者其他文章

相关文章

相关问题

相关资料

X社区推广