CI/CD流水线插件在服务质量看护中的实践

一、前言

1、CI/CD

CI/CD 是一种通过在应用开发阶段引入自动化来频繁向客户交付应用的方法。CI/CD 的核心概念是持续集成、持续交付和持续部署。

作为一种面向开发和运维团队的解决方案,CI/CD 主要针对在集成新代码时所引发的问题(亦称:"集成地狱")。

具体而言,CI/CD 可让持续自动化和持续监控贯穿于应用的整个生命周期(从集成和测试阶段,到交付和部署)。这些关联的事务通常被统称为"CI/CD管道(Pipeline,一般也称为管线或流水线)",由开发和运维团队以敏捷方式协同支持。

1、CI持续集成(Continuous Integration)

现代应用开发的目标是让多位开发人员同时处理同一应用的不同功能。但是,如果企业安排在一天内将所有分支源代码合并在一起(称为"合并日"),最终可能造成工作繁琐、耗时,而且需要手动完成。

这是因为当一位独立工作的开发人员对应用进行更改时,有可能会与其他开发人员同时进行的更改发生冲突。如果每个开发人员都自定义自己的本地集成开发环境(IDE),而不是让团队就一个基于云的 IDE 达成一致,那么就会让问题更加雪上加霜。

持续集成(CI)可以帮助开发人员更加频繁地(有时甚至每天)将代码更改合并到共享分支或"主干"中。一旦开发人员对应用所做的更改被合并,系统就会通过自动构建应用并运行不同级别的自动化测试(通常是单元测试和集成测试)来验证这些更改,确保这些更改没有对应用造成破坏。

这意味着测试内容涵盖了从类和函数到构成整个应用的不同模块。如果自动化测试发现新代码和现有代码之间存在冲突,CI 可以更加轻松地快速修复这些错误。

2、CD持续交付(Continuous Delivery)

对于一个成熟的 CI/CD 管道来说,最后的阶段是持续部署。作为持续交付------自动将生产就绪型构建版本发布到代码存储库------的延伸,持续部署可以自动将应用发布到生产环境。由于在生产之前的管道阶段没有手动门控,因此持续部署在很大程度上都得依赖精心设计的测试自动化。

实际上,持续部署意味着开发人员对云应用的更改在编写后的几分钟内就能生效(假设它通过了自动化测试)。这更加便于持续接收和整合用户反馈。总而言之,所有这些 CI/CD 的关联步骤都有助于降低应用的部署风险,因此更便于以小件的方式(而非一次性)发布对应用的更改。不过,由于还需要编写自动化测试以适应 CI/CD 管道中的各种测试和发布阶段,因此前期投资还是会很大。

2、云化流水线

云化流水线是交付新版本软件必须执行的一系列步骤,是一种专注于通过自动化在整个软件开发生命周期中改进软件交付的实践,根据用户需要的场景,如开发测试环境应用部署、生产环境应用部署等,对这些自动化任务进行自定义编排,一次配置后就可以一键自动化触发调度执行,避免频繁低效的手工操作。

通过在软件开发生命周期的整个开发、测试、生产和监控阶段自动执行 CI/CD,能够更快、更安全地开发更高质量的代码。尽管可以手动执行 CI/CD 管道的每个步骤,但 CI/CD 管道的真正价值是通过自动化实现的。

流水线是通过生成、测试和部署代码的路径(也称为 CI/CD)推动软件开发的过程。通过自动化该过程,目标是最大限度地减少人为错误,并维护软件发布方式的一致过程。其中包含的工具可能包括编译代码、单元测试、代码分析、安全性和二进制文件创建。

二、插件介绍

1、流水线插件介绍

从上面我们可以得知,流水线服务本质上是一个可视化的自动化任务调度平台,需要配合软件开发生产线中编译构建、代码检查、测试计划、部署等服务的自动化任务使用。

而流水线内置了一系列常用的插件 ,供用户在流水线进行编排时使用作为整个流程的某个环节。这样的插件是可扩展、可自定义的。利用流水线在执行过程中串行、并行的不同执行类型,以及顺序执行场景下对于各个步骤是否执行成功与否的判断,流水线插件在CI/CD的过程中可以起到重要作用。

2、一些插件的简单介绍

1、代码检查

在服务开始尝试将代码部署到机器上时,首先必须要执行的就是将代码从代码仓中拉取、构建、打包等操作。

而对于代码本身而言,除了本地的一些代码检查规范外,我们还会在流水线中添加这样的检查步骤,对全量代码进行诸如规范性、安全性、圈复杂度、CleanCode 等一系列内容的检查。

当服务尝试部署的代码中可能存在影响质量的严重问题时,代码检查中所暴露的问题会在该步骤的门禁检查中发现并直接拦截,禁止流水线进一步的向下执行,避免引入可能的风险和问题。

2、安全病毒检查

