1.背景
1.1 目前使用 Jenkins 遇到的问题
- 编排引擎不稳定
Jenkins 是由 Java 编写的编排引擎,在 Full GC 时会 Stop The World(STW)。在大规模构建时,STW 可能会导致 Jenkins 无法处理新的请求。
- 大量构建卡顿
Jenkins 使用磁盘文件存储数据,每条流水线、每次构建都会占用一个文件目录,产生大量文件。通常流水线数量有限,但在构建达到 10000+ 级别时,会感受到 IO 对 Jenkins 的影响。
- 开发插件成本高
虽然 Jenkins 已经有很多的插件,但是面对内部庞大的各种系统,CICD 系统依然有开发插件的需求。开发 Jenkins 插件,需要掌握 Java 语言,学习 Jenkins 的插件机制。开发插件就是以 Jenkins 的运行周期为切入点,对其进行扩展。首先根据需要扩展的功能,在 Jenkins Packages 文档中,找到扩展的类。然后,在插件的主类中 extends 扩展类,实现自己的业务逻辑。
- 并发性能差
由于 Jenkins 本身的限制,在 Kubernetes 上无法运行多个副本。基于 Kubernetes 的 Jenkins 并发量,构建并发量最多达到 400 左右时会出现明显瓶颈,继续提升需要架构层面的较大优化升级。
1.2 对 CICD 的诉求
- 跨网络
服务上云,但代码不能出公司。需要在云上组装,而在内网构建容器镜像。
- 可大规模执行流水线
CICD 提供的是一次性运行时。CICD 是自动化系统,执行次数越多,意味着节省的人力时间越多。在未来,CICD 会承载越来越多的场景。集群安装、证书巡检…
- 零停机运维
之前编排引擎的维护主要集中在凌晨,因为每次重启 Jenkins,都需要花费数分钟时间,在这个时间段内,CICD 系统无法提供服务。
- 较短时间交付,持续迭代
设计一个庞大而完善的系统并不是初衷,我们希望快速验证想法,投入使用,然后不断地快速迭代,优化并完善系统。
2. 选型比较
2.1 一个好的 CICD 具备哪些特征
一个好的 CICD 工具应该具有如下特点:
- Outer DSL 简单易掌握 - User
- Inner DSL 高效易维护 - Developer
- 生态,能复用的原子要多 - Ecosystem
通过 UDE 可以给一个 CICD 工具评分,下面对常见的几个 CI 进行比较:
- Jenkins
Outer 是 Groovy 编写的 Jenkinsfile 文件,Inner 是 Java 编写的 Jenkins。UD 都不算好,Jenkins 难以维护,但插件庞大,E 大大加分。
- GitLab CI
Outer 是 Yaml 编写的 .gitlab-ci.yml 描述文件,Inner 是 Ruby 编写的解析引擎,使用 Go 写的 Runner。U 很好,上手很快,之前也写过一些文档,GitLab。D 不算好,Ruby 性能一般,会的人越来越少。E 就比较糟糕了,虽然有类似 Jenkins share library 的 template 提供原子级别的复用,但跨团队的复用率很低,不利于构筑社区生态。
- Tekton
Outer 是 Yaml 编写的 PipelineRun 描述,Inner 是 Go 编写的 Controller,不断地在 Kubernetes Pod 上执行编排流程。插件方面,目前 Tekton 社区提供有一百多个插件以供复用。
2.2 Tekton vs Jenkins
在最近几年的编排引擎市场份额调查中,Jenkins 连续多年超过一半的使用率,这是 Jenkins 近 20 年积累、头部虹吸效应的结果。但进入云原生时代之后,基础设施发生了变化,Jenkins 并没有很好的跟上脚步,Jenkins X 放弃 Jenkins 转而使用 Tekton 作为默认编排引擎。而自研编排引擎成本过大,因此,这里主要将 Jenkins 与 Tekton 进行对比:
编程语言 | Java | Golang |
开发插件语言 | Java | Shell、Yaml |
流水线描述语言 | Groovy、Shell | Yaml、Shell |
插件生态 | 很多插件,LDAP、GitLab | 不足 |
插件数量 | 1500+ | 100+ |
插件之间的兼容性 | 可能会有冲突,不能随便升级 | 完全兼容 |
二次开发 | 封装 Api | 组合 Task |
是否高可用 | 集成 Gearman、主从模式 | 依赖 Kuberntes 的高可用 |
单实例并发构建规模 | 几百并发 | 依赖 Kuberntes 的 Pod 管理能力,可以很大 |
数据存储 | 本地磁盘 | Etcd |
是否支持自动触发 | 支持 | 支持 |
是否有商业支持 | 无 | 无 |
3. 基于 Tekton 的解决方案
3.1 Tekton 包含哪些组件
- Pipeline
CI/CD 工作流程的基础模块,用来创建任务、流水线。
- Triggers
CI/CD 工作流程的事件触发器,可以用来根据事件自动触发流水线。
- CLI
用于管理 CICD 工作流的命令行工具。
- Dashboard
一个通用的流水线 Web 管理工具。
3.2 Tekton 流水线由什么构成
上面是一个 Pipeline 的示意图。一个 Pipeline 通常由多个 Task 组成,一个 Task 具有一个独立 Pod 运行环境。这些 Task 串、并执行。而每个 Task 中,又有若干个 Step ,Step 是串行执行的。一个 Step 具有一个独立 Container 运行环境。下面是一个执行简单脚本的流水线示例:
|
|
3.3 支持多集群构建
支持多集群能能显著增强 CICD 的可扩展性、可维护性。Tekton 已经将所有资源 CRD 抽象,这意味着我们借助于 KubeFed v2、Karmada、Open Cluster Management 等开源组件,可以非常容易实现流水线资源在多集群下的分发。如上图,我们将全部流水线资源创建在 host 集群,用于元数据的管理。然后通过开源的多集群管理方案,对不同集群上的资源进行分发。每个集群是一个单独的构建环境,这样能够有效地分散 CICD 流水线带来的负载压力。按照目前的资源规划,公司内网的服务器资源非常有限,我们需要尽可能使用云上的资源进行组装。host 集群、部分 worker 集群在公网,而执行 CI 构建的集群必须在内网。这里需要将 host 集群与 worker 集群之间能够网络打通。通过隧道打通网络是危害内网安全的操作,因此利用开源多集群组件实现不具备可行性。但我们并未就此止步,这种设计给了我们启发。
3.4 实现的架构
如上图,是一个我们目前实现、并准备继续优化的架构。主要分为三个部分:
- web
提供用户操作界面,通过图形化的方式编辑、描述流水线。
- apiserver
提供 web API 接口、worker 拉取任务的接口
- worker
拉取当前集群的流水线任务、执行并推送结果用户在 web 端创建流水线,通过 Apiserver 保存在 DB,同时产生一条同步事件。Worker 通过轮询的方式,从 Apiserver 拉取消息队列中的同步任务,接着在当前集群执行。执行完成之后,将执行的结果和相关的日志推送 Apiserver 保存至 DB 中。最终,用户在页面上可以从 DB 中直接查看执行的结果。值得注意的是,一个集群上可以跑多个 worker 服务,对集群的要求也只是能连通 Apiserver,因此在公网、内网都可以接入 worker 集群进行构建。
4. 总结与展望
4.1 功能丰富、性能优化
好用的产品是在需求之下不断地打磨出来的。我们会继续的收集大家对 CICD 的需求,并完善 CICD 系统。
- 审批功能
流程控制是 CICD 必备的功能之一。通过 runAfter,可以控制 task 任务之间的执行顺序和依赖,但审批功能 Tekton 社区并没有提供解决方案。我们针对审批功能提供了两种方案,正在设计和实现中。
- 接入物理机构建
由于目前主要服务于 web和后端项目的镜像构建,暂时没有提供物理机的接入。但我们已经考虑了方案,只等用户需求。
- 子流水线
子流水线允许将一条流水线拆分成多个,不同的子流水线可以在不同的 worker 集群执行,同时可以更好的控制流程。
- 流水线集群管理
目前的流水线后端是支持多集群的,但是前端暂时没有提供设置入口。支持多集群构建是这次设计的亮点之一,我们也希望能够尽快提供用户自助接入、自助管理、自助使用。
4.2 承载更多功能的 CICD 系统
除了这次的具体设计实现,我还想聊一下对 CICD 系统的理解。通常,我们认为 CICD 系统只是用来做构建、发布。但实际上,CICD 提供的是一种运行时,与 Serverless 相对应。这种运行时,可以承载很多的应用场景,甚至替代一些 SaaS 。这里说两个场景:
- 交付
在 Kubernetes 集群下,我们可以使用 Helm 进行交付应用。但是如何交付 Kubernetes 呢?面向 VM/裸金属服务器的服务如何交付呢?答案就是流水线。服务商通过 Task 插件封装各自的服务,提供给集成商。而集成商通流水线编排各种服务,面向客户提供交付的解决方案。
- 自动化运维
1.故障处理2.增删节点3.申请资源4.服务变更…