DevOps02-CI/CD

  • DevOps贯穿整个软件生命周期,而CI/CD则是它的基础和技术核心。但是在没有自动化测试、持续集成和持续部署的支撑下,DevOps就是空中楼阁。

1、CI/CD介绍

  • CI是指持续集成(Continuous Integration,CI)。狭义的CD指持续交付(Continuous Delivery,CD),广义的CD指持续部署(Continuous Deployment)。
    • 持续,是指每完成一个完整的部分,就向下个环节交付,发现问题可以马上调整,使问题不会放大到其他部分和后面的环节。
    • 集成,是指个人研发的软件部分向整体部分交付,以便尽早发现个人开发部分的问题。
    • 交付,是指研发尽快向质量团队或者用户交付,以便尽早发现生产环境中存在的问题。
    • 部署,是指代码通过测试后自动部署到生产环境。

1.1、持续集成

  • 随着软件项目复杂度的增加,对软件组件之间的集成和协同工作提出了更多的要求。如果开发人员在后期才进行集成,发现和解决问题的代价会很大,很有可能导致项目延期甚至失败。因此要“尽早集成、经常集成”,在项目早期发现的风险和质量问题更容易得到解决,并保证了开发进度,持续集成就诞生在这样的背景下。
  • 持续集成指的是频繁地将代码合入到主干分支,只有通过自动化测试和验证的代码才能被集成,目的是让产品在可以快速迭代的同时还能保证质量。简单来说就是,针对软件系统的每次变更,能持续且自动地进行验证:构建、测试。根据测试结果,我们可以确定新代码和原有代码能否正确地集戚在一起,并确保核心功能正常执行,不受影响。Martin Fowler说过:“持续集成并不能消除Bug,而是让它们非常容易被发现和改正。”
  • 持续集成的流程如图所示: 

  • 重复循环该过程就是持续集成:
    • (1)开发人员首先获取当前代码库的副本。随着其他开发人员将更改后的代码提交到源代码库,该副本与源代码库中的代码差异会逐渐变大。源代码库可能不仅仅被更改,还可能添加新库,或者增加其他依赖、或者冲突的模块。
    • (2)代码分支分出的时间越长,当开发者将分支重新集成到主线时,产生的冲突和失败的风险就越大。当开发人员将代码提交到源代码库时,必须先更新代码,使本地副本和代码库中的主线分支同步。代码库包含的更改越多,开发人员在提交自己的变更前必须进行的工作越多。
    • (3)开发人员的代码提交到源代码库时,会触发CI服务器进行构建和测试,并把测试结果反馈给开发人员。测试通过,代码会合并到源代码库中继续下一轮的测试;测试失败,开发人员根据反馈结果修改后重新提交。
  • 持续集成一般由代码变更触发,遵循先进先出的原则,否则容易引起混乱,很难定位是谁的代码破坏了集成。而且构建时间不能太长,否则构建次数将会变少,大部分变更会一直处于等待状态。
  • 注意,在代码提交之前,开发人员必须在本地完成单元测试,集成测试通常在检测到新提交时在CI服务器上自动运行。
  • 持续集成的好处:
    • 快速发现错误,变更容易追踪,节省了项目生命周期间的时间和金钱。
    • 避免版本发布最后时期才集成的混乱。
    • 测试失败或出现bug时更容易恢复。
    • 保证主干分支的可用。
    • 频繁地集成推动开发人员设计模块化、不太复杂的代码。

1.2、持续交付

  • 持续交付是在持续集成的基础上,频繁地将集成后的代码交付给质量团队(QA)或者用户,部署到更接近真实运行环境的类生产环境中以供测试评审,通过测试就进入发布,生产阶段。它旨在更快更频繁地构建、测试和发布软件,有助于降低交付的成本、时间和风险。直接和可重复的部署过程对于持续交忖非常重要。持续交付并不意味着每个变更都会尽快部署到生产环境中,它意味着每次更改都被证明可以随时部署。持续交付的目标不是要消灭缺陷,而是要规范开发和测试的流程,从根源上提高产品的质量。开发人员需要了解任何代码提交都可能会随时发布给客户,这一点很重要。
  • 持续交付的流程如图所示:
    • 持续交付在持续集成的基础上多了手动部署和系统测试等流程。

  • 持续交付的产品一般有:
    • 源代码交付:比如Python/Shell脚本,但这些文件不是标准的软件包,不利于集中管理和运行。
    • 软件包交付:比如通过RPMBuilder工具制作Linux标准包。
    • 镜像交付:可以是虚拟机镜像,也可以是Docker镜像。
  • 持续交付的优点:
    • 提高了产品质量,加快了产品上市时间。
    • 产品更符合用户需求,提高了客户满意度。频繁地发布使开发团队可以更快地获得用户反馈,专注于开发对客户有用的功能,有助于构建正确的产品。
    • 提高了生产力和效率。
  • 在实施持续交付的过程中,必须考虑将基础设施的维护纳入进来,作为支持产品运行的一部分。传统的基础设施运维管理存在以下几个问题:
    • 自动化缺乏串联:虽然有一定的自动化,但不能做到无人值守,需要执行一些临时命令。由于环境释放和重建的成本高,因而倾向于不释放,导致资源利用率低。
    • 与产品团队脱节:很难根据需求随时动态增加环境。需要额外的文档来描述环境,可能更新不及时。
  • 通过基础设施代码化(Infrastructure as Code,IaC)可以解决上述问题,大大有助于实现持续交付,促使持续交付和DevOps的成熟,是DevOps的一项关键实践。
  • IaC有四项关键原则:
    • 再生性:环境中的任何元素可以轻松复制。
    • 一致性:无论何时,创建的环境中各个元素的配置是完全相同的。
    • 快速反馈:能够频繁、容易地进行变更,并快速验证变更是否正确。
    • 可见性:所有对环境的变更应该容易理解、可审计、可回退、受版本控制。

