CI/CD 实践 基于华为云 DevCloud

CI/CD 实践 基于华为云 DevCloud

协作开发、CI/CD实践

项目背景

本项目为基于Spring Boot + VUE的便利店内外部综合管理系统。采用前后端分离式独立开发,前后端事先约定请求接口。前端先用假接口如fastmock开发,待相应接口上线服务后再接入api接口。

api接口

本项目采用Apifox作为api接口文档平台。Apifox 是接口管理、开发、测试全流程集成工具,集成了 Postman + Swagger + Mock+ JMeter 等常用接口开发软件的功能。通过一套系统、一份数据,解决多个系统之间的数 据同步问题。只要定义好接口文档,接口调试、数据 Mock、接口测试就可以直接使用,无 需再次定义;接口文档和接口开发调试使用同一个工具,接口调试完成后即可保证和接口文 档定义完全一致。

image-20230201212916356

后端

后端使用的技术栈包括Spring Boot+Mybatis为基础web框架,Redis做缓存、Spring Security和JWT做认证授权、对象存储OSS存储文件 。

前端

前端使用 VueCli 进行开发,使用 AntDesign 组件库进行前端页面需求布局,独立封装 axios ,使用 typescript 进行逻辑处理,能更好地进行模块化开发,使用vuex和loaclStorage进行用户信息和token的存储。

协作开发

华为云中提供了代码托管服务。

image-20230201212927424

配置密钥

华为云代码托管首先要配置ssh密钥,要不然拒绝你的push找不到原因。

image-20230201212938570

保护分支

我们的项目采用master保护分支模式,在功能开发中统一使用功能分支+合并请求的方式进行功能的开发与合并。需要在保护分支设置中设置了master分支为所有人只能合并、不能提交的模式。如下图。

image-20230201212947956

开发新功能

当开发新功能时,需要创建新的分支,所有修改的代码均提交在新建的分支中。也可以在本地维护自己的开发分支,并确保开发新功能时自己的开发分支与最新的master分支相同。在此处使用华为云web界面进行新建分支演示。

  • 点击分支--新建分支,基于master分支,分支名称 华为云推荐采用feature工作项编号的名称(注意:不要有+号、空格等特殊符号),点击确定。

image-20230201213000968

  • 在ide中切换到开发分支,进行开发。

image-20230201213009743

  • 开发完成后填写提交注释信息,华为云推荐格式为fix #工作项编码 更多信息,push到远端仓库。

image-20230201213015979

  • 在开发完成并测试验证完毕后,进行分支合并。点击合并请求 --> 新建合并请求 --> 选择待合并的分支 --> 下一步 --> 完善相关信息(可选合入后删除源分支) --> 确定 --> 完善相关信息(可选) --> 合入

image-20230201213028363

image-20230201213033676

image-20230201213037751

image-20230201213044759

注意: 在合并分支之前必须保证源分支代码能正确运行

持续集成

后端

本项目后端采用maven为构建工具,由于需要部署到服务器,为了方便操作,将打包成两份成品。一份为单纯的jar包放在华为云的发布仓库中做备份,另一份打包成docker镜像并推送到阿里云镜像仓库。

image-20230201213055740

打包jar包

华为云中提供了打包jar包并发布到发布仓库的模板。

只需点击编译构建 --> 新建任务 --> 选择好代码源 --> 下一步。就出现maven模板。

image-20230201213101362

image-20230201213107324

image-20230201213112207

在构建步骤中可以自己调整一些选项,华为云模板的注释可以帮助我们理解步骤有什么用。我们在实践中此没有修改他的配置。

# 参数说明:
#		-Dmaven.test.skip=true:跳过单元测试
#		-U:每次构建检查依赖更新,可避免缓存中快照版本依赖不更新问题,但会牺牲部分性能
#		-e -X :打印调试信息,定位疑难构建问题时建议使用此参数构建
#		-B:以batch模式运行,可避免日志打印时出现ArrayIndexOutOfBoundsException异常
# 使用场景: 打包项目且不需要执行单元测试时使用
mvn package -Dmaven.test.skip=true -U -e -X -B

#功能:打包;执行单元测试,但忽略单元测试用例失败,每次构建检查依赖更新
#使用场景: 需要执行单元测试,且使用构建提供的单元测试报告服务统计执行情况
# 使用条件:在”单元测试“中选择处理单元测试结果,并正确填写测试结果文件路径
#mvn package -Dmaven.test.failure.ignore=true -U -e -X -B

#功能:打包并发布依赖包到私有依赖库
#使用场景: 需要将当前项目构建结果发布到私有依赖仓库以供其他maven项目引用时使用
#注意事项: 此处上传的目标仓库为Devcloud私有依赖仓库,注意与软件发布仓库区分
#mvn deploy -Dmaven.test.skip=true -U -e -X -B

打包docker镜像

为了服务部署的方便和易迁移性,本项目采用docker镜像方式进行部署,因此需要打包docker镜像。

在实践中发现,若使用华为云容器镜像服务swr作为镜像仓库,他人(非仓库所有人)触发构建功能时不能把打包好的镜像上传到仓库,提示无权限,且swr不能在华为云构建服务器上设置静态密码。因此我们选择使用阿里云容器镜像服务。

docker基本操作和容器镜像仓库基本操作在此不再赘述。

  • 在阿里云容器镜像服务设置固定密码

image-20230201213123497

  • 创建镜像仓库(可能需要创建命名空间,在此不赘述)

image-20230201213130110

  • 学习操作指南(很重要,下面大量用到这些命令)

image-20230201213138448

  • 在项目根目录下添加Dockerfile

本项目的Dockerfile如下,仅供参考。

