什么是CICD
CI/CD 是由 CI (Continuous Integration 持续整合) 以及 CD (Continuous Delivery/Deployment 持续交付/部署) 两个字所组成,其核心概念是持续集成、持续交付和持续部署。
CI/CD 可让持续自动化和持续监控贯穿于应用的整个生命周期(从集成和测试阶段,到交付和部署)。
CI/CD的产生
软件开发的早期,软体的更新都是一次大版本的更新。对于开发者来说,是一次把完整的功能都做完、测试完,才会上线交付给使用者。
但是近些年,软件开发的模式在发生转变,尤其是自 2001 年,软体开发业界有 17 位资深专家,齐聚一堂发布了敏捷宣言(The Agile Manifesto) 倡议,进而衍伸出后来的敏捷开发,而 CI/CD 也可说是这之后出来的词汇。
敏捷宣言十二项原则
- 我们最优先的任务,是透过及早并持续地交付有价值的软体来满足客户需求。
- 竭诚欢迎改变需求,甚至已处开发后期亦然。 敏捷流程掌控变更,以维护客户的竞争优势。
- 经常交付可用的软体, 频率可以从数周到数个月, 以较短时间间隔为佳。
- 业务人员与开发者必须在专案全程中天天一起工作。
- 以积极的个人来建构专案, 给予他们所需的环境与支援, 并信任他们可以完成工作。
- 面对面的沟通是传递资讯给开发团队及团队成员之间效率最高且效果最佳的方法。
- 可用的软体是最主要的进度量测方法。
- 敏捷程序提倡可持续的开发。赞助者、开发者及使用者应当能不断地维持稳定的步调。
- 持续追求优越的技术与优良的设计, 以强化敏捷性。
- 精简 ── 或最大化未完成工作量之技艺 ── 是不可或缺的。
- 最佳的架构、需求与设计皆来自于能自我组织的团队。
- 团队定期自省如何更有效率,并据之适当地调整与修正自己的行为。
在这个宣言下,软体开发不再是一次完成一整包后才交付,而是“及早”交付,且开发者需要持续接收回馈,根据持续演进与变化的需求持续地开发。在这个概念下,我们需要一个能够落地的具体方式,而 CI/CD 正是落地的手段之一。
CI 持续集成(Continuous Integration)
协同开发是目前主流的开发方式,也就是多位开发人员可以同时处理同一个应用的不同模块或者功能。
因为涉及到多人开发,所以不可避免的需要在开发结束后进行整体代码的即成合并,而随着开发单元增加,代码合并面临的代码冲突和环境不兼容问题会急剧增加。持续集成(CI)可以帮助开发者在完成推送后,自动进行整合,可以更早的返现冲突,进而更高效的发现和解决冲突。
现代的 CI 工具,会在代码推到远端分支后,由 CI 的伺服器完整跑过整个 CI 流程。而现代软体开发团队的 CI 流程,做的不仅仅是确保代码合并没有冲突。而是可以通过配置实现包含格式化检查、静态检查、自动化测试(单元测试和集成测试)等相关发版前的测试工作。
- 格式化比如代码的缩进、空行、注释等等校验;
- 静态检查可以设计团队内部的一些规则和要求,比如代码风格、代码质量、代码规范等;
- 自动化测试,包含单元测试、整合测试、E2E 测试来确保准确性;
- 编译与构建,确保编译和构建的顺利,是CI过程中最重要的环节,部分团队还会针对安全、效能进行监控。
CI 最核心的作用,是透过自动化的方式,让工程师可以在把代码推到远端分支后,不用自己手动,就能完成这些重要的检测,并完成代码的整合。
CD 持续交付/部署(Continuous Delivery/Deployment)
CI 在完成了构建、单元测试和集成测试这些自动化流程后,持续交付可以自动把已验证的代码推送至仓库默认分支、部署至产品环境。 持续交付旨在建立一个可随时将开发环境的功能部署到生产环境的代码库。
对于一个完整、成熟的 CI/CD 管道来说,最后的阶段是持续部署。持续部署是持续交付的延伸,持续部署意味着开发人员对应用的改动,在编写完成后的几分钟内就能部署到生产环境及时生效(前提是它通过了自动化测试)。
总而言之,这些 CI/CD 的关联步骤,都极大地降低了应用的部署风险。
不过,由于还需要编写自动化测试以适应 CI/CD 管道中的各种测试和发布阶段,因此前期工作量还是很大的。
几个概念
- 持续集成(Continuous Integration):频繁地(一天多次)将代码集成到主干。让产品可以快速迭代,同时还能保持高质量。它的核心措施是,代码集成到主干之前,必须通过自动化测试。只要有一个测试用例失败,就不能集成。“持续集成并不能消除 Bug,而是让它们非常容易发现和改正。”
- 持续交付(Continuous Delivery):频繁地将软件的新版本,交付给质量团队或者用户,以供评审。如果评审通过,代码就进入生产阶段。持续交付可以看作持续集成的下一步。它强调的是,不管怎么更新,软件是随时随地可以交付的。
- 持续部署(continuous Deployment):代码通过评审以后,自动部署到生产环境。是持续部署是持续交付的下一步,持续部署的目标是,代码在任何时刻都是可部署的,可以进入生产阶段。
如何实现CICD
持续集成工具
[Jenkins](https://www.jenkins.io/)
[GitLab CI](https://docs.gitlab.com/ee/topics/build_your_application.html)
TeamCity
Travis CI
Bamboo
CircleCI
Gitlab CI 工作逻辑框架
GitLab CI/CD 是GitLab Continuous Integration(Gitlab持续集成)的简称。GitLab 自GitLab 8.0开始提供了持续集成的功能,且对所有项目默认开启。只要在项目仓库的根目录添加.gitlab-ci.yml文件,并且配置了Runner(运行器),那么每一次push或者合并请求(Merge Request)都会触发CI Pipeline。
GitLab Runner
GitLab Runner 是一个开源项目,可以运行在 GNU / Linux,macOS 和 Windows 操作系统上。每次push的时候 GitLab CI 会根据.gitlab-ci.yml配置文件运行你流水线(Pipeline)中各个阶段的任务(Job),并将结果发送回 GitLab。GitLab Runner 是基于 Gitlab CI 的 API 进行构建的相互隔离的机器(或虚拟机)。GitLab Runner 不需要和 Gitlab 安装在同一台机器上,且考虑到 GitLab Runner 的资源消耗问题和安全问题,也不建议这两者安装在同一台机器上。
Gitlab Runner 分为三种:
共享Runner(Shared runners)
专享Runner(Specific runners)
分组Runner(Group Runners)
Pipelines
Pipelines 是分阶段(stage)执行的构建任务。如:安装依赖、运行测试、打包、部署开发服务器、部署生产服务器等流程。每一次push或者Merge Request都会触发生成一条新的Pipeline。若一次推送包含了多个提交,则管道与最后那个提交相关联,管道(pipeline)就是一个分成不同阶段(stage)的作业(job)的集合。
下面是流水线示例图:
Stages
Stages 表示构建阶段,可以理解为上面所说“安装依赖”、“运行测试”等环节的流程。我们可以在一次 Pipeline 中定义多个 Stages,这些 Stages 会有以下特点:
- 所有 Stages 会按照顺序运行,即当一个 Stage 完成后,下一个 Stage 才会开始(当然可以在.gitlab-ci.yml文件中配置上一阶段失败时下一阶段也执行)
- 只有当所有 Stages 完成后,该构建任务 (Pipeline) 才会成功
- 如果任何一个 Stage 失败,那么后面的 Stages 不会执行,该构建任务 (Pipeline) 失败
下面是一个流水线内的阶段任务示例图:
Jobs
Jobs 表示构建的作业(或称之为任务),表示某个 Stage 里面执行的具体任务。我们可以在 Stages 里面定义多个 Jobs,这些 Jobs 会有以下特点:
- 相同 Stage 中的 Jobs 无执行顺序要求,会并行执行
- 相同 Stage 中的 Jobs 都执行成功时,该 Stage 才会成功
- 如果任何一个 Job 失败,那么该 Stage 失败,即该构建任务 (Pipeline) 也失败(可以在.gitlab-ci.yml文件中配置允许某 Job 可以失败,也算该 Stage 成功)
.gitlab-ci.yml
GitLab 中默认开启了 Gitlab CI/CD 的支持,但是要是用这个功能,我们需要告诉系统,我们需要执行什么样的pipeline,在gitlab里面,这通过 .gitlab-ci.yml 来实现。
且使用YAML文件.gitlab-ci.yml来管理项目构建配置。该文件需要存放于项目仓库的根目录(默认路径,可在 GitLab 中修改),它定义该项目的 CI/CD 如何配置。所以,我们只需要在.gitlab-ci.yml配置文件中定义流水线的各个阶段,以及各个阶段中的若干作业(任务)即可。
下面是.gitlab-ci.yml文件的一个简单的Hello World示例:
1 | # 定义 test 和 package 两个 Stages |
以上配置中,用 stages 关键字来定义 Pipeline 中的各个构建阶段,然后用一些非关键字来定义 jobs。每个 job 中可以再用 stage 关键字来指定该 job 对应哪个 stage。job 里面的script关键字是每个 job 中必须要包含的,它表示每个 job 要执行的命令。
Badges
Badges 即:徽章,当 Pipelines 执行过程中或者执行完成时会生成徽章,你可以将这些徽章加入到你的README.md文件中,便于从仓库主页看到最新的构建状态。 github
官方徽标
例如 :
Gitlab CI 实践
如上所述,我们的所有测试都需要提供硬件的基础环境(Runner)来为自动化的集成测试/部署提供底层基建,所以我们要做的第一件事情就是安装gitlab Runner
gitlab Runner配置
安装gitlab Runners
在此以CentOS为例进行说明,其他平台参考 官方指南。
1 | 添加官方的repo. |
注册 Gitlab
安装了 GitLab Runner 之后,就可以为 GitLab 中的仓库注册一个 Runner,注册的交互式命令如下:
1 | 输入注册命令 |
Gitlab Runner 常用命令汇总
面的表格中列出了一些常用的Gitlab Runner命令,以供参考:
| 命令 | 描述 |
|---|---|
| gitlab-runner run | 运行一个runner服务 |
| gitlab-runner register | 注册一个新的runner |
| gitlab-runner start | 启动服务 |
| gitlab-runner stop | 关闭服务 |
| gitlab-runner restart | 重启服务 |
| gitlab-runner status | 查看各个runner的状态 |
| gitlab-runner unregister | 注销掉某个runner |
| gitlab-runner list | 显示所有运行着的runner |
| gitlab-runner verify | 检查已注册的运行程序是否可以连接到GitLab,但它不验证GitLab Runner服务是否正在使用运行程序。 |
任务配置
任务配置的一个示例
接下来,用一个实际项目来演示 GitLab CI/CD 的配置和使用,其中主要包括:编译测试、项目打包、部署服务、Sonar手动检查、Sonar定时检查五个阶段。
下面用一个传统的 Java web 项目(这里称之为cidemo)和Tomcat来作为示例,并用来展示常用配置的使用。当我每次push代码或者Merge Request时,都会生成一条流水线,且会自动执行我们上面所说的一些阶段,而Sonar手动检查我们设置为手动操作,且再额外配置Sonar定时检查的任务。
1 | # 定义stages |
Gitlab CI/CD yaml 常用配置介绍
开始构建之前.gitlab-ci.yml文件定义了一系列带有约束说明的任务。这些任务都是以任务名开始并且至少要包含script部分,.gitlab-ci.yml允许指定无限量 jobs。每个 jobs 必须有一个唯一的名字,且名字不能是下面列出的保留字段:
| Keyword | Required | Description |
|---|---|---|
| script | yes | Runner执行的命令或脚本 |
| extends | no | 定义此作业将继承的父作业(GitLab 11.3 引入),重定义参数覆盖,未定义参数继承父任务 |
| image | no | 所使用的docker镜像,查阅使用docker镜像 |
| services | no | 所使用的docker服务,查阅使用docker镜像 |
| stage | no | 定义job stage(默认:test) |
| type | no | stage的别名(已弃用) |
| variables | no | 定义job级别的变量 |
| only | no | 定义一列git分支,并为其创建job |
| except | no | 定义一列git分支,不创建job |
| tags | no | 定义一列tags,用来指定选择哪个Runner(同时Runner也要设置tags) |
| allow_failure | no | 允许job失败。失败的job不影响commit状态 |
| when | no | 定义何时开始job。可以是on_success,on_failure,always或者manual |
| dependencies | no | 定义job依赖关系,这样他们就可以互相传递artifacts |
| artifacts | no | 定义job生成的文件列表,数组内的 paths 和 when 分别定义报告的路径和产出场景。 |
| cache | no | 定义应在后续运行之间缓存的文件列表 |
| before_script | no | 重写一组在作业前执行的命令 |
| after_script | no | 重写一组在作业后执行的命令 |
| environment | no | 定义此作业完成部署的环境名称 |
| coverage | no | 定义给定作业的代码覆盖率设置 |
| etry | no | 定义在发生故障时可以自动重试作业的时间和次数 |
| parallel | no | 定义应并行运行的作业实例数 |
| 接下来对其中的一些重要管检测进行补充说明 |
stages
stages 用来定义可以被 job 调用的 stages。stages 的规范允许有灵活的多级 pipelines。
stages中的元素顺序决定了对应job的执行顺序:
- 相同 stage 的 job 可以平行执行。
- 下一个 stage 的 job 会在前一个 stage 的 job 成功后开始执行。
接下仔细看看这个例子,它包含了3个 stage:
1 | stages: |
- 首先,所有 build 的 jobs 都是并行执行的。
- 所有 build 的 jobs 执行成功后,test 的 jobs 才会开始并行执行。
- 所有 test 的 jobs 执行成功,deploy 的 jobs 才会开始并行执行。
- 所有的 deploy 的 jobs 执行成功,commit才会标记为success。
- 任何一个前置的 jobs 失败了,commit会标记为failed并且下一个 stages 的 jobs 都不会执行。
这有两个特殊的例子值得一提:
- 如果.gitlab-ci.yml中没有定义stages,那么 job’s stages 会默认定义为build,test和deploy。
- 如果一个 job 没有指定 stage,那么这个任务会分配到 test stage。
参考信息