线上jenkins job CPU 使用率飙高问题调查分析

背景

线上环境的jenkins job通过master机器去拉起slave VM运行的,在实际使用中,该job会偶发性的出现CPU使用率瞬间升高,导致job被系统kill掉的情况。

现象

我们的PR会自动触发jenkins job, job的主要内容是: 编译,单元测试,打包。编译阶段通常能正常通过,因为我们的project比较大,所以给的MAVEN-OPT也比较充足: 4g堆内存,1g初始 metaspace。单元测试是通过maven surefire plugin做的,在运行过程中会出现CPU暴涨,然后接着就是GC overhead exceed limitaion的异常或者直接显示killed(可以确认不是人为kill)。

调查

该问题不定时出现,有时候出现之后会持续多天,上次出现距今半年有余,当时和平台组小伙伴一起调查,通过top命令可以看到在执行到某一步的时候CPU使用率突然暴涨,接着进程就被终止了,当时根据提示原因怀疑是GC的问题,所以一开始不断增加内存,寄希望于通过资源解决问题,但是不管内存增加一倍还是两倍,都不行,起的VM的内存已经增加到12G了,依然不能解决问题。

接下来观察到出现问题的时间点都是在fork这个动作时,这里要简单说下maven surefire plugin的工作过程,这个插件在运行单元测试的时候是通过fork VMs来运行的,我们项目里的配置如下:

1C
once
true
-Xverify:none -Xms512m -Xmx512m

这里有一个参数比较重要:forkCount,如果是纯数字,那么会启动对应数量的VM,如果以C结束,则会根据(机器的CPU核数 * 前面数字)来启动对应数量的VM. 当时平台组小伙伴登录到jenkins VM上可以看到运行fork的一瞬间有16个unit test VM被fork出来,每个VM都有0.5G的空间,如果再加上其他的空间如metaspace,那是很高的一个数字了,特别是fork后马上会有unit test运行,如果UT本身也比较消耗空间,这个时候GC很高就是一个可以预见的事情了,所以觉得可以通过减少forkCount和增加每个fork出来的VM的大小来尝试解决问题。所以是把forkCount从1C改到0.5C,把512m改成了1g。从总量上来看消耗的空间跟修改之前一样,但是因为vm减少了,更多的空间是交给了UT,并且更少的VM意味着更少的UT同时运行,所以GC的频率应该要减少一些。
尝试新的配置之后发现确实问题解决了,并且这个job一直平稳运行,直到最近。

再次出现

最近修改了代码,升级了依赖包,因为这个依赖包里面有大量第三方依赖并且包本身的代码量也在快速增加,所以升级完之后又出现了之前的问题,问题的表现基本一致,不同的点是这次不是在fork的时间点出问题了,而是可能在任意时间点。。

一开始觉得可以继续降低forkCount解决问题,把forkCount改成了固定数字:4, 也就是每次都只起4个VM来跑UT,无效。然后尝试把fork出来的VM空间增大,从1G改成了2G(机器VM是12G),仍然无效。本地使用类似的配置甚至更低的配置都能成功跑过,这个时候已经没有头绪了。平台组小伙伴登录到VM上也只能看到CPU使用率很高,但是不能确定GC发生的位置。

在jenkins页面上发呆的时候突然看到有Thread Dump的按钮,点了一下试试,结果还真的把jenkins VM的thread dump出来了,马上用fastthread.io分析了一下,发现maven的cli里面的一个类DefaultMirrorSelector的一个方法:

stackTrace:
java.lang.Thread.State: RUNNABLE
at java.lang.String.substring(String.java:1969)
at java.lang.String.split(String.java:2353)
at java.lang.String.split(String.java:2422)
at org.eclipse.aether.util.repository.DefaultMirrorSelector.matchesType(DefaultMirrorSelector.java:206)
at org.eclipse.aether.util.repository.DefaultMirrorSelector.findMirror(DefaultMirrorSelector.java:101)
at org.eclipse.aether.util.repository.DefaultMirrorSelector.getMirror(DefaultMirrorSelector.java:59)
at org.eclipse.aether.internal.impl.DefaultRemoteRepositoryManager.aggregateRepositories(DefaultRemoteRepositoryManager.java:155)

在多次dump过程中一直占用很高的CPU,这个时候才怀疑到maven的头上,跟本地的maven版本对比了一下,本地是3.6.0而线上是3.3.9。平台组小伙伴尝试用3.6.0版本的maven搭建了一个新的jenkins pipeline并尝试build,成功了!

至此该问题基本上确认了,是低版本的maven存在的问题。

总结

jenkins的报错信息比较误导人,误认为是GC的问题,但是通过thread dump发现CPU的飙升其实是maven的问题,所以分析这种CPU飙升的问题的时候先做几次thread dump总是没错的。

你可能感兴趣的:(javacpu过高)