1.3、持续部署

  • 持续部署是持续交付的下一阶段,即软件通过测试后自动部署到生产环境。持续部署的前提是能自动化完成测试、构建、交付等步骤。目标是代码在任何时刻都是可部署的,可以进入生产阶段。
  • 持续部署的流程如图所示。

  • 持续部署意味着每次更改都会自动部署到生产环境中,强调的是自动化。为了进行持续部署,必须持续交付。虽然持续部署可能不适合每个团队或项目,但持续交付是DevOps实践的绝对要求

1.4、CI/CD工作流

  • 以上简单介绍了CI/CD的概念及每个阶段的工作流程,整合起来即一套完成的CI/CD流水线。在实际应用中,一般通过代码评审系统实现代码审查和集成反馈。整个CI/CD系统配置了代码仓库系统代码评审系统构建工具系统
  • CI/CD工作流程如下:
    • (1)代码提交:开发者向代码评审系统(比如Gerrit)提交代码。
    • (2)第一轮测试:系统监听到代码评审系统的事件后即触发相关的测试。这里的测试有如下几种:
      • 单元测试:针对函数或者模块的测试。
      • 代码风格检查:针对代码编写的风格进行检查,比如Python的pep8等。
      • 集成测试:功能测试。
    • (3)构建:是将源代码转换为可以运行的软件包或者镜像等。(测试通过后,代码就可以合并到主干分支,然后进行构建。)
    • (4)第二轮测试:构建完成后进行下一轮测试,此阶段的测试比第一轮测试全面,包括功能测试、系统测试、性能测试等。
    • (5)交付:第二轮测试通过,代码就进入发布阶段。
    • (6)部署:经过多轮测试后的版本就是一个可直接部署到生产环境的稳定版本,此版本发布到制品库上,用户就可以在获取版本后通过自动化工具部署到生产环境。

2、OpenStack的CI/CD

  • https://review.openstack.org
  • https://docs.opendev.org/opendev/infra-manual/latest/
  • https://github.com/openstack-infra
  • OpenStack作为现在世界上第二大开源社区,有着一个完整的、标准化的、自动化的持续集成测试平台。它由社区的OpenStack-Infra团队开发维护,具有高可靠性、灵活性和可扩展性,对于搭建企业内部CI/CD系统有非常好的借鉴意义。
    • 注意,如果不做特殊说明,CD就是指持续交付

2.1、当前Cl/CD系统的形态

  • 以Jenkins作为持续集成工具为例,CI/CD系统的复杂程度分4个等级:
    • 第1级:如图2-4所示,仅使用Jenkins,且都在一套服务器上,适用于产品项目较小、资源较少的场景,使用维护较为简单。
    • 第2级:如图2-5所示,在第1级的基础上引入了Gerrit评审系统,但是具体构建还是在本地,测试环境受限。
    • 第3级:如图2-6所示,也是目前稍具规模的公司或社区都会使用的系统,使用Jenkins+Jenkins-Job-Builder(JJB)工具。在执行构建时,使用了一些静态资源(如虚机、容器或物理机),可以满足多场景构建和测试需求,但是资源利用率较低,多次测试的环境上下文存在耦合,目前OPNFV社区使用这种框架。
    • 第4级:如图2-7所示,在第3级的基础上引入了任务门控系统(Zuul)和动态资源管理系统(Nodepool)的使用模式,也是目前OpenStack开源社区的应用。

2.2、OpenStack的CI/CD架构

  • 首先做个说明,社区的CI/CD已经演进到V3版本,版本之间的差异比较大,本文所有框架和组件的介绍都基于V2版本。
  • CI/CD系统框图如图所示。大致分为以下几部分:
    • Gerrit服务器:代码评审服务。
    • CI Master:主要由以下几个组件构成。(是系统的核心,从监听代码变更,到匹配需要运行的任务,再到触发任务在合适的节点运行。)
      • 任务门控系统(Zuul)
      • 持续集成系统(Jenkins)和Jenkins任务管理工具(JJB)
      • 节点资源池管理系统(Nodepool)
      • 集群任务分发系统(Gearman)
    • CI Slave:即真正运行测试的节点,由Nodepool创建和管理,供Jenkins Master使用。
    • LogServer:日志服务器。
    • ELK日志分析服务器:可视化的日志分析平台。
    • 制品库:版本发布服务器。

  • 下面先简单介绍各组件功能,包括工具原理和使用方法。

