1. 解耦引擎释放流水线能力
在设计系统时,我们常面临两难。是内敛复杂度,对外提供单一易用的功能;还是释放复杂度,将灵活归还用户。这非常考验产品能力。设计 CICD 系统时,我们可以直接将 Jenkinsfile、PipelineRun 等概念直接抛给用户,让用户自己学习相关领域的知识,再来使用产品。当然,也可以继续抽象,在人与系统之间建立模型,实现意识与指令的转换。我们想要更加易用的产品,因此选择屏蔽底层概念,继续抽象、建模。从 Jenkins 、GitLab CI,再到 GitHub Actions、Tekton,新的基础设施总会有各种各样的基础组件涌现。我们想减少这种切换的成本,在各种引擎之间能够切换。技术在不断地更替,但我们想对用户保持一致。虽然流水线相关技术在快速演进,但执行这件事的终究是人。人的知识是有传承的,无论技术怎样变化,做流水线引擎的社区是相对稳定的,都是有交集的一群人。这给解耦引擎,设计通用流水线提供了可能。
2. 流水线的数据模型
在很多 CICD 引擎中,能够找到相似的概念。
- Jenkins
流水线包含很多个 Stage,而 Stage 中的 steps 包含多个串行执行的脚本。
|
|
- GitLab CI
流水线包含 build、test 两个 串行的 Stage,每个 Stage 包含若干并行执行的 Job 。
|
|
- GitHub Actions
流水线由 jobs 定义,一个流水线有很多个可能的 job(示例中的 build 构成),每个 job 又包含很多串行的 steps。
|
|
基于以上的示例,我们对 Pipeline 进行抽象。
|
|
如下图,一个流水线包含若干个 Stage, Stage 可以并行或者串行。Stage 中包含若干个串行的 Step 执行脚本。而在 Tekton 中,Stage 对应着 Task 。一个流水线的运行时,可以是一个 Kubernetes 集群、一个物理机、一个 Container 环境等。一个 Stage 的运行时,可能是一个 Pod、一个物理机、一个 Container 环境等。一个 Step 有一个工作空间,然后执行 Shell Script。流水线并不需要复杂的定义,即使几个简单的脚本,也可以编排复杂的逻辑。但是对流水线进行抽象和建模,有利于插件(Step)扩展,流水线产品本身的开发和维护。流水线会经过一系列的 Manager 处理,与特定的运行时、执行引擎、凭证等关联起来。最终渲染出引擎接受的流水线描述,例如 Jenkins 的 Jenkinsfile、Tekton 的 Yaml。
3. 代码层的数据模型
下面给出核心数据结构的主要字段:
|
|
在代码层面,需要注意两点:
- 模板与实例。模板是系统内置的引擎相关的框架或片段,例如 Step、Stage、Pipeline ;而实例是填充个性化参数之后的模板或片段,PipelienStep、PipelineStage、PipelineRun 。
- 作用范围。参数中有一个字段 Scope ,用于表示参数在什么范围可见。实际上,真正使用参数的是 Step,但是组装之后 Stage 作用域的参数会被提升到 Stage 中。同理,Pipeline 作用域的参数会被提升到 Pipeline 中。参数在不同的层级会有不同的用处,从内向外抽取,从外向内注入。
4. 流水线运行时的数据与交互
上面是一个流水线的执行流程,可以对照着每个步骤查看,这里不再重复。下面主要以不同角色的视角描述流水线的运行。
4.1 系统内置 Step 插件模板
首先需要在系统中内置一些常用的插件 Script。例如,Jenkins 的 Git Clone 插件
|
|
Jenkins 的构建并推送镜像。
|
|
Jenkins 执行脚本。
|
|
当然也可以是其他引擎的插件片段,但主要是脚本片段 + 参数注入,因此不再列举。
4.2 创建流水线时,用户视角
如上图,用户首先会根据用户选择的引擎,得到一个 Step 模板列表。然后通过编排,将 Step 组装成一个流水线。其中,Step-1、2、3 表示的是选择一个模板 Step 并初始化参数的实例。然后组装出 Pipeline 的数据结构保存在后端。如果是使用模板进行创建,那么只需要提前帮用户初始化 Pipeline 数据结构即可。
4.3 创建流水线,开发人员视角
- 前端研发
请求 Step 模板列表之后,根据用户输入的实例化参数,组装 Pipeline 结构。
- 后端研发
将用户的 Pipeline 数据保存之后,根据 Step 模板信息,将 Pipeline 渲染成引擎需要的流水线描述。例如,生成 Jenkinsfile 文件,同步到 Jenkins 创建流水线。
4.4 运行时,数据流向及交互
- 执行流水线
前端调用后端接口,获取 Pipeline 的定义,将 Pipeline 级别的参数弹框,让用户输入相关的参数。点击确认后,创建 PipelineRun 对象。后端根据 PipelineRun 对象,触发引擎的执行接口,将 PipelineRun 中定制化的参数传入流水线执行。
- 查看流水线
PipelineRun 即为流水线的执行历史,需要从引擎中查询流水线的状态,并写入 PipelineRun 对象。
- 再次运行、暂停、继续、审批
在 PipelineRun 中记录有某次执行的全部记录,包括参数、Pipeline 定义。因此,只要引擎支持上述功能,都可以实现。