小来说:

公司在发展,支撑公司业务的基础架构也要发展。
来也科技从30个微服务起家,发展到现在微服务数量已超过250个。这些服务要如何运维、发布?
来也科技做为一家注重产品稳定性、安全性的公司,都在哪些阶段触发测试(集成、功能、安全)才能保障每次上线的产品安全且高可用?
本文介绍了来也科技CI的演变过程,踩坑经验,欢迎阅读。


为了方便阅读,附上本文结构:

  1. 背景

  2. 演进

  3. Prow简介

  4. Prow在来也科技的具体实践

    1. 代码review,自动合并,合并后触发测试

    2. 项目标签管理

    3. Prow集成测试

    4. 统计、通知和报警

    5. 使用External plugins,修改代码或periodics扩展 Prow,增加CI功能

  5. CI流程

  6. 分支CI策略

  7. 配置文件CI流程

    1. 配置模板

    2. 配置填充项

    3. 配置发布

  8. 结语





背景
来也科技使用微服务架构,产品迭代速度快,持续发布是一种常态。而CI/CD 核心就是频繁发布、流程自动化、可重复、快速迭代。在云原生环境的基础设施支撑下,能很好的实现蓝绿、金丝雀等部署方式。
通过在来也科技内部建设CI工具,可以自动化服务开发的整个流程,提高开发、发布效率;同时可以便捷的在流程中加入诸多单元,如:静态扫描、集成测试、安全检查等,在快速迭代的同时,提供质量保障;除此以外,可以在流程中的指定阶段暴露metric,用于统计分析,度量内部系统交付质量。

演进

在来也科技,CI主要经历了以下几个阶段:
1. 手动阶段
最初,来也科技内部只有30个服务。此时开发环境采用手动更新;生产环境采用手动+Python脚本上线。
这种方式效率低,手动输入内容较多,问题频繁。

2. 半手动阶段
此时,来也科技只有开发、生产两个环境,服务数量较少,我们引入GitLab做代码仓库,引入Jenkins做CI工具。
一个项目对应一个Jenkins Freestyle Job,仅用于生产环境上线时进行参数化构建。研发人员输入服务TAG,触发构建,而后Jenkins自动拉代码、构建、发布。此时,如果网络不好,则会进入漫长的等待阶段。
开发环境由开发人员登录服务器手动拉取代码,并编译启动;生产环境开始通过Jenkins上线。
生产环境上线自动且规范,但开发阶段还是比较痛苦。如果服务数量多,则情况不可持续。

3. 自动阶段--初级
随着服务数量增长,手动更新服务的方式严重影响开发效率。同时,因为一个项目可能会被构建成多个服务,串行的方式导致构建效率低下。
于是,我们对原本的Job进行改进,从“一个项目一个Job”升级为“为项目下的每个服务创建一个Job”,通过关联方式进行控制。
此时,开发和生产环境统一采用Jenkins发布。
发布效率提升了,但新增项目或服务的时候,Jenkins相关配置较多,较繁琐。

4. 自动阶段--中级阶段
服务数量持续增长,频繁变更的开发环境影响测试效率,而当前的Job配置复杂,繁琐,不可持续,新增稳定的测试环境不易复用当前的Job。
通过引入Declarative Pipeline,简化Jenkins相关配置,同时对当前流程进行梳理,将通用流程梳理出来形成Pipeline共享库,在项目Pipeline中进行引用。通过一个Jenkinsfile完成项目中所有服务所有环境所有分支CI流程的控制,新项目进行少量配置即可生成Jenkinsfile。Jenkins只需要针对项目配置一个Job,Job只需要配置项目Git地址。服务数量及环境的增长并不会增加太多工作量。
上线流程耦合构建流程,导致上线时间延长,各类三方库仓库、GitLab网络等问题都会导致上线时间进一步增加。上线、构建流程拆分是一个必然结果。针对开发、测试环境构建与部署全自动;生产环境,出于业务安全考虑,部署操作由开发人员手动触发,构建流程则是由开发人员创建TAG之后,自动触发。

