Skywalking对应的ES的CPU很高的问题
结论
原因1:ES的新生代配置的太小,yong gc频率很高, 1s一次yonggc
解决方案:手动设置新生代、老年代比例 -XX:NewRatio=1
原因2:ES的索引没有配置为高性能写模式( 这个配置立杆见影 )
解决方案:对skywalking对应的index添加如下配置"index.merge.scheduler.max_thread_count" : "1", "index.refresh_interval" : "30s", "index.translog.durability" : "async", "index.translog.sync_interval" : "120s"
原因3: skywalking的agent上传JVM状态信息过于频繁,导致产生大量的ES request(这个不是主要原因)
解决方案: 修改agent代码,设置JVM上传频率为30s一次
环境
ES实例:4核 * 14G, 只有一台实例,基于docker起的
OAPServer:只有一台,512M
agent节点:也就是JVM实例大概50个
解决过程
agent 上传JVM信息太频繁
遇到的现象是,已经把oap-server的sample比例设置为1%了,可以看到skywalking中追踪的数据很少,但是发现ES所占CPU依然很高
image.png
修改oapServer把写入ES的BulkRequest的log级别修改为debug模式,发现短时间内的确有大量的Request
image.png
通过分析oapServer的堆栈发现:几乎所有的Request都是Metric相关的(service_relation_server_p90、instance_jvm_memory等等),而这里面的绝大部分又都是jvm相关的
jvisualvm
查看源码发现,jvm信息是通过单独的grpc接口上传到oapServer的,并且是1S收集一次, 1S上传一次,如此高频率的收集jvm数据,每一次收集的有多个指标(memory cpu gc),每个指标又根据分钟、小时、天、月更新多个index,就会导致产生大量的UpdateRequest对象,假设有50台JVM实例,每上传一次JVM数据产生18个Request,就会导致 50 * 18 = 900 个Request,也就是说,在没有处理任何TraceSegment的情况下,每秒就要产生900个Request
image.png
我这次是直接把1s修改成了30s,重新打包agent,部署、重启,查看日志发现发送到ES的Request相对来说是少了一些,但是ES的cpu占用并没有下降很多大概从300%下降至250%左右
ES的新生代配置的太小
由于我们是直接用docker起的ES,用的官方的镜像,官方镜像用的JDK12,在这个docker容器中jmap jstat并不能用,好在ES直接把gclog打印到了文件中,查看gclog才发现yonggc是如此的频繁,甚至不到1s一次
[2019-07-30T11:03:20.242+0000][1][gc,start ] GC(158) Pause Young (Allocation Failure)
[2019-07-30T11:03:20.242+0000][1][gc,task ] GC(158) Using 4 workers of 4 for evacuation
[2019-07-30T11:03:20.258+0000][1][gc,age ] GC(158) Desired survivor size 17432576 bytes, new threshold 6 (max threshold 6)
[2019-07-30T11:03:20.258+0000][1][gc,age ] GC(158) Age table with threshold 6 (max threshold 6)
[2019-07-30T11:03:20.258+0000][1][gc,age ] GC(158) - age 1: 954344 bytes, 954344 total
[2019-07-30T11:03:20.258+0000][1][gc,age ] GC(158) - age 2: 4864 bytes, 959208 total
[2019-07-30T11:03:20.258+0000][1][gc,age ] GC(158) - age 3: 2256 bytes, 961464 total
[2019-07-30T11:03:20.258+0000][1][gc,age ] GC(158) - age 4: 391800 bytes, 1353264 total
[2019-07-30T11:03:20.258+0000][1][gc,age ] GC(158) - age 5: 119784 bytes, 1473048 total
[2019-07-30T11:03:20.258+0000][1][gc,age ] GC(158) - age 6: 1456 bytes, 1474504 total
[2019-07-30T11:03:20.258+0000][1][gc,heap ] GC(158) ParNew: 275195K->2126K(306688K)
[2019-07-30T11:03:20.258+0000][1][gc,heap ] GC(158) CMS: 798450K->798452K(3853568K)
[2019-07-30T11:03:20.258+0000][1][gc,metaspace ] GC(158) Metaspace: 85950K->85950K(1128448K)
[2019-07-30T11:03:20.258+0000][1][gc ] GC(158) Pause Young (Allocation Failure) 1048M->781M(4062M) 15.861ms
[2019-07-30T11:03:20.258+0000][1][gc,cpu ] GC(158) User=0.06s Sys=0.00s Real=0.02s
[2019-07-30T11:03:20.258+0000][1][safepoint ] Leaving safepoint region
[2019-07-30T11:03:20.258+0000][1][safepoint ] Total time for which application threads were stopped: 0.0163155 seconds, Stopping threads took: 0.0001191 seconds
[2019-07-30T11:03:20.642+0000][1][safepoint ] Application time: 0.3842620 seconds
[2019-07-30T11:03:20.642+0000][1][safepoint ] Entering safepoint region: GenCollectForAllocation
[2019-07-30T11:03:20.643+0000][1][gc,start ] GC(159) Pause Young (Allocation Failure)
[2019-07-30T11:03:20.643+0000][1][gc,task ] GC(159) Using 4 workers of 4 for evacuation
[2019-07-30T11:03:20.659+0000][1][gc,age ] GC(159) Desired survivor size 17432576 bytes, new threshold 6 (max threshold 6)
[2019-07-30T11:03:20.659+0000][1][gc,age ] GC(159) Age table with threshold 6 (max threshold 6)
[2019-07-30T11:03:20.659+0000][1][gc,age ] GC(159) - age 1: 1309016 bytes, 1309016 total
[2019-07-30T11:03:20.659+0000][1][gc,age ] GC(159) - age 2: 38944 bytes, 1347960 total
[2019-07-30T11:03:20.659+0000][1][gc,age ] GC(159) - age 3: 2616 bytes, 1350576 total
[2019-07-30T11:03:20.659+0000][1][gc,age ] GC(159) - age 4: 720 bytes, 1351296 total
[2019-07-30T11:03:20.659+0000][1][gc,age ] GC(159) - age 5: 378152 bytes, 1729448 total
[2019-07-30T11:03:20.659+0000][1][gc,age ] GC(159) - age 6: 111352 bytes, 1840800 total
[2019-07-30T11:03:20.659+0000][1][gc,heap ] GC(159) ParNew: 274766K->2257K(306688K)
[2019-07-30T11:03:20.659+0000][1][gc,heap ] GC(159) CMS: 798452K->798453K(3853568K)
[2019-07-30T11:03:20.659+0000][1][gc,metaspace ] GC(159) Metaspace: 85950K->85950K(1128448K)
关键是无论怎么增大Xms XmX和deploy.resources.limits.memory都不管用,新生代的大小都是只有275195K这么大,下面是我们的ES启动配置
后来网上搜了之后才发现,原来CMS 默认的新生代并不是总堆大小的1/3,必须显式设置XX:NewRatio才可以。可以参考https://www.jianshu.com/p/832fc4d4cb53
于是手动设置XX:NewRatio=1,设置了之后发现yonggc频率变成大概6s一次,CPU占用也大概下降至180%左右,其实6s一次还是挺频繁,后续还是需要考虑增加ES的node,以及增加内存来分散压力
ES的高性能写模式
其实这块儿主要参考了https://blog.csdn.net/lengxiangwu/article/details/90445014,大家可以直接看这篇博文
或者参考官网:https://www.elastic.co/guide/en/elasticsearch/reference/6.8/tune-for-indexing-speed.html
核心就是修改index的设置, 个人实现发现主要起作用的是index.refresh_interval这个参数"index.merge.scheduler.max_thread_count" : "1", "index.refresh_interval" : "30s", "index.translog.durability" : "async", "index.translog.sync_interval" : "120s"
由于skywalking是通过template的方式创建index,一个一个修改template的配置比较麻烦,我就直接修改了skywalking的代码,添加了上面的配置,然后批量删除旧的与日期相关的index(之所以没有全部删除是因为全部删除的话需要重启agent), 重新打包、部署skywalking-server
org.apache.skywalking.oap.server.storage.plugin.elasticsearch.base.StorageEsInstaller
private JsonObject createSetting() {
JsonObject setting = new JsonObject();
setting.addProperty("index.number_of_shards", indexShardsNumber);
setting.addProperty("index.number_of_replicas", indexReplicasNumber);
setting.addProperty("analysis.analyzer.oap_analyzer.type", "stop");
//custom config for better es update performance
setting.addProperty("index.refresh_interval", "30s");
setting.addProperty("index.translog.durability", "async");
setting.addProperty("index.translog.sync_interval", "120s");
setting.addProperty("index.merge.scheduler.max_thread_count", "1");
return setting;
}
重启skywalking-server,查看es的index配置,发现配置已经生效
image.png
再次观察ES的CPU占用情况,已经降到了100%左右,至此 ES的CPU优化暂告一段落吧, 由于skywalking对ES的操作实在是太多了,想完全把ES的CPU降下来是不太现实的,只能考虑增加ES机器,减小采样频率了