除了代码层面的检查外,我们还会对构建产物进行安全与病毒扫描,这里会直接与中央的病毒样例库连接做病毒的扫描和查杀,避免存在恶意病毒文件连同代码一起被打包上传到现网机器。

3、开源依赖检查

对于开源漏洞与依赖层面,我们也会进行相关的扫描与检查。毕竟对于三方依赖而言,服务自身在做开发使用的过程中可能并不关注该依赖是否有开源问题、当前版本是否已经过时存在漏洞等,因此对于这一系列依赖,我们会直接与自身的中心仓库做版本比较,其中包含三方库与二方库等,支持Maven、Pypi、Npm、Go等类型。

对于每一个依赖包而言,我们都会给出其对应的每个版本号是否为优选、可选或禁选。对于不再中心仓库的依赖包或者版本为禁选类型,同样的开源依赖检查会无法通过、流水线也自然无法进一步向下执行。

3、如何保证插件配置完整性

对于众多的检查与插件配置,上千个微服务、上万条流水线,我们如何才能保证所有的流水线都能配置了对应的具体插件呢?

对于高度自定义化的流水线而言,虽然可以提供某些模板机制,但本身插件配置对于不同环境、不同编程语言都会有各自不同的需求,由中央统一全量修改流水线虽然理论上可行,但过于机械、大费周章,被修改流水线的服务也有可能一头雾水、甚至流水线都被修改的无法执行,并不是最好的策略。

这个时候,我们一般会有更加直接且简单的措施:生产准入门禁卡点机制

  1. 对于服务的流水线而言,一般会有Alpha、Beta、Gamma和生产 四个发布阶段,这其中前三个阶段影响较小,而生产的部署是涉及到现网变更、影响具体C端用户的,因此需要格外注意。这里,我们会在生产阶段前添加一个检查各项指标是否达标的生产准入门禁卡点,若服务在流水线中某项指标未达标,则会直接显示检查未通过、不允许下一步的操作------也即不允许升级生产。
    • 服务的门禁卡点指标未达标有可能是确实存在缺陷,也有可能是未配置对应的检查插件,由此便可以反推服务完成插件的配置。
  2. 倘若希望添加新的检查机制,一般会在准入门禁卡点中加入提示等级的信息,以告知服务用户当前构建包存在哪些问题、需要完成哪些整改、具体整改的DDL等,不做强制的拦截。
    • 这里需要注意的是,新的门禁卡点不是你想加就加的,需要在产品部TMG研讨达成一致结论,并举行相关宣讲、赋能、邮件全量知会到具体负责人,才能进一步行使加卡点的权利。
  3. 在一段时间后,我们会提升门禁卡点的等级,在要求服务整改的同时,也给予服务申请暂时屏蔽备案的选择。
  4. 最后在DDL后,我们会直接将门禁设为强制,此时如果服务当前的构建包仍然包含对应问题,则无法跳过、必须整改,否则将无法将代码部署至生产环境。

三、插件实践

除了上述的一些常见流水线插件外,笔者方面同样基于插件开发规范、提供了相关流水线插件,旨在提升服务质量提升与问题提前拦截。

1、用例通过率

基于云化测试平台的功能拨测与告警中我们提出,将服务在云化测试平台中所撰写的测试用例有效的组织起来,以不同的形式调度、检查,对服务的在线功能质量能够起到重要作用。

这里同样地,服务将当前所有认为较为重要的功能测试用例统一归纳在一个测试任务中,并在每个部署阶段完成后全量执行该测试任务,以检查新版本代码是否引入新的问题、是否对已有功能造成影响

具体实现层面则较为简单,直接以任务执行是否达到100%为标准即可。对于存在失败用例的情况,流水线层面不允许向下执行。

2、接口覆盖率

在第一点中我们提出了用例通过率插件用以衡量功能可用性,但是这其中还存在另外一个问题:

  • 用例的实际覆盖情况如何呢?
    • 假如我的服务有100个接口,但我的测试任务只包含了一个接口的测试,那么即使测试任务完全成功,我们也明白检查是没意义的。

对于用例的覆盖率而言,一般有很多评判标准:例如分支覆盖、代码覆盖等等。由于我们云化在线测试本质上是对服务接口的测试,因此这里我们引入了接口覆盖率来作为评判标准。