# 以java8为基础镜像
FROM java:8
# 把./target/*.jar复制到/home/springboot/app.jar里
ADD ./target/*.jar /home/springboot/app.jar
# 设置时区,防止Calendar与北京时间差8小时
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' >/etc/timezone
# 指定端口
CMD ["--server.port=8080"]
# 暴露端口
EXPOSE 8080
# 运行jar包的命令 --spring.profiles.active=sit激活sit的配置文件
ENTRYPOINT ["java","-jar","/home/springboot/app.jar","--spring.profiles.active=sit"]
  • 在华为云中新建构建任务,可以选择maven模板(同上小节)
  • 删除上传软件包到软件发布库步骤,添加执行docker命令步骤

image-20230201213148280

  • 添加如下命令
#[build]
-t registry.cn-xxxxx.aliyuncs.com/username/xxx:xxx .
#该命令是用于打包并打上标签,需要项目根目录下的Dockerfile正确
#注意命令最后面的点不是多余的
#标签为阿里云容器镜像服务的标准格式
#具体-t后面的内容可以参考上面的阿里云操作指南
#注意版本号最好是固定的
########################################
#[login]
--username=阿里云用户名 registry.cn-xxxx.aliyuncs.com -p ${aliyun_docker_pw}
#该命令是用于登录阿里云容器镜像服务仓库的
#详细命令参考上面的阿里云操作指南
#${aliyun_docker_pw}是取变量的意思,变量可以在参数设置中设置好上面的固定密码,保护隐私
########################################
#[push]
registry.cn-xxxx.aliyuncs.com/xxx/xxx:xxx
#该命令是用于推送镜像到阿里云镜像仓库
#详细命令参考上面的阿里云操作指南

image-20230201213158754

image-20230201213204532

  • 完成,测试

前端

  • 创建构建任务,使用npm模板(同后端的打包jar包)

image-20230201213209744

  • 调整npm构建步骤

把最后一行#tar -zcvf demo.tar.gz ./换成tar -zcvf dist.tar.gz ./dist

目的是为了把dist文件夹完整地打包起来

image-20230201213215191

  • 调整上传软件包到软件发布库步骤

把构建包路径改为刚刚打包出来的文件./dist.tar.gz

image-20230201213227615

持续部署

后端

本项目使用docker部署,持续集成步骤中已经将镜像推送至阿里云。

docker基本操作和linux基本操作在此不再赘述。

  • 添加主机组,设置 --> 通用设置 --> 主机组管理 --> 新建主机组 --> 按要求填写信息 --> 添加主机 --> 按要求进一步填写主机信息

image-20230201213234210

image-20230201213238983

  • 新建部署任务(使用空白模板)
  • 编辑部署步骤(新增执行shell命令)

下为我的部署shell命令,仅供参考。

# 停止之前运行的服务 cvcserver可以自己改docker的基础
docker stop cvcserver
# 删除之前运行的服务 也可以用-f一步代替两部
docker rm cvcserver
# 删除之前的镜像
docker rmi registry.cn-xxxx.aliyuncs.com/xxx/xxx:sit
# 登录阿里云容器镜像服务 参考阿里云的操作指南 ${aliyun_pw}同样要在参数设置里设置
docker login --username=xxxxx registry-vpc.cn-xxx.aliyuncs.com -p ${aliyun_pw}
# 拉取镜像
docker pull registry.cn-xxx.aliyuncs.com/xxx/xxx:sit
# 运行镜像 --network myapps是我自己服务内网络
docker run --network myapps -p 8080:8080 --name cvcserver -d registry.cn-xxx.aliyuncs.com/xxx/xxx:sit

image-20230201213247245

image-20230201213251619

  • 测试,完成

前端

  • 创建部署任务,选择空模板(参考后端部署)
  • 添加步骤选择部署来源

源选择构建任务,构建序号Lastest,下载到自己主机的一个可以临时存放的位置

image-20230201213257381

  • 添加步骤解压文件

把上一步下载下来的dist.tar.gz解压到临时目录

image-20230201213304999

  • 添加步骤删除文件

删除原来的前端文件(nginx配置好的目录)

image-20230201213312795

  • 添加步骤拷贝文件

从临时文件夹中解压的dist内容复制到nginx配置好的目录

image-20230201213319357

  • 添加步骤执行shell命令

给前端文件授权777全开,防止出现nginx403错误

chmod -R 777 /home/docker/nginx/www

image-20230201213326113

  • 添加步骤删除文件

删除临时文件夹里的文件

image-20230201213331994

  • 所有的步骤的图放一下在这

其实这些步骤完全可以编成一个shell命令,不过这样更清晰,好调试

image-20230201213339591

流水线

后端

  • 新建流水线 --> 选择代码源和空白模板
  • 编辑符合自己需求的工作流

我的工作流如下,先经过代码检查,然后执行两个构建任务,再执行部署任务,最后测试url连通性。

image-20230201213346688

  • 开启流水线自动触发,流水线编辑界面 --> 代码源 --> 更多设置 --> 触发事件 --> master分支在代码提交时触发

image-20230201213352098

  • **重要:**记得关闭检查任务、构建任务、部署任务里的自动触发,要不然流水线自动执行一次,任务本身也自动执行一次,造成冲突,搞出奇奇怪怪的bug。

前端

同上,在这只放一下图

image-20230201213358243

总结

​ 要多与后端沟通,确定好每个接口的数据类型和请求类型。

​ 无论是否有使用js框架,还是不太建议使用jsp进行页面开发。合作性的项目前后端要坚持分离,不然耦合性太大,前后端可能会互相影响导致项目迭代速度太慢。

Licensed under CC BY-NC-SA 4.0