1、Gerrit

  • 代码评审管理系统,提供了提交和补丁管理功能,同时提供了丰富的用户权限管理,可以控制哪些用户或组可以提交代码、合并代码、管理代码库并提供Gerrit事件流通知服务。

2、Zuul

  • 监听Gerrit事件流,匹配到对应的pipeline,以及该项目在这个pipeline下需要执行哪些任务。Zuul可以处理具有复杂依赖关系的多个补丁(Patch)。它能监控正在执行的任务,并可提前结束因依赖的Patch测试失败而必定失败的测试任务。

3、Jenkins和JJB

  • Jenkins负责具体任务的构建,每个Jenkins任务都需要通过配置Jenkins的config.xml文件实现。而在任务数量达到一定级别后,手动去配置每个任务会变得非常复杂,而且手工配置常常会带来人为操作风险。
  • 如图所示,可以看出,手工干预的步数越多,部署成功率越低。

  • XML文件也不易于维护,因此引入了JJB这个工具。顾名思义,JJB就是用来创建Jenkins任务的工具,支持Yaml或者Json格式存储Jenkins任务模板。JJB通过解析用户配置的文件来自动生成config.xml,支持模板和任务组的方式,复用性高。

4、Gearman

  • Jenkins是Master/Slave架构,一台Master管理所有Slave节点。在Slave节点增加到一定数量后(大约100台),Jenkins的Master节点就会出现问题并成为瓶颈。同时Master节点是单点部署,无法完成HA等处理。为了扩展Jenkins而引入了Gearman,加人Gearman后,Zuul不再与Jenkins直接交互,而是提交执行任务的请求给Gearman服务器,由Gearman服务器完成任务的分发。通过Gearman,使得CI测试架构具有了伸缩性:通过部署多个Jenkins Master,实现了Jenkins Master的HA功能。

5、Nodepool

  • 管理和维护Jenkins Slave的服务,会在一个或多个己部署的OpenStack环境中自动创建Slave节点,并在Slave节点运行完一次测试后删除并重建。

6、LogServer

  • 日志服务器,用来存储任务构建的所有日志信息。如果测试失败,开发者可以查看日志信息排查故障,同时它也是ELK采集日志的源。

7、ELK

  • 可视化的海量日志分析平台,从日志服务器上采集日志进行存储、分析及可视化,可用于系统监控和故障排查等。

8、制品库

  • 存放发布版本的服务器。

2.3、CI/CD系统工作流程

  • CI/CD系统的框架有了,如何搭建并配置这套复杂的系统使各组件之间协作是本书要详细阐述的内容。这里先不做说明,后续章节会详细介绍各组件的搭建和配置,这里先总体介绍下各组件之间的协作流程。

1、准备工作

  • (1)Zuul
    • 配置Zuul对接的Gerrit服务器。
    • 定义pipeline以及每个项目在对应pipeline下运行哪些任务。
  • (2)JJB定义项目任务,解析任务并上传到Jenkins服务器。
  • (3)Nodepool
    • 一个可用的OpenStack云环境。
    • 编译镜像,上传到云环境。
    • 连接到云环境,用上传的镜像孵化Slave节点资源池。

2、工作流

  • CI/CD系统的工作流程如图所示:

  • (1)贡献者在Gerrit上提交新Patch、添加评论等。
  • (2)Gerrit提交一个通知事件到它的事件流中(Event Stream)。
  • (3)Zuul从Gerrit的事件流中读取事件,并准备好本地项目代码,然后匹配事件到一个或多个pipeline,并找到该Patch对应的项目下的pipeline任务,把任务提交给Gearman服务器。
  • (4)Gearman把Job分发到可以执行该任务的Worker上。
  • (5)Worker根据Slave节点信息在对应节点上构建任务。
  • (6)构建后操作:把当前任务执行的日志信息和中间结果(包括编译结果)拷贝到日志服务器上。
  • (7)返回任务结果到Jenkins的事件流中:
    • Jenkins通过消息方式通知Nodepool此次任务完成。
    • Nodepool删除执行该Job的Slave节点,并重新孵化新的节点。
    • Jenkins通过Gearman把当前的任务执行结果返回给Zuul。
  • (8)根据测试结果,Zuul在Gerrit中对Patch添加一个Review结果。
  • (9)贡献者在Gerrit上可以查看相关日志信息。
#                                                                                                                          #
posted @ 2023-03-07 23:29  麦恒  阅读(188)  评论(0编辑  收藏  举报