云计算的普及,不仅改变了目前的 IT 基础设施与企业系统架构,同时也改变了技术团队的组织架构和企业内部的研发流程。而持续集成(CI)和持续交付(CD)就是企业内部研发流程提升交付效率的关键。
CI/CD 的核心价值是效能与质量。效能提升的关键在于自动化,它包含了两个方面:一是整个软件研发流程自动化,降低人力成本;二是 CI/CD 平台在业务接入、资源管理、线上支持、问题排查等诸多方面实现自动化,从而减少技术支撑方面的成本。而质量保障,一方面是研发质量,需要提供相应的质量检查与测试工具;另一方面是过程质量,CI/CD 平台需要能够持续采集研发流程中的指标数据(如交付频率、交付周期、交付成功率),建立起一个完善的质量度量体系。
目前各大公司的 CI/CD 平台,在工具选型、落地实现等方面各有不同,此次 InfoQ 采访到了网易杭州研究院高级产品开发工程师汪灿丰和高级服务端开发工程师梅光辉,他们分享了网易轻舟在 CI/CD 方面的实践。由于干货内容太多,我们将这次实践分为了上中下三篇内容,分别介绍网易的 CI 实践、CD 实践、测试自动化及 API 版本管理。
1. 网易 CI/CD 实践的背景
据了解,网易的大部分互联网产品业务团队都是使用统一的平台来进行 CI/CD 实践,目前平台上运行着数万个仓库,活跃仓库近万个,同时平台内还有大量副本数超过数千个的服务,这些服务每天承载着业务方大量的访问。
为了适应业务发展的节奏,网易内部广泛推行了敏捷开发,每天通过平台发布应用的次数达到数万次,产品迭代节奏快,交付周期短。所有业务使用统一的平台进行 CI/CD,无需各自搭建,降低成本,使用统一的规范,避免了相同问题的多次采坑。
网易公司内部的 CI/CD 实践主要是轻舟 CI/CD 平台,该平台提供了可视化的流水线编排界面,一次配置,多次运行,支持团队成员共享配置。每次提交或者合并代码都会自动触发流水线的执行,自动将代码发布到环境上。数据显示 CI/CD 流水线相比传统的手工或脚本方式平均效率提升了 5 倍以上,发布效率提升了 8 倍以上。另外通过整合多种自动化测试工具帮助业务方实现高质量的集成。通过可视化度量平台从多个维度了解构建,部署的相关指标数据,帮助产品提高发布效率。
之前,网易内部有一套从主机部署时代发展过来的自动化部署平台,但是其对容器部署的支持不友好,部署之前需要先在服务器上部署 agent,注册到内部 CMDB,再下发指令执行。这样的执行操作注定了这个平台在大批量部署场景下会存在性能瓶颈。
基于原平台存在的性能问题,以及对业务容器化、微服务化演进的判断,网易研发了新的轻舟 CI/CD 平台,成为了网易云原生技术栈的重要组成部分,满足云原生环境下企业在能效、团队协作效率、产品迭代速度等多方面的需求。
在使用体验上,轻舟 CI/CD 是一体的,但是在系统架构设计方面,其实是分开的。下面,我们就分别来讲讲 CI 和 CD 的系统设计。
2. 网易 CI 实践的整体架构及工具选型
与绝大多数互联网公司一样,网易内部各业务方的持续集成也大都是由研发团队的 QA 负责的,CI 工具选择了比较常用的 Jenkins,同时在流水线的上下游整合了其它的主流 CI 工具。整个流水线平台依托于 Kubernetes,基于 K8s 的 CRD(自定义资源)重新设计了流水线上层模型,并通过 Operator 驱动流水线执行过程。
当流水线触发一次执行时,将会产生一个 PipelineRun 对象,Operator 将会根据对应的流水线预先定义好的阶段生成对应的 Job,然后通过 Jenkins 的 API 触发 Job 执行。Job 执行完成后,将会通过 Webhook 回调上层服务接口,将状态回写到 PipelineRun 中,然后触发下一个阶段的执行。
CI 工具选型
传统的持续集成实践本身就很占资源,编译构建对磁盘、CPU,甚至是网络都有一定的要求,如果在高峰期想要保证持续集成任务能够及时运行,通常需要预留大量的构建机资源,这不仅会导致资源浪费,同时也需要更多人力来维护管理,成本会很高。
所以,网易技术团队在进行 CI 工具选型时,就直接圈定了 4 个当前主流的工具,分别是 Travis CI、Circle CI、GitLab CI 和 Jenkins,并详细对比了 4 个工具。
其中,Travis CI、CircleCI 在 Github 开源项目中使用较多,但不支持私有化部署,不适合公司实践;GitLab CI 可以与代码仓库无缝集成,且天然支持 GitOps,但整体生态目前并不成熟,需要自己制作 builder 的镜像,并与 Gitlab 深度绑定;Jenkins 作为传统的 CI 工具,社区和生态都比较完善,Jenkins 2.0 版本提供了 Pipeline as code 的特性,可以将 CI 过程纳入版本管理,支持 GitOps。此外,Jenkins 社区也诞生了 kubernetes-plugin 这样的插件,可以将流水线的执行从传统的物理机转移到容器中,再依托 Kubernetes 强大的调度能力,就可以实现资源的动态调度和编排。
基于以上原因,网易轻舟最终选择了 Jenkins,但是传统的 Jenkins 在系统运维、资源管理、使用方式等方面,存在很多问题。例如,Jenkins 的 master-slave 架构比较经典,但由于诞生时间较早,所有配置和数据都是基于文件存储的(存储在 Jenkins master 节点 home 目录下),修改时,会先更新内存,然后异步写入磁盘,而当底层文件被更新时,Jenkins 并不会自动 reload 更新后的数据,因此,很难做到高可用;Jenkins master 实例对文件目录是独占的,不同 Jenkins 实例也无法共享底层存储...... 因此,网易技术团队希望能够在传统 Jenkins 版本中做出以下改进:
- 结合 Docker/Kubernetes 云原生技术栈,解决传统 Jenkins 使用中的痛点;
- 通过将业内最佳实践固化到产品中,解决易用性的问题,降低用户使用门槛,提高接入效率;
- 重新设计上层流水线模型,提供更加强大的流水线编排调度能力;
- 自研流水线底层组件,替代 Jenkins 插件,提供更加灵活的扩展能力,方便与公司内部系统进行集成;
- 提供丰富的企业级特性(如认证、鉴权、审计、告警、通知等),满足企业客户需求;
除了 Jenkins,网易轻舟在整个流水线的上下游也整合了其它的主流 CI 工具,例如:
- 代码仓库:支持 Git/SVN,可以适配绝大多数代码托管平台,并且对 Gitlab 做了深度适配,例如,支持从 Gitlab Webhookpayload 中提取各类参数;
- 代码扫描:集成了 SonarQube,支持了所有主流的编程语言;
- 单元测试:整合了 Java 中常用的 Jacoco 工具,方便用户在运行单元测试时,生成对应的 UT 和覆盖率报告;
- 代码编译:Java 支持了 Maven/Gradle,并且内置了 Nexus 私服,方便做内网代理;Node.js 支持了 Npm;Golang 支持了 go mod 缓存加速;
- 镜像构建:支持 Docker out of Docker、Kaniko 两种构建方式,用户可以根据自身需求选择使用;
- 镜像仓库:整合了 Harbor,支持 Harbor webhook,并且集成了 Harbor 的镜像扫描和镜像复制功能;
- 容器部署:支持 kubectl 原生部署、升级,以及轻舟 CI/CD 应用部署等多种部署方式;
- 自动化测试:支持 Java 语言常用的 TestNG,同时整合了网易内部的 GoAPI 测试平台,支持在流水线中运行指定的接口测试集,并在执行时输出测试报告;
- 告警通知:集成了邮件发送、短信告警、网易 POPO 等多种通知方式;
据了解,目前国内大部分公司对 Jenkins 的使用方式依然停留在 1.0 的时代,很多人都还在使用 Jenkins 的 UI 配置方式,并没有使用到 Jenkins 2.0 提供的 Pipeline as code 功能,并且在使用场景上,大量依赖一些自定义脚本,这些脚本通常由 QA 同学事先预置在机器上,一旦机器出现宕机或故障,迁移起来成本也很高。相较之下,选择更新版本的 Jenkins,使用更前沿的功能,并适时辅以其它 CI 工具,往往会有更好的效果。
3. 网易 CI 实践的分支管理与协作流程
所有 CI 实践的源头一定是代码变更,好的代码分支管理和版本变更规范是做好 CI 实践的前提。网易轻舟是基于 GitFlow 进行代码管理和协作流程的,主要的阶段包括预发验证、测试验证和代码审查、集成、验证。
网易轻舟 CI 实践中的主要分支有 devlop 和 master,辅助分支包括 feature/*、release/v1.x.0、hotfix/v1.x.1。
- develop: 集成分支, 不允许 commit/push, 仅允许来自 feature/* 或 hotfix/v*
- 分支的合并;
- master: 版本分支, 不允许 commit/push, 仅允许来自 release/v*或 hotfix/v*
- 分支的合并, 每次接受合并后必须在其上创建 TAG;
- feature/*: 功能分支, 从 develop 派生, 不允许来自 develop 的合并, 合并到 develop 后删除;
- release/v1.x.0: 发版更新验证分支, 从 master 派生, 派生时根据 master 分支 TAG 标记并递第二位版本号命名。不允许 commit/push, 只允许来自 develop 的合并, 合并到 master 之后删除;
- hotfix/v1.x.1: 补丁更新验证分支, 从 master 派生, 派生时根据 master 分支 TAG 标记并递增第三位版本号命名。允许 commit/push, 不允许 来自 develop 的合并, 合并到 master 后必须再合并到 develop , 合并后删除。
除此之外,关于 TAG,网易轻舟也有一些特定要求:
- v1.x.0: 发版更新, release/v* 分支合入 master 后, 在 master 分支上创建;
- v1.x.1: 补丁更新, hotfix/v* 分支合入 master 后, 在 master 分支上创建;
- 此外,为了保证代码合入的质量,我们也制定了对应的代码 review 原则;
- 任何代码变更都必须提 mr,不允许直接 push 到 dev/master 等保护分支;
- 任何变更至少必须有两名团队其他成员 review 过,才能合入 develop、hotfix 分支,只有被团队接受的代码才是团队的代码。
网易 CI 流程的具体步骤
通常,一个持续集成流程会包括以下步骤:代码检出、单元测试、静态扫描、编译、构建、镜像扫描和测试部署。好的持续集成流程一定是面向质量的,所以,在实践过程中除了工具和流程,还需要做好代码质量相关的工作。
对于大部分团队来说,CI 可能是由 QA 维护的,因此在实际使用过程中,会将线下环境的 CI 和线上分离开,线上通常更加注重持续部署,主要由 QA、PE 等角色负责,流程上会简化一些,更加注重运维规范、上线审核。
代码检出: 通常需要在流水线中配置代码源、ssh 秘钥以及代码分支等信息,为了优化代码检出性能,CI/CD 流水线会采用的 git 浅拷贝的方式来加速整个过程。
静态扫描: 这是流水线至关重要的一环,运行时会将扫描的数据上传至用户预先配置好的 SonarQube 平台,并且通过 SonarQube 的 QualityGate 进行质量卡点,除了 QualityGate,用户还可以自定义一些流水线特有的卡点规则,并且配置通过邮件、网易 popo 等通知执行结果。
单元测试: 这是 CI 过程中必不可少的一环,CI/CD 流水线整合了常用的 Jacoco 工具,方便用户在运行单元测试时生成对应的 UT 和覆盖率报告,报告的内容可以在流水线执行详情页查看,同时支持用户设置单元测试通过率、UT 覆盖率卡点,并发送相应的邮件通知。
代码编译: 支持大部分主流的编程语言,比如 Java、Node.js、C++、Golang 等,并且针对各类语言做了相应的编译加速方案,支持 Maven、Gradle、C++、Go mod 构建缓存。
镜像构建: 支持 Docker out of Docker、Kaniko 等多种构建方式,同时整合了网易轻舟 Harbor,集成了 Harbor 的镜像扫描功能,支持在流水线运行过程中执行镜像扫描、查看镜像安全报告,同时支持安全项卡点以及邮件通知等功能。
集成测试:CI/CD 流水线与网易内部自动化测试平台 GoAPI 做了集成,用户可以预先在平台侧配置好相应的接口执行集,然后在流水线中选择对应的执行集执行,并在流水线详情页中查看执行集的通过率,同时支持卡点以及邮件通知。
流水线触发器: 多种触发器,比如定时触发、PollSCM、Webhook 以及流水线串联触发,其中 Webhook 还额外提供了对 Gitlab、Harbor 的支持,支持从 Gtilab、Harbor 的 payload 中提取相关参数,并在流水线阶段中使用。