说在开头
CI、CD 其实是三个概念,包含了一个 CI 和两个 CD,CI全称 Continuous Integration,表示持续集成,CD包含 Continuous Delivery和 Continuous Deployment,分别是持续交付和持续部署。这三个概念之间是有前后依赖关系的。
CI/CD 并不是一个工具,它是一种软件开发实践,核心是通过引入自动化的手段来提高软件交付效率。CI/CD 最终目的:让工程师更快 & 更高质量 & 更简单的交付软件!
持续集成 & 持续交付 & 持续部署
持续集成(Continuous Integration)
什么是持续集成?
定义:持续频繁的(每天多次)将本地代码“集成”到主干分支,并保证主干分支可用
持续集成这个词,起源于极限编程,是当时原始的12种实践之一,持续集成的目的快速反馈问题让产品可以快速迭代,同时还能保持高质量。它的核心措施是,代码集成到主干之前,必须通过编译、代码扫描、安全扫描、自动化测试等。只要编译失败、扫描出高级别问题或测试用例失败,就不能集成。
Martin Fowler 说过,“持续集成并不能消除Bug,而是让它们非常容易发现和改正。”
好处?
(1)通过自动化手段提高集成速度
在传统的研发过程中,一般通过手动方式执行编译、测试、部署,自动化后,可大幅提高集成频次,同时可以减少维护手工脚本带来的低级问题
(2)将问题前置
在每次 commit 时就触发编译、测试,更快的发现问题
(3)防止本地代码大幅偏离主干
如果不是经常集成,主干又在不断更新,会导致以后集成的难度变大,甚至难以集成。
触发方式?
自动触发,通过自动化的 CI 服务或工具,自动监听代码库 Git Push & MR 等事件触发
常见的工具如 Jenkins、GitlabCI、CircleCI、GithubActions 等等
蚂蚁支持持续集成平台有:雨燕(前端)、LinkE CI(后端)、伙伴(终端)、ACI(多语言)等
面临的问题 & 阻碍?
理想很丰满,现实很骨感,持续集成理念虽好,但实践过程中却有非常多的问题和阻碍,远没有想象中那么简单!
常见的问题如:
(1)执行环境异构导致结果差异
比如Java测试,用户执行测试一般是本地通过IDEA右击执行测试或 mvn 命令,但是 CI 服务上的环境&机器规格和本地有很多差异,比如 JDK 不一样、Maven 版本不一样、Mac 和 Linux 差异等等会造成很多本地执行可以通过但是 CI 上执行不通过,或者 CI 上执行明显比本地慢的现象。
解决此类问题本质还是减少异构,比如通过容器化方式让执行环境标准化,通过 Maven 缓存提高测试时间等等,同时作为 CI/CD 平台方,也最好将测试、构建的过程透明化(执行机制、清晰的错误码和解决方案),方便在出现问题的时候开发可以很方便的自助排查。
(2)没有严格的测试要求
持续集成的理念是只要有问题一定要及时修复,比如执行测试,必须保证所有case全部通过才能集成到主干,但是在现实中的场景,特别是一些规模大的历史项目,由于历史债等原因,导致测试case无法全部通过或者必须通过重试解决,开发很难有动力去改,导致集成质量低效。
所以,持续集成的前提是,需要有一个心里准备花大精力去治理项目中的一些历史债,尤其是改善历史 case ,补充新 case ,降低 case 噪音,确保每次自动化触发的测试可以准确反馈集成质量!
(3)非常慢的构建 & 测试速度
在 CI 上执行构建或测试的速度非常慢,导致集成效率非常低,这里的慢有两种原因,一种是本身测试&编译就慢,这时需要深入分析,需要结合并行分组、分布式缓存等技术解决。
这里的自动化测试中的集成测试可能会非常慢,所以一般保证在持续集成阶段先让单元测试通过,集成测试放在在持续交付阶段完成。
持续交付(Continuous Delivery)
什么是持续交付?
定义:是持续集成的下一步,持续频繁地将软件的新版本交付到类生产环境(类似于预发),交付给测试、产品验收。
持续交付强调的是“交付”,不管怎么更新,软件是随时随地可以交付的,相比持续集成,持续交付除了交付到类生产环境之外,还会执行一些集成测试、API测试等等,确保交付的产物可以直接交付部署。
触发方式?
手动触发,通过研发平台手动触发(如触发LinkE预发部署流水线),一般交付结果是一个二进制包或者镜像
面临的问题 & 阻碍?
(1)很难保证和线上环境完全一致
由于持续交付的环境很难和线上环境一致,所以可能导致一些问题无法验证,使得交付的产物具备一定风险
探索方案:仿真环境
(2)大量脑壳疼的联调环境问题
经常遇到的场景是虽然本系统开发好了,但是由于各种原因导致上下游系统没有 ready 导致无法持续交付,针对上下游依赖非常多的系统,对联调环境稳定性、效率有非常强的依赖
(3)很难保证严格充分的自动化测试
持续交付最终要做到交付的产物可直接部署线上,那么对代码的质量要求就非常高,需要覆盖全场景充分的测试 case
持续部署(Continuous Deployment)
什么是持续部署?
定义:是持续交付的下一步,“自动”将代码部署到生产环境
持续部署强调的是“部署”,它的目标是,代码在任何时刻都是可部署的,可以进入生产阶段。
持续部署和持续交付触发方式的区别是,持续部署是自动完成的,持续交付是手动完成的
触发方式?
自动触发,通过研发平台配置定时任务,自动获取交付产物进行自动部署。
目前即使在蚂蚁,应该很少有团队和研发平台能够真正做到持续部署,因为持续的部署的条件更加严苛
面临的问题 & 阻碍?
(1)发布条件受限
部分场景上线需要严格的审批的流程 & 频繁的封网
(2)低效手工发布流程
发布过程需要人工一次次确认,灰度和线上发布过程多分组场景下,需要一次次人工确定,不放心自动发布
目前蚂蚁解决方案:无人值守
(3)缺乏线上问题快速准确的反馈机制( 精准 OPS 能力)
没有 OPS 或者 OPS 噪音太大都会导致反馈不及时,这样研发就不敢把部署过程直接交给系统,也是上面发布需要人工确认的根因
目前公司对线上安全要求非常高,持续部署现阶段很难在蚂蚁大规模落地,特别针对类似金融核心这些安全要求非常高的系统,不过其实也未必要“一刀切”,公司还有很多级别不是特别高的应用,可以尝试小范围针对这些应用试点,同时不断持续改进周边 OPS 工具
最后
最后,我们来畅想一下,一个优秀的 CI/CD 研发过程应该是什么样子的?
假设开发同学小蚂第一天入职,他打开电脑,通过以下几步完成上线:
第一步:从 AntCode clone 代码到本地,修改了几行代码,本地测试后,通过git commit & push到代码仓库
第二步:push 过程自动触发了 CI 流水线,5 min 后,执行结束,通过钉钉通知告诉小蚂某个测试case失败了
第三步:小蚂本地修改失败 case ,重新 commit & push ,再次触发 CI 流水线,自动执行编译、测试、扫描等,收到钉钉消息,执行通过
第四步:小蚂将功能交给测试&产品验收,发现问题或收到建议,重新到第二步修改代码。
第五步:测试&产品验收没有问题后,手动触发预发部署流水线,并自动生成部署产物交付,后面流程就不用管啦。
第六步:持续部署流水线每几分钟自动检测交付产物包,发现更新后,自动将产物部署到线上。发布成功后,小蚂收到通知小蚂的功能生效啦。
理想状态?
(1)由于发布而导致的加班频次应该大幅减少
(2)开发大部分精力是花在写代码和思考如何写好代码,扫描、部署的事情交由平台自动化完成,并且体验良好,反馈精准。