云效、流水线、Gradle缓存问题、build.gradle配置snapshot

云效

大部分研发者们或许都听说过阿里云云效,甚至有不少使用经验;但是几乎很少有人会关注过云效的(子)域名:

如上图,云效的定位(愿景)就是为研发者们提供一站式的团队协作、效能提升的DevOps工具栈。云效包括如下几个产品,每个产品都在持续优化(新增功能、优化交互体验、修复Bug等)中,且会新增产品:

  • 项目协作:Projex,集成项目、迭代、需求、任务、Bug等管理功能,其他功能包括:里程碑、风险、度量、工作项、工时、路线图等;
  • 效能洞察:Insight,没用过;
  • 钉钉文档:DingTalk Docs,没用过;
  • 知识库:Thoughts,没用过;
  • 代码管理:Codeup,类似于GitHub,GitLab等;
  • 流水线:Flow,类似于Jenkins构建;
  • 制品仓库:Packages,对Java开发者而言,类似于Nexus仓库;
  • 应用交付:AppStack,流水线的进阶+高级版,鄙人后面会再写一篇AppStack使用经验文章;
  • 测试管理:Testhub,测试用例库,用于管理Test Case。

关于DevOps,推荐一本书《DevOps实践指南》,另外我写了一系列(共三篇)读书笔记,供参考阅读:

流水线

流水线隶属于云效的一个子产品,但是有一个独立的子域名flow.aliyun.com;也就是说,可直接打开流水线。另外流水线是对代码进行构建,也可从代码管理(Codeup,也有自己的独立子域名)里,对某一个代码仓库Repository,打开若干流水线。

一个Git repository可以创建若干个流水线:

选择某个流水线,不出意料,路径是pipelines,和Jenkins Pipelines理念一致:

如上图,功能稍微摸索一下就明白。流水线运行不一定会产生部署动作,云效留出两个Tab标签页,点进去也能发现运行历史个数大于部署历史个数。

另外点击页面的右上角,见名知意:

比较核心的操作在于上图左侧的【编辑】,默认进入流程配置标签页:

在【基本信息】里可设置【环境、标签、分组】,这样就方便在流水线子域名里对若干个不同的Git Repository进行统一管理:

在【触发设置】里可设置自动构建的触发逻辑:

在【变量和缓存】里可设置构建时需要使用的变量;当然也可以写死,比如说Harbor镜像仓库域名。但是如果小概率事件发生,比如Harbor域名需要变更,则需要更新域名。因此统一在流水线里设置全局的变量,然后所有的流水线都可以使用此变量。

终于要来到本文的重点,几流水线构建【缓存】,右上角有【清理缓存】功能可用于删除Gradle缓存:

什么场景下需要删除Gradle缓存?

Java开发里,经常遇到的场景,比如A应用依赖于B应用(通过OpenFeign,直接依赖于B应用提供的Client)或依赖于C框架。如果B或C应用使用正式版(比如,1.2,或1.1-release,或1.0-RELEASE),而且B或C应用发生变更,则C应用在构建时,需要删除本地缓存,否则依赖旧的版本,则会出现意料之外的问题。

当然云效流水线还有其他很多需要了解和调研的知识点。

Gradle缓存问题

如上图,第4次构建于3月6日(此次构建之前已经进入到流水线编辑页面,执行删除过Gradle缓存),但是当时没有注意到耗时仅47秒(删除过缓存,构建耗时绝对不可能只要几十秒)。

3.7日才发现服务异常:

也就是说,流水线里清除Gradle缓存,重新运行:

  1. 速度很快;
  2. 缓存没有被清理(应用运行失败,使用旧版本的依赖)

具体原因,还在钉钉群咨询阿里云云效支持。

排查

排查(试图解决问题)的尝试包括:

  • 重试法宝,如上上图的第5次构建,镜像版本还是有问题;
  • 修改流水线配置,再次运行流水线,镜像版本还是有问题;
  • 不开启缓存,理论上是第2行的Gradle缓存/root/.gradle/caches引发的问题,为了防止其他缓存(如/root/.cache)可能造成构建的镜像版本不对(主要还是对流水线的底层原理不太清楚),将其他缓存也关闭。再次运行流水线,镜像版本还是有问题;

解决方法

使用云效AppStack构建出一个镜像,如下图【第一次】执行耗时21分钟,比较正常,通过Helm使用该镜像版本后,没有问题:

但是

AppStack实际上还是基于流水线,增加统一的多环境管理能力,以及多分支构建和部署能力(比较适合于公有云+私有化场景)。

这一次AppStack构建镜像版本成功,不代表后面不会出现类似问题。

解决方案

上面使用AppStack确实可以解决问题,但是因为AppStack基于流水线,终究还是一个临时的解决方法。

分析

流水线和AppStack的构建,关于缓存配置,肯定是需要开启的,要不然构建会很慢,无法形成持续构建(CB,Continuous Build),不满足DevOps理念。

另一方面,某些被其他应用依赖的应用,比如被调用方,或框架组件,会不定期需要更新或调整逻辑。如果每次有更新,在流水线(或AppStack)里都需要手动删除缓存:

  • 非常麻烦
  • 容易忘记

思路

联想到Maven版本管理经验,有两类

  • RELEASE:可小写为release,也可省略,代表正式版本;
  • SNAPSHOT:可小写为snapshot,代表快照版本。

同样地,Nexus仓库中也分为release和snapshot。主要区别在于,本地获取这些依赖的机制有所不同。

假设应用依赖一个库的正式版本,构建时构建工具会先在本次仓库中查找是否已经有这个依赖库Jar包,没有的话才会去远程仓库中去拉取。而快照方式,则是默认每次都去远程仓库检查是否有更新版本的Jar包,有则下载缓存到本地仓库,并使用最新版本的依赖。

在团队协作中,A应用依赖于B应用提供的Jar。如果以release方式发布到公司内部Nexus仓库(或publish到云效制品仓库)的话,则A应用不能获取到接口的更新。

解决方法:

  • 手动去本地仓库删除这个Jar(对应于流水线构建前的手动删除缓存),然后重新下载;
  • 强制要求B应用每次更改Jar包时都发布一个新的版本,当然A应用也需要同步升级版本号。

无论哪种方式,都会显得很低效。此时就需要使用快照方式去发布版本。

使用snapshot方式,每次构建时去内部远程仓库检查是否有更新版本的snapshot快照版本,有则下载;但是这样会显得低效。

因此Maven提供以下机制控制检查快照版本的时间间隔,包括:

  • always:每次都去远程仓库查看是否有更新;
  • daily:只在当天第一次构建时检查是否有更新,其它时候则不会检查,默认配置值;
  • interval:设置一个分钟为单位的间隔时间,在这个间隔时间内只会去远程仓库中检查一次;
  • never:不会去远程仓库中查找,和正式版本行为一样。

pom.xml配置示例:

xml 复制代码
<repositories>
	<repository>
		<id>myRepository</id>
		<url>http</url>
		<snapshots>
			<enabled>true</enabled>
			<updatePolicy>daily</updatePolicy>
		</snapshots>
	</repository>
</repositories>

Gradle

当前这个Git Repository使用Gradle构建工具。类似地,在build.gradle配置文件里,可设置本地缓存的更新策略:

gradle 复制代码
configurations.all {
	// check for updates every build
	resolutionStrategy.cacheChangingModulesFor 0,'seconds'
}
dependencies {
	// api "com.testla:testla-security:1.1"
	implementation(group: 'com.testla', name: 'testla-security', version: '1.1-snapshot', changing: true)
}

总结:在开发模式下,可频繁地发布SNAPSHOT版本,以便让其它项目能实时使用到最新的功能做联调;当版本趋于稳定时,再发布一个正式版本,供正式使用。当然在做正式发布时,也要确保当前项目的依赖项中不包含对任何SNAPSHOT版本的依赖,保证正式版本的稳定性;否则可能不能发布成功(即校验是否含有SNAPSHOT依赖)。

这也是Maven的推荐规范和做法。

最后

当然,很多公司一般也允许在生产(正式)环境里使用SNAPSHOT版本的依赖;毕竟团队协作复杂后,关联方都需要同步升级版本也比较麻烦。建议根据公司、团队、项目、和业务的具体情况,case by case,按需选择。

参考Maven问题总结

相关推荐
身如柳絮随风扬10 小时前
Redis如何实现高效插入大量数据
数据库·redis·缓存
予早11 小时前
Redis 设置库的数量
数据库·redis·缓存
黑金IT11 小时前
vLLM本地缓存实战,重复提交直接复用不浪费算力
人工智能·缓存
Rick199313 小时前
Redis查询为什么快
数据库·redis·缓存
Rick199314 小时前
Redis 底层架构图
数据库·redis·缓存
Arva .15 小时前
Redis 数据类型
数据库·redis·缓存
笑我归无处16 小时前
Redis和数据库的数据一致性问题研究
数据库·redis·缓存
小红的布丁16 小时前
操作系统与高性能 IO:零拷贝、一次读 IO、CPU 缓存与伪共享
缓存
洒满阳光的午后16 小时前
一个基于Runbook的版本发布系统设计思路
devops·cicd
SPC的存折17 小时前
(自用)LNMP-Redis-Discuz5.0部署指南-openEuler24.03-测试环境
linux·运维·服务器·数据库·redis·缓存