快速导航
1. 引言
此文为自己工作中的记录,处理时间为2020年3月份,由于工作比较忙未能及时整理,如今趁着国庆放假整理出来,分享给大家,希望能够给遇到同样问题的朋友一些帮助,也希望能够给大家提供一些解决问题的思路。
2. 背景
公司maven仓库基于JFrog的Artifactory4.7.1社区版本搭建,从之前的本地服务器上整个打包移植到云上,无论是本地还是jenkins上都经常出现Read timed out打包失败的情况,
查看仓库日志,发现频繁的去远程下载maven-meta.xml配置文件以及其他一些私有的jar包,导致超时。
怀疑是配置的更新策略不恰当,导致经常去远程同步数据,由于目前没有管理员账号,不能确定。
有待进一步验证。。。。
3. 问题分析
要解决问题,首先得进行问题分析,需要通过走访问题提出人、查看日志、观察系统现场等手段来进行信息收集,以确定问题所在。如果能够一次性定位问题,那解决问题相对容易,往往很多问题并不能百分之百确定根本原因,特别是采用开源框架,这时需要进行反复试验和验证才能最终确定和解决问题。
2020-3-19 星期四
运维同学反馈前一日通过jenkins构建生产包出现拉取jar包失败的情况,而且以前也经常出现该问题,怀疑是maven仓库问题,要求协助处理(PS:公司maven仓库基于jfrog的社区版artifactory搭建,本来由另另外一位运维同事负责)。
本着解决问题的心态,而且对于该问题我也已经到了无法忍受的地步,看来该问题已经到了刻不容缓的地步。因此便决心接过来进行处理。
截取几次jenkins的流水线构建情况:
前一日构建出错日志:
流水线执行情况:
现象比较明显,构建失败的情况消耗时间都挺长(2分钟左右),就是构建成功,也经常消耗大量的时间。
2020-3-23 星期一
由于上周(3月18日)运维人员反馈生产jenkins打包出现Read time out现象,于是决心彻底解决该问题,周五要来artifactory的管理员账号,首先从配置入手,基本上都在网上查询相关资料,周五的定位点在于存储空间的优化,信心不足,没有动手。
周一上班继续artifactory配置相关最佳实践的资料查阅,依然从snapshot的保存周期、包管理有效期等方面入手,但依然证据不足。
然后转而直接查看jenkins的构建日志,发现出错时大量的Read time out,但是通过查看出现问题时artifactory主机的监控快照,没有发现任何性能方面的问题。结合artifactory.log日志,以及netstat访问情况,发现有比较多的远程异常访问,甚至本来应该从本地snapshot拉取的文件,也出现在远程拉取日志中,具体如下:
aaaa-开头的仓库文件均是本地snapshot的文件,为何全部转到remote仓库去拉取,这显然不是很合理,remote除了配置的阿里云为国内镜像外,其他几个都是国外网站,其网络情况可想而知。
通过云服务器上的netstat快照日志(腾讯云提供的每隔1小时的监控数据:/chkusr/osw/archive/oswnetstat)可以得到印证,访问出错时有大量的连接到国外的仓库镜像:
上图中除10开头的其他都是外部地址
分析了一下出问题的项目,都是采用公司新框架中统一的build.gralde文件,其中增加了对于快照版本snapshot的支持,即构建时除了连接原有release仓库外,还有连接snapshot仓库,公共的gradle脚本中配置了两条地址信息:
buildscript {
ext {
maven = [
snapshot: 'http://xxx.xxx.com:8081/artifactory/libs-snapshot',
release: 'http://xxx.xxx.com:8081/artifactory/libs-release'
]
dependencies = [
……
]
}
repositories {
maven {url maven.release}
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:2.0.5.RELEASE"
classpath "gradle.plugin.com.gorylenko.gradle-git-properties:gradle-git-properties:1.5.2"
classpath "io.franzbecker:gradle-lombok:1.14"
}
}
而刚好出现问题的都是拉取snapshot包,因此基本上确定目标是snapshot仓库的问题,通过管理台列表,每次点击本地的快照目录时,加载速度奇慢无比,几乎加载到内心崩溃:
在欣赏无敌风火轮旋转的同时,在风火轮的正下方,淡淡的remote吸引了我的注意,到此为止,我们可能已经找到了构建问题的“凶手”:远程仓库。
由于使用快照版本基本上都是公司内部项目,因此基本上可以确定在snapshot中不需要连接远程仓库,于是我开始实施验证我的想法,为了确保实施出错可以回退,也同时为后面做参考,进行了此次记录。
4. 问题解决
4.1 第一步snapshot移除remote(第一次尝试失败)
一、libs-snapshot虚拟仓库
修改前包含三个实际的仓库:
ext-snapshot-local, remote-repos, libs-snapshot-local
删除远程仓库remote-repos,修改后仅包括两个实际的仓库:
ext-snapshot-local, libs-snapshot-local
二、plugins-snapshot虚拟仓库
修改前包含三个实际的仓库:
plugins-snapshot-local, ext-snapshot-local, remote-repos
删除远程仓库remote-repos,修改后仅包括两个实际的仓库:
plugins-snapshot-local, ext-snapshot-local
然而,事情却不是我想的那么顺利,本地测试时,却又出现如下eclipse解析问题:
2020-03-23 16:20:42,828 [http-nio-8081-exec-1602] [INFO ] (o.a.r.HttpRepo :418) - aliyun downloaded http://maven.aliyun.com/nexus/content/groups/public/org/eclipse/platform/org.eclipse.core.filesystem/maven-metadata.xml 588 bytes at 116.89 KB/sec2020-03-23 16:21:28,573 [http-nio-8081-exec-1608] [WARN ] (o.a.r.v.i.t.PomTransformer:70) - Failed to parse pom ‘Failed to build dom document’:
于是怀疑是plugins-snapshot虚拟仓库还是会需要远程,因此将第二步还原,再次观察确认。
观察了一阵子日志,发现还是有报错信息,同时我注意到一个现象,本来是release发布的jar包,也会先去snapshot库去搜索一下,当找不到时再去release下载,这操作似乎不太合理。
20200323165508|2|REQUEST|10.30.122.64|anonymous|HEAD|/libs-snapshot/org/projectlombok/lombok/1.14.8/lombok-1.14.8.pom|HTTP/1.1|404|0
20200323165508|1|REQUEST|10.30.122.64|anonymous|HEAD|/libs-snapshot/org/projectlombok/lombok/1.14.8/lombok-1.14.8.jar|HTTP/1.1|404|0
20200323165508|1|REQUEST|10.30.122.64|anonymous|HEAD|/libs-release/org/projectlombok/lombok/1.14.8/lombok-1.14.8.pom|HTTP/1.1|200|0
回顾前面提到的gradle统一的构建脚本,其中buildscript配置了两个地址,一个snapshot,一个release,因此基本上可以确定是此处配置存在问题,导致重复查找,那么,是否有方法可以让gradle根据不同的版本去查找对应的仓库分支呢?
在查找解决方案的过程中,偶然在jenkins中看到某个系统大量的失败构建,于是,紧急将第一步也进行了还原操作。第一次尝试的优化宣告失败。
继续寻求解决方案。
现在时间2020-3-23 17:22分
由于影响了开发的正常构建,于是只能在下班时间再进行尝试,下一步思路,想减小一下修改步伐,由于出现超时较多的情况是连接jcenter、maven国外官网,刚好看到阿里的maven仓库里面也有这类服务器的代理,是不是可以先将这些国外网站的地址先修改为阿里的代理地址,看能否对构建效率有所提升。相当于使用阿里的国内地址代替其他几个国外仓库的地址,需要进行实验。
4.2 第二步,remote全部修改为阿里地址(第二次尝试初见成效)
2020-03-23 22:46分
一、apache
修改前地址:
http://repo.maven.apache.org/maven2
修改为阿里仓库central地址:
https://maven.aliyun.com/repository/central
二、central
修改前地址:
https://repo1.maven.org/maven2/
修改为阿里仓库central地址:
https://maven.aliyun.com/repository/central
三、gradle-plugin
修改前地址:
https://plugins.gradle.org/m2/
修改后地址:
https://maven.aliyun.com/repository/gradle-plugin
四、jcenter
修改前地址:
http://jcenter.bintray.com
修改后地址:
https://maven.aliyun.com/repository/jcenter
修改前配置:
修改后配置:
4.3 第三步,remote精简,只保留阿里public和groovy(第三次尝试失败)
从artifactory日志中可以看出,每次构建都会依次从各个远程仓库重复下载jar包,因此决定清理,删除如下远程仓库:
apache、central、gradle-plugin、jcenter
仅保留如下远程仓库:
aliyun: http://maven.aliyun.com/nexus/content/groups/public/ groovy:
https://dl.bintray.com/groovy/maven
处理后,尝试从eclipse中拉取gradle依赖,结果出现大量的从远程拉取依赖,导致阻塞,如下:
怀疑是删除了gradle-plugin的缘故,遂将其加回去。
编译工程时依然会连接远程,只好又尝试加回其他几个。
先加回central,测试。
虽然不再从远程下载,但是其中一个字符串处理的包org.apache.commons.lang3始终未拉取下来。
查看eclipse中默认使用的自带的gradle的wrapper版本,将其修改为本地的gradle4.10.2即可。
尝试使用命令行重新构建却失败,如下(地址已做脱敏处理):
$ date && gradle build -x test --refresh-dependencies && date
2020年03月23日 23:54:29
FAILURE: Build failed with an exception.
* Where:
Build file 'D:\workspace\xxx\build.gradle' line: 1
* What went wrong:
A problem occurred evaluating root project 'covertools'.
> Could not resolve all artifacts for configuration 'classpath'.
> Could not find gradle.plugin.com.gorylenko.gradle-git-properties:gradle-git-properties:1.5.2.
Searched in the following locations:
- http://xxx.xxx.com:8081/artifactory/libs-release/gradle/plugin/com/gorylenko/gradle-git-properties/gradle-git-properties/1.5.2/gradle-git-properties-1.5.2.pom
- http://xxx.xxx.com:8081/artifactory/libs-release/gradle/plugin/com/gorylenko/gradle-git-properties/gradle-git-properties/1.5.2/gradle-git-properties-1.5.2.jar
Required by:
unspecified:unspecified:unspecified
> Could not find io.franzbecker:gradle-lombok:1.14.
Searched in the following locations:
- http://xxx.xxx.com:8081/artifactory/libs-release/io/franzbecker/gradle-lombok/1.14/gradle-lombok-1.14.pom
- http://xxx.xxx.com:8081/artifactory/libs-release/io/franzbecker/gradle-lombok/1.14/gradle-lombok-1.14.jar
Required by:
unspecified:unspecified:unspecified
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
* Get more help at https://help.gradle.org
BUILD FAILED in 1s
于是又将剩余删除的远程仓库全部加回去。
2020-3-24 0点—1点20
刚过凌晨,准备进行一下回归验证休息,就被突如其来的错误给终止了。
在本地项目执行gradle build构建始终不成功,提示无法找到依赖插件。
gradle-git-properties-1.5.2.jar
gradle-lombok-1.14.jar
根据错误日志去仓库中查找,的确不存在,回看之前的操作,很快定位是由于删除了远程仓库导致仓库缓存的jar包不存在导致,但是,等等,似乎有些不合理,为什么没有自动从远程拉取呢?
尝试了使用其他版本的gradle、修改远程仓库配置均不奏效,只好将前一日备份的远程cache备份导入还原。
可是,问题依旧。
这个时候,已经到了凌晨1点多,artifactory设置的每日2点进行备份,如果2点前问题还不解决,就将再次进行备份,如果当前的仓库异常,则也会被备份覆盖。
于是,一股紧迫感涌上心头。网上各种搜索,未果。在一次无意间操作release的虚拟仓库时,发现仓库列表中的远程仓库依然只有aliyun和groovy两项,后面加回去的呢?
经过短暂回顾才恍然大悟。也许是半夜操作头脑短路,一时间竟然忽略了remote-repos这个虚拟的仓库,之前直接删除了几个远程的物理仓库,其关联关系也从虚拟仓库中解除了。当物理仓库加回去的时候,并没有自动还原这个关系。
因此手动将关系重新加回去,运行gradle构建脚本测试,成功。
这里其实犯了一个严重的错误,在操作远程仓库时,不应该直接删除物理仓库,既然artifactory设计了虚拟仓库的概念,我们就应该充分利用,正确的做法应该是将不需要的远程物理仓库从虚拟仓库中解除关系即可实现“第三步”的目标。
接下来基于虚拟仓库的思路,我准备重新分配另外一组完整的虚拟仓库,和原有的虚拟仓库并行,在不影响原有仓库的情况下进行试验,这样就不需要等到非工作时间才能操作了。
2020-3-24 9点28分
由于凌晨的小插曲,早上不到9点便来到公司,观察了一下,开发的构建没有受到影响,虽有惊无险,也给自己一个很好的警示:在没有明确的把握时,不要盲目行动。
4.4 第四步,重建虚拟仓库(本地试验成功)
一、新建虚拟仓库xxx_remote,包括aliyun、gradle-plugin两个远程物理仓库
二、新建虚拟仓库xxx_public,包括ext-release-local,ext-snapshot-local,libs-release-local,libs-snapshot-local,xxx_remote
三、修改gradle构建脚本,删除snapshot配置,将release的地址修改为新的仓库xxx_public的地址
执行gradle命令和使用eclipse(内置gradle的wrapper版本)均测试成功。
PS:为保持和原有命名统一,将两个虚拟仓库下划线修改为横线,即xxx-remote、xx-public
4.5 第五步,优化配置,进一步提升性能
一、修改ext-snapshot-local的参数:
Max Unique Snapshots: 5
二、修改libs-snapshot-local的参数:
Max Unique Snapshots: 5
三、修改plugins-snapshot-local的参数:
Max Unique Snapshots: 5
5. 总结
至此,本次artifactory问题解决。
解决问题有时候和侦探查案一样,复杂的问题定位是一个漫长的过程,需要极大的耐心,需要坚持不懈,我们也需要始终保持一颗好奇心,不放过任何一个细节,往往线索就在一点一滴的细节之处,当“案子”真相大白的时候,我们不仅解决了问题,既帮助了团队,也收获了成功的喜悦,成就了自我,互利双赢,何乐而不为呢。