文章目录
(一)概念简介
什么是CI/CD?
- 软件产业不成熟的时候,一个软件从零开始到最终交付,大概包括:规划、编码、构建、测试、发布、部署和维护。一个人可以完成上述所有工作。但随着软件的复杂度不断攀升,就开始出现了精细化分工。除了软件开发工程师之外,还有软件测试工程师,以及软件运维工程师。分工后对应的流程就变成了:开发–>测试–>部署,将整个流程细分之后,也就缩小了每个节点所需要的时间,大家各司其职,从而可以使整个软件的开发周期缩短,而且变得频繁,这也就是所谓的 “敏捷开发”, 那如何支撑敏捷开发呢?就要说到CI/CD了。
- CI :
Continuous Integration
(持续集成) - CD :
Continuous Delivery
(持续交付)或Continuous Deployment
(持续部署) - 这里所说的持续,其实就是反复不间断执行,并且加速这个过程。这就是敏捷开发,持续的走这一套流程:开发->测试->部署,相比于周期较长的瀑布式开发,这样提高了产品版本迭代的速度,也提高了开发团队的工作效率。
- 随之而来带来的问题就是,开发和运维之间的矛盾,敏捷开发对开发人员固然是好,因为可以及时检验自己代码的正确性,但是对于运维人员来说并不友好,在运维眼中,稳定比一切都重要,版本更新,意味着要打破这个稳定状态。这就是开发与运维之间的矛盾,
DevOps
就可以很好的解决这个问题。
什么是DevOps?
Development(开发)
和Operations(运维)
两个词的组合,可以将它理解为一组过程,或者是一种思想。DevOps
就是打破开发人员和运维人员直接的壁垒,通过自动化流程来使得软件开发过程更加快捷和可靠。目前市场中也出现了这个工种:DevOps工程师- DevOps不仅仅是简单的将Dev和Ops两个团队合并,而是彼此介入对方的工作,运维人员可以在早期开发的时候介入开发过程,了解所使用的架构以及技术,可以制定相应的技术方案,开发人员也可以参与到系统的部署中,提出优化建议
- DevOps这一概念是2009年提出来的,但是最近几年才火起来的,主要是因为虚拟化技术和容器这几年应用的很广泛了,而这两者,为DevOps提供了很好的前提条件。
做CI/CD,有什么好处?
- CI/CD是为了敏捷开发诞生的产物,敏捷开发可以更早的发现产品的问题,而不是等到所有功能都做完之后,再来审查这个产品哪里有不合适的地方。
- 那我目前所经历的流程举例。公司在所CI/CD之前的开发流程是这样的:开发人员在自己的分支开发完成之后,提交到gitlab上,并提
mearge request
到master
主分支上,然后开发老大来进行代码的审查review
,review
之后,再由老大去mearge
到主分支上,接着再去服务器上执行脚本,重新部署服务,这样一套下来,花费的时间就很多,因为不老大忙的很。而且由于是人工审查代码,有的时候看不出来哪里有问题,一到真正编译的时候就报错了。这个过程是很耗费时间,且效率低下的。 - 有了CI/CD之后,这个流程就变得自动化,利用工具做CI/CD,基本上都是一样的原理:当指定的某个分支有代码
mearge
过来的时候,就会触发CI/CD,最基础的步骤就是单元测试,以及自动构建。这个过程中一旦出现一点错,都会失败,所以对代码规范的要求极高。但是这样无感式的测试与构建,节省了大量的时间,也提高了开发效率。
(二)实现CI
- 流程简介
- 在仓库根目录创建一个
.gitlab-ci.yml
文件,定义GitLab runner
要做哪些操作。 默认有3个stages阶段:build
、test
、deploy
。 - 为该项目指派一个
Runner
,当有合并请求或者 push的时候,Ranner
就会自动开始pipeline
(管道操作)。 - 当build完成后(返回非零值),会看到
push
的commit
或者mearge request
前面出现一个绿色的对号。 这个功能很方便的检查出本次合并请求是否会导致build失败。
配置Ranner
1. 安装GitLab-Ranner
#在ubuntu server16.04版本下使用命令安装
$ sudo wget -O /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64
#授予可执行权限
$ sudo chmod +x /usr/local/bin/gitlab-runner
#创建一个gitlab-ci用户
$ sudo useradd --comment 'GitLabRunner' --create-home gitlab-runner --shell /bin/bash
#安装,并作为服务启动
$ sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner
2. 注册Ranner
- 执行命令:
sudo gitlab-runner register
,输入gitlab的url
和token
(在Setup a specific Runner manually
中找) - 启动
Ranner
:gitlab-runner start
命令不行的话就用gitlab-runner run
- 启动成功后看:仓库->settings->CI/CD->Ranner Settings->点击Expend,看Ranner是否运行,绿色代表运行中。
.gitlab-ci.yml文件
- 当有新内容push到仓库后,GitLab会查找是否有
.gitlab-ci.yml
文件,如果文件存在,Runners
将会根据该文件的内容开始build
本次commit
。采用YAML语法, 注意缩进格式是空格,不能用tab键缩进。 - 这是最基础的脚本文件,还有很多功能集成的话,也可以写入。注意,这个里面的tag是要和ruanner中的tag一样,才能更具项目找到
runner
去执行pipeline
。 .gitlab-ci.yml
示例
stages:
- build
- test
before_script:
- echo "Restoring Packages..."
build_job:
stage: build
script:
- echo "Release build..."
except:
- tags
test_job:
stage: test
script:
- echo "Tests run..."
- cd cds.ci.test
- gradle test
(三)代码评审
- SonarQube简介
- "烂代码"会导致浪费时间,延误进度,后期也不好维护。所以从一开始就要规范出优质代码。有很多代码扫描工具可使用,这里就选择
SonarQube
。 - 可检查项目代码的漏洞和潜在逻辑问题。提供了丰富的插件,支持多种语言的检测, 如 Java、Python、Groovy、C#、C、C++等几十种编程语言的检测。
- 检查核心:
- 是否遵循编程标准:如命名规范,编写的规范等。
- 设计是否存在的潜在缺陷:SonarQube通过插件Findbugs、Checkstyle等工具检测代码存在的缺陷。
- 代码的重复代码量:SonarQube可以展示项目中存在大量复制粘贴的代码。
- 代码中注释的程度:源码注释过多或者太少都不好,影响程序的可读可理解性。
- 代码中包、类之间的关系:分析类之间的关系是否合理,复杂度情况。
安装SonarQube
# 1. 获取postgresql的镜像
docker pull postgres
# 2. 启动postgresql
docker run --name postgresDB -e POSTGRES_USER=sonar -e POSTGRES_PASSWORD=sonar -d postgres
# 3. 获取sonarqube的镜像
docker pull sonarqube
# 4. 启动sonarqube
docker run --name sonarqube --link postgresDB -e SONARQUBE_JDBC_URL=jdbc:postgresql://db:5432/sonar -p 9000:9000 -d sonarqube
# 5. 将sonar-gitlab-plugin-2.1.0.jar添加到docker中的sonarqube容器中
docker cp sonar-gitlab-plugin-2.1.0.jar sonarqube:/opt/sonarqube/extensions/plugins
# 6. 打开localhost:9000,可以看到ui界面
集成Gitlab
- 在build.gradle中添加配置,启动sonarqube插件
- 配置sonar系统认证权限信息,以便将分析结果上传到sonar中展示
- 运行分析命令:gradle sonarqube,然后就可以在sonarqube页面上看到分析结果。
plugins {
# plugins段放置位置有要示,放在buildscript段前面会报错,放到文件最末尾也报错,紧跟buildscript放置OK
#添加插件信息
id "org.sonarqube" version "2.6-rc1"
}
apply plugin: "org.sonarqube"
subprojects {
sonarqube {
# 如果同时存在src/main/java与src/main/test,则要按以下方式设置,如果没有单元测试用例目录test,也可以只填写src
properties {
property "sonar.sources", "src/main/java"
}
}
}
#配置sonarqube的权限信息
sonarqube{
properties {
property "sonar.host.url", "http://192.168.90.58:9000"
property "sonar.projectName", "${artifactIdParam}"
property "sonar.projectKey", "${artifactIdParam}"
property "sonar.projectVersion", "${versionParam}"
property "sonar.login","admin"
property "sonar.password","admin"
}
}
编写.gitlab-ci.yml脚本,增加执行代码审查的脚本
code_review
stage: test
script:
- echo "Start reviewing code"
- gradle sonarqube
(四)实现CD
- 实现自动化部署,就要在
.gitlab-ci.yml
中指定执行的脚本,与自动化测试类似。 - 注释:only指定了只有在master分支push的时候才会被执行。tags是对应注册ranner时候的tag。(这相当于唯一标识)
在实际开发中要通过一个shell脚本来将资源自动部署到指定位置,在script中添加一个脚本:
stages:
- deploy
deploy:
stage: deploy
script:
- echo "start deploy....."
- ./deploy.sh
only:
- master
tags:
- iot
- script中的deploy是编写的shell脚本,可以实现将要发布的内容自动部署到发布目录下:
#!/bin/bash
deploy_path="/home/..."
project_path="gitlab_ci_cd_test";
judge_path = "$deploy_path/$project_path"
if [ ! -d "$judge_path" ]
then
project_url="http://XXXXcom/daq/gitlab_ci_cd_test.git"
git clone $project_path $deploy_path
else
cd $deploy_path
git pull
fi
(五)钉钉消息推送
- 钉钉自定义机器人,获取webhook
- 编写脚本,在打包构建之后执行
# 前一个命令执行状态判断是成功信息还是失败信息
if [ "$?" -eq "0" ];then
log "[OK]"
DEPLOY_SYSTEM="${!YZT_ENV_SERVER_IP2}:${!YZT_ENV_SERVER_PORT2}"
sendDingTalkSuccessNotifications
else
logStep ">> $?"
DEPLOY_SYSTEM="${!YZT_ENV_SERVER_IP2}:${!YZT_ENV_SERVER_PORT2}"
sendDingTalkErrorNotifications
fi
# 相关脚本
function sendDingTalkErrorNotifications() {
DEPLOY_STATUS='部署失败!'
sendDingTalkNotifications
}
function sendDingTalkSuccessNotifications() {
DEPLOY_STATUS='部署成功!'
sendDingTalkNotifications
}
# 推送模板发送(模板拼接)
function sendDingTalkNotifications() {
logStep " STEP 5 - Send Notifications to DingTalk"
local title="「前端CI/CD」 ${PROJECT_NAME}"
local text="### ${title} \n #### 构建分支:${CI_COMMIT_REF_NAME} \n #### 构建状态:${DEPLOY_STATUS}\n #### 部署主机:${DEPLOY_SYSTEM} \n #### 提交者:${GITLAB_USER_EMAIL} \n\n\n ##### [流水线 Pipeline #${CI_PIPELINE_ID}](${CI_PROJECT_URL}/pipelines/${CI_PIPELINE_ID}) \n"
curl POST "$CI_DINGTALK_WEBHOOK_URL" -H 'Content-Type: application/json' -d "{\"msgtype\": \"markdown\",\"markdown\": {\"title\":\"$title\",\"text\": \"$text\"}}"
# curl POST "$CI_DINGTALK_WEBHOOK_URL" -H 'Content-Type: application/json' -d '{ "msgtype": "markdown", "markdown": {"title":"CI/CD cmp-web","text": "##### 构建分支:test \n Pipelines状态:成功\n ######## [流水线Pipeline #3181](http://git.1ziton.com/front-end/cmp-web/pipelines/3181) \n"}}'
}
function log() {
echo "$(date):$@"
}
function logStep() {
echo "$(date):====================================================================================="
echo "$(date):$@"
echo "$(date):====================================================================================="
echo ""
}