在具体实现上:

  1. 我们会针对每个服务提供所需看护的API基线,一般而言,服务在生产环境中存在的接口我们认为是必须看护的。
    • 对于API基线的数据来源,我们一般采用调用链运行态数据而非直接套用API设计文档接口信息。
      • 我们没有直接使用API设计态的yaml作为API基线的来源,是因为本身API的测试覆盖是需要有所侧重的,评价一个接口是否有测试看护的价值,很重要的一个维度就是看该接口是否已经在现网被用户所使用。
      • 如果我们直接将全量的设计接口作为看护标准,一方面会给服务非常大的测试负担与压力;另一方面也会造成一些无谓的测试人力浪费,为很多整个生命周期都在研发阶段的接口投入过度的测试看护精力。
      • 当然,这种策略一方面会造成测试看护存在一定的延迟性(只有服务接口先发布到现网,我们的API基线才会有看护要求);另一方面也会因为设计实现的不一致而引发一些问题,这一点后面会提到。
  2. 在服务执行流水线的过程中,在完成全量用例任务执行后,我们会根据用例执行结果id等信息,完成对用例中所有对应接口的获取采集,并与所需看护的API基线进行比较:若覆盖率未能达到既定百分比,则会认为当前测试任务覆盖率未达标。
    • 这里,我们主要面向的发布阶段为Alpha、Beta与Gamma阶段,旨在对开发阶段的测试完整性做检查。
    • 在功能层面,我们首先会获取到具体的测试任务执行结果id,根据id获取全量用例以及对应的ActionWord信息。
    • 对于去重后的ActionWord,我们会逐个做内容解析、并汇总全量的测试任务覆盖API,同时与对应的API基线做比照,以此来计算整体的覆盖情况。
      • 在对于测试接口与API基线接口的比较也存在一定的问题:设计与实现可能存在不一致现象。这个现象始终困扰着整个开发团队------至少是我们的团队,很多时候API的实现未必是严格按照API设计文档来执行的,这其中有很多现实原因。而一旦接口完成了开发,反过去再修改设计文档又显得倒行逆施。
      • 针对这类不一致问题所带来的API匹配与比较问题,我们一方面会推动服务完善API设计文档,另一方面也通过一些模糊匹配、接口版本信息/路径内参数提取替换等方法,完成API设计态与实现态的匹配。

3、接口健壮性

除了必要的功能可用性与测试覆盖率,我们还对接口本身的健壮度提供了相应的自动化测试与评估插件。

在具体实现与原理上:

  1. 所谓健壮性(Robustness) ,也就是我们之前一直提及的鲁棒性,是指一个计算机系统在执行过程中处理错误,以及算法在遭遇输入、运算等异常时维持正常运行的能力。对于接口而言,也就是说在接收各种类型的请求参数时,能否正确的进行处理并返回预期结果的能力。
    • 举个例子,例如普通的根据主键id查询的接口,假如外部服务传递的id为-1,若不作相关参数校验直接查表,必然会返回5xx类型错误。
  2. 我们会搭建接口健壮性测试平台,服务需完成对应API设计文档yaml地址、测试阶段、测试地址、认证token等配置。
  3. 在服务执行流水线的过程中,会触发接口健壮性检查任务。此时测试平台会自动拉取最新的yaml文档、分析接口构成、具体参数与取值范围,并完成非常规请求参数构造并根据测试地址发起测试,根据接口的返回值情况来做进一步的接口健壮性判断。
    • 对于非常规请求参数的构造,主要是以yaml文档中对于API参数的定义类型与取值范围做相应的模板化构造:例如,对于int类型参数,我们就可以取0、-1、边界值等作为请求参数;对于String类型则可以取类似%、*等类型的符号等。
    • 对于测试结果而言,我们一般认为2xx、4xx类型为成功,而5xx类型为系统内部故障、属于失败类型。

接口健壮性测试可以协助服务提前返现代码中可能存在的异常情况判断与接口状态码规范问题,检查的结果也主要起到参考作用,并不会直接参与门禁卡点与强制拦截。

四、结语

在CI/CD流水线的大背景下,虽然插件只是其中很小的组成部分,但合理的开发、运用与推广插件,不仅能够极大程度降低质量看护与运营门槛、提升效率,而且还能为服务带来很多额外的质量看护视角,从而提升服务在研发段的质量看护能力,降低现网的故障产生。

参考文章:

什么是 CI/CD - RedHat

相关推荐
幽兰的天空27 分钟前
Python 中的模式匹配:深入了解 match 语句
开发语言·python
网易独家音乐人Mike Zhou4 小时前
【卡尔曼滤波】数据预测Prediction观测器的理论推导及应用 C语言、Python实现(Kalman Filter)
c语言·python·单片机·物联网·算法·嵌入式·iot
安静读书4 小时前
Python解析视频FPS(帧率)、分辨率信息
python·opencv·音视频
小二·5 小时前
java基础面试题笔记(基础篇)
java·笔记·python
小喵要摸鱼7 小时前
Python 神经网络项目常用语法
python
wuxingge8 小时前
k8s1.30.0高可用集群部署
云原生·容器·kubernetes
一念之坤8 小时前
零基础学Python之数据结构 -- 01篇
数据结构·python
wxl7812279 小时前
如何使用本地大模型做数据分析
python·数据挖掘·数据分析·代码解释器
NoneCoder9 小时前
Python入门(12)--数据处理
开发语言·python
志凌海纳SmartX9 小时前
趋势洞察|AI 能否带动裸金属 K8s 强势崛起?
云原生·容器·kubernetes