记一次大型微服务项目本地打包迁移部署
- 引
- 代码合并
- 发布过程
- 本地部署
- 服务配置
- 服务打包自启动测试
- 外部依赖
- 排除部分外部依赖
引
服务的运维也是一个挺复杂工作,如项目上线后的一次小版本发布,开发人员需要基于工程最新代码拉取feature分支,本地修改、测试,然后合并发布到类如sit、uat等non-prod环境交由测试人员测试。复测成功后,多个feature分支有序合并到prod分支进行定期发布。
代码合并
生产发布的第一步肯定是生产分支代码的合并更新,同一个基础分支,可能你也在改,我也在改,后合并的那次commit可能就会出现conflict,我个人更喜欢在本地强制合并再处理冲突。除了merge,还有cherry-pick命令也可用于代码合并,比如有一个分支的代码需要合并到prod,但这个分支的代码与prod的gap较大,但我只想合并其中的一两次提交到prod,那这个时候就可以使用cherry-pick命令。
发布过程
代码的修改合并是修复生产缺陷或者是发布新功能最重要的一环(我个人这样认为),通常项目会通过gitlab或者是github做代码分布式版本管理,对的,github也可以本地部署。在敏捷开发过程,可以使用类如jenkins等工具,流水话的执行代码拉取,打包,上次,启动,而上线过后,使用devops k8s来管理监控服务已成为主流,如redhat的openshift。根据生产分支的最新代码制作镜像,然后创建deployment动态创建或销毁服务最小单元pod。
本地部署
之前参与的一个大型微服务项目需要本地化部署,做后续展示备用,当然页面和数据也需要脱敏。
服务配置
服务迁移最头疼的就是配置了,特别是没有使用配置中心的项目,绝大部分配置都放到了yaml配置文件中,然后通过profile执行环境。如果使用了配置中心则直接修改对应key就行了,如果没有,就只能一个个服务去修改了,这是一个苦力活儿。过程中需要注意value的缩进,有时候CV过去缩进就有问题,系统无法识别,启动报错。
数据库如果版本不同,可能需要注意一下驱动配置是否可用。如果是切换了数据库类型,如mysql换成了oracle,那还得检查代码里面的明文sql语法是否通用了。还有奇葩的,如提供给你的数据库主机没有外网ip,这个可以通过jcsh来解决,参考云数据库没有公网ip如何访问,navicate ssh隧道 + com.jcraft.jsch。redis和mq我感觉也可以这样去尝试解决,目前还没有测试过。
服务打包自启动测试
如上面提到的,提供给我们的redis就没有外网ip,所以本地测试不了服务是否能正常启动(全部使用目标配置)。但是,按我理解,打包是不需要测试项目能否正常启动的吧。这里我使用的是Idea来打包,如果是直接使用mvn命令来打包应该不会遇到这个问题。Idea有个跳过测试的开关,把这个关掉就可以了。
外部依赖
当前打包的A工程依赖了B工程,在打包A时,需要把B也引入进来,否则A在启动时会报错。线上打包肯定是使用的remote nexus已经deploy的最新依赖,本地打包也可以使用本地install的依赖,当然agv一定得对得上。默认情况,使用spring-boot-maven-plugin似乎并不会把这些依赖打到一个包里,可以添加includeSystemScope属性,设置为true。
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration> <includeSystemScope>true</includeSystemScope>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
排除部分外部依赖
同样是外部依赖,有些我们并不想打到一个包里。如A依赖的B,B依赖了一个B1,这个B1不需要引入进来,并且引入进来后因为缺失相关配置,服务启动同样会报错,如apollo或者是链路追踪的一些依赖。你可以在A引入B的地方,手动排除掉B1。如下。
<dependency>
<groupId>com.roswu</groupId>
<artifactId>B</artifactId>
<version>1.0.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>com.roswu</groupId>
<artifactId>B1</artifactId>
</exclusion>
</exclusions>
</dependency>
这样做可以达到目的,也很通俗易懂。但有个问题是,如果A依赖了C/D/E/F…同样依赖了B1,这个时候再每个都去exclusion一下就很麻烦了。好在spring-boot-maven-plugin也支持exclusions属性,我们可以统一配置在这里,这样一来,打包的时候就会统一过滤掉B1依赖。
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration> <includeSystemScope>true</includeSystemScope>
<exclusions>
<exclusion>
<groupId>com.roswu</groupId>
<artifactId>B1</artifactId>
</exclusion>
</exclusions>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>