5. 云原生阶段
当前,来也科技业务服务的数量已经超过250个。
传统Jenkins使用过程中有很多痛点,诸如:
1.诞生时间早,配置、数据都是基于文件,Master故障会导致整个流程不可用
2.Slave Job分配不均衡,存在资源浪费
3.来也科技内部Java项目较少,Java、Groovy扩展方式不太适合

结合内部现状,参考当前主流的CI/CD系统,我们设计了新的解决方案:
我们将代码库迁移到私有部署的GitHub,引入了Prow,通过 External plugins 接入内部运维系统,再通过Pipeline Operator生成项目Pipeline,由Tekton进行执行。完成整个CI流程。
Prow是当前主流云原生工具使用的ChatOps工具,主要用于管理Issue、PR。下文会详细介绍Prow的功能,以及它在来也科技CI流程中扮演的角色。
Tekton是谷歌开源的kubernetes原生CI/CD系统,功能强大易扩展。Tekton CRD的设计与来也科技之前使用Jenkins的方式很类似,可以把一些通用的流程抽象出来,形成任务,在Pipeline中进行引用。
图片

Prow简介

Prow是Kubernetes sig-test开发的CI/CD工具,起初用来对Kubernetes进行持续集成、测试。Kubernetes相关项目12小时运行Jobs数近2w(数据来自 https://prow.k8s.io)。
当前,除Kubernetes以外,也被很多热门项目使用,如:Istio、Prometheus等。

图片

相比于CircleCI和Travis CI等CI工具,Prow支持私有部署,支持动态扩容,满足不同规模的的任务执行。
Prow提供:
  • GitHub 自动化

  • ChatOps via simple /foo commands

  • 通过OWNERS进行权限管理

  • GitHub PR自动合并 - tide

  • 标签管理

  • 分支保护

  • release notes管理

  • CI/CD并行执行,报告,历史记录

  • Promethus Metrics

  • Config-as-Code 配置自动生效

  • 插件扩展

Prow在来也科技的具体实践

结合Prow相关功能以及来也科技内部情况,我们使用Prow做分支合并辅助、单元测试、集成测试和项目功能管理。同时对Prow进行了修改,增加External plugins扩展其功能。

代码review,自动合并,合并后触发测试

Prow通过OWNERS文件,提供了对项目更精细的权限管理。对于一个项目的不同目录,添加不同的OWNERS文件,可以实现对一个项目赋予不同人不同权限。OWNERS中可以定义两种身份:reviewers和approvers。
reviewers职责是:review代码风格,发现bug。通过输入/lgtm(look good to me)表示review通过。Prow给PR增加lgtm标签。
approvers职责是:保证PR实现的功能可以被添加。通过输入/approve表示review通过。Prow给PR增加approved标签。

图片

个人历史PR情况:
图片
Prow可以添加presubmit job,其通过Kubernetes CRD实现。在创建PR时,可以按照配置的规则运行,运行结果作为GitHub check显示。

presubmit job中,会自动拉取代码,在本地执行PR合并,运行事先定义的脚本。

当PR有lgtm和approved,并且没有阻止合并的标签,同时所有check都通过后,PR将被tide自动合并。



图片
PR被合并后,会按照规则,触发测试环境或开发环境部署。
Prow添加postsubmit。在postsubmit中,检查相应环境是否部署成功,调用相应结果,检查接口返回,项目日志,数据库状态等方式验证程序正确性。

项目标签管理

在来也科技我们使用tapd管理产品经理提出的需求,使用GitHub管理技术需求。
通过Prow的标签管理功能,在PR被提出的时候,机器人要求PR需要添加kind和feature标签,kind标签分为 需求,bug等。feature标签更具代码修改的功能,添加相应标签。并且使用Prow owners-label功能,在特定目录被修改时,添加对应的feature标签。
通过标签,可以方便review人员确定review范围,测试人员确定测试范围。
通过PR和标签,了解feature发展。
下一步,我们计划GitHub lable可以和tapd打通,更方便管理项目。

Prow集成测试

上一节,我们提到来也科技使用Prow 的postsubmit job,对部署的代码进行自动测试。
对于预生产环境和生产环境的代码,因为不是通过push触发部署,所以我们使用periodics job,定期对不同环境执行测试。测试代码也有不同的分支,对应的是不同环境的代码。当项目部署到不同的环境时,环境对应分支的代码,被merge到相应的分支。通过这种方式,实现不同的环境在部署前后,执行相应的测试代码。
目前还存在的问题是,测试没有在部署时被自动触发,计划通过上线代码时,push某个代码仓库,触发代码执行。

统计、通知和报警

Prow通过crier组件实现通知功能,crier实现Kubernetes Reconciler监控Prow jobs状态,并发出通知。默认支持GitHub,pubsub,slack等reporter。
在来也科技我们使用企业微信做日常通知工具,通过实现crier reporter接口,实现企业微信通知。
crier只能提供Prow jobs状态的通知,对于运行在Prow jobs中的测试,没有办法提供通知,在Kubernets使用testgrid结合Prow实现这部分功能,但是和gcs结合太紧还没有被开源。
在来也科技我们通过Prow落日志,结合来也科技的日志报警系统,对测试结果进行统计分析和报警。
图片

使用External plugins,修改代码或periodics扩展 Prow,增加CI功能

1.ChatOps方式,修改配置文件(具体内容下文配置文件CI流程)
2.更新镜像
在来也科技,会使用Prow periodics job自动对编译镜像、运行镜像进行升级。比如当go版本升级时,定时任务会检查到相应变更,同时触发相应go版本对应内部编译镜像的构建;再或者当运行时镜像出现升级后,如安全升级,一般不会直接修改服务所使用的Dockerfile。
同样的会通过periodics job定时检查项目使用的编译、运行镜像,如果有差异,自动修改,提交PR。项目维护者确定是否要对镜像进行升级。

CI流程

结合来也科技内部情况,我们主要CI流程如图,在PR阶段我们会把很多工作前置。
图片
PR检查:静态扫描,主要进行敏感字符检测、单元测试、第三方库黑名单、安全扫描等等
镜像构建:构建前也会对代码进行静态扫描,通过docker多阶段构建镜像,并对镜像进行扫描
自动发布:镜像构建成功后,通过helm自动部署到开发、测试环境,生产环境开发手动触发。
集成测试:服务上线后自动触发集成测试。包含两种测试方式,一种通过Prow触发,另一种通过来也科技集成测试平台Siber触发。Siber是来也科技开发的通过网页配置的方式实现接口调用和检查的集成测试平台。
安全检查:在生产环境上线前,会使用雳鉴对整个系统进行安全检查。
生产发布:集成测试后进行错误统计、追踪。我们会针对重要服务增加限制条件,在服务发布到生产环境的时候,会先通过集成测试平台检查预生产环境相应服务测试情况,如服务在预生产环境未通过测试,会强制中断服务上线。

分支CI策略

来也科技当前有5个环境,开发环境、x86架构测试环境、arm64架构测试环境、预生产环境、生产环境。根据环境及内部流程,来也科技使用Gitflow方式协作开发代码,CI工具参与其中的每个阶段。
开发阶段:开发者从master切出分支,命名为feature/*,在feature/* 开发,自测,feature/* 分支的代码会自动被部署到开发环境
测试阶段:feature/* 分支代码开发完毕,合并到test分支,代码也会自动上线到不同架构的测试环境,QA在这个分支上进行测试
发布阶段:test*分支测试完成后,合并到master分支,创建TAG,TAG使用Semantic Versioning规则命名,TAG相应代码会自动的执行构建流程
hotfix:当生产环境需要打补丁时,从master分支拉出 hotfix 分支,修复后,同时合 test、master,打tag发布

配置文件CI流程
来也科技内部并没有使用配置中心,而是使用传统的配置文件。
最初存在的问题:
1.微服务架构,项目多,配置文件多、配置项多
2.产品需要支持私有部署,服务数量多,手动配置不现实,私有部署永远无法自动化
3.人工编辑,人为错误概率太大,迟早会出现问题
4.多环境,无法持续
针对存在的问题,考虑到配置中心如果出现异常影响范围大,最终我们还是没有启用配置中心,而是选择继续使用配置文件。我们设计了新的配置文件方案,每个项目提供一个配置模板,不同环境提供不同填充项,借此生成项目配置文件,再更新到相应环境。

配置模板

每个项目创建初期,都要求提供一个配置文件模板,通过上文提到的OWNERS文件方式,进行权限管理。
在不同环境(包括私有部署)内容不一样的地方需要使用模板变量。
模板示例:
图片

配置填充项

根据环境以及是否是敏感字符,变量对应填充项分布在不同Git仓库,由不同人进行维护。
上述模板示例渲染结果:

图片
除Git仓库中维护的填充项以外,还有针对项目单独生成的填充项,如MySQL实例账号,阿里云相关产品access key等。一方面可以减少部分维护工作,另一方面可以定期或者在需要的时候,快速完成所有服务账号的替换,也可以做到权限的最小化降低安全隐患。
MySQL通过项目账号、读写账号分离,可以更细粒度的控制服务对DB的访问,如控制账号链接数等;同时也可以做更多的统计分析,如对慢SQL进行分析、报警,进一步可以针对项目SQL进行kill等。
阿里云以OSS为例,在项目需要使用OSS的时候,对项目配置policyDocument即可,在生成配置文件之前,会生成相应access key,并配置相应权限,如图为配置示例:
图片

配置发布

非生产环境,在修改配置模板或者模板对应填充项的时候,会针对变更项目生成配置文件,同时根据项目最终会部署的namespace,生成ConfigMap,提交到ConfigMap仓库,通过GitOps的方式,自动更新到相应集群。
出于业务安全考虑,生产环境ConfigMap更新操作与服务部署方式一致,均由开发人员或运维人员手动触发变更。
在之前,ConfigMap的发布,需要运维人员生成后通过kubectl apply到相应集群。
当前配置发布流程:
1.开发人员在配置文件模板仓库中修改相应项目模板,提交PR
2.通过Prow presubmit 对PR进行检查
1.预生成
2.格式检查
3.配置review ,merge
4.评论/updateconfig,机器人在评论中展示脱敏后的配置diff结果
5.二次确认配置结果,评论/updateconfig,配置推送到ConfigMap仓库,通过ArgoCD在预生产环境生效,运行自动测试
6.评论/updateconfig,配置在生产环境生效,运行自动测试
通过/updateconfig env,可以指定环境部署配置。命令生效要求相应配置在前置环境已生效。
配置更新流程:
图片
ConfigMap的回滚,主要有两种方式,一种是直接revert配置模板仓库,然后重新触发ConfigMap deploy操作即可。另一种方式则是,直接在ConfigMap仓库进行revert。
通过配置文件模板可以根据模板以及填充项绘制出 项目 - 中间件关系图,在中间件需要进行维护、升级、迁移的时候,可以快速确认影响范围,提前做出相应预案,避免影响到系统。同样的当中间件出现异常的时候,可以根据关系图第一时间确认范围,进行报警,并通知项目负责人确认服务情况。

结语

从最开始手动阶段30个左右的服务到当前业务服务数量250+,从简易流程到当前完整的CI流程,来也科技一直只有一个SRE工程师,CI工具对提升工作效率具有举足轻重的作用。



本文作者:杨进杰,张勇

本文编辑:刘桐烔