1 Tekton 基础
Tekton 项目早先用于在 knative 项目中,而如今已经被独立出来成为一个单独的项目。
而 Tekton 本身作为一个独立的系统也有多个组件,所以在本章我着重介绍 Tekton 的基础概念以及各个组件的功能
Tekton 就是 CI/CD SERVER 的实现,用于帮助用户来构建 Pipelines ,至于 pipelines 中用到的那些工具,就需要我在制定 pipelines 时调用。
比如在 Jenkins 中想使用这些工具据需要安装许多的插件。
Tekton 工具集原理:
在 Tekton 实现这个功能的办法并不需要额外安装插件,而是需要 task,每一个 task 里同样也会有多个 step 组成,而后当需要运行这个流水线的时候,就相当于把这个 Task 运行起来,而当 Task 运行起来之后就相当于将 Task 里面的 Step 运行
假如在某个 Task 中定义了 3 个 step ,那么就会分别按照顺序运行这三个 Step,而且每一个 Task 就是一个独立的 Pod 中,在一个 task 的 Pod 中又有多个 Step,而这个 Step 就相当于一个独立的容器。
由于 step 是一个容器,所以就需要给这个 step 指定一个 image;因此该 step 需要使用到的流水线工具,其实就是在引用的 image 中就包含对应的工具集,所以在 Tekton 中想要实现工具集就不再和 JenKins 一样需要安装多个插件,而是确保该工具的镜像存在即可
注意:
这里在 Task 中的多个容器并非并行运行,而是依次运行;在 Tekton 中采用了以下方法来实现该功能:
当第一个容器执行完毕退出之后才会创建第二个 Step 容器,等第二个 Step 容器执行完毕之后才会运行第三个 step ,而不是同时在运行以此类推
而每一个 step 执行处理之后的结果有可能会被剩下运行的 step 所引用,所以为了便以引用这里可能就需要通过一个 volume 来实现,将该 volum 都挂载至这三个 Step 之上,第一个 step 执行之后的数据写入到 volume 上,然后第二个 step 在进行读取,以此类推
但是这个 volume 先要实现持久化的话就需要使用到 PVC 来实现
一个流水线可以有多个 Task 来组成,而多个 Task 可以按照管理人员的需要将其定义为多个并行运行或者多个串行运行
Tekton Pipelines 提供了上面的 CRD,其中部分 CRD 与 Kubernetes core 中资源相对应
Task => Pod
Task.Step => Container
1.1 Tekton 系统组件
Tekton Pipelines:并非指定某一个特定的流水线,而是这个组件本身就叫做 Pipeline
- Tekton 最核心的组件,由一组 CRD(该 CRD 提供了 Task、pipeline 等功能)和相关的 Operator(而 Operator 则是实现 CRD 的运行)、Webhook (提供准入控制器功能)共同组成
-
需要部署并运行于 Kubernetes 集群之上,作为 Kubernetes 的集群扩展功能
Tekton Triggers
- 触发器,可触发 Pipeline 的实例化;可选组件;
- Tekton Trigger 是一组 CRD ,主要用于监视外部提供事件的事件源上的相关事件,并将事件源对应的转换成对 Tekton Pipeline 中所定义的某个特定的 pipeline 模板的引用,从而触发并基于该模板创建出实例
Tekton CLI
- 命令行客户端工具,用于与 Tekton 进行交互;可选组件;
Tekton Dashboard
- Tekton Pipelines 的基于 Web 的图形界面;可选组件;
Tekton Catalog
- 由社区贡献的 Tekton 构建块(building blocks,例如Tasks和Pipelines等),用户可直接使用
Tekton Hub
- 用于访问 Tekton Catalog 的图形界面,基于 Web
Tekton Operator
1.2 概念模型
Tekton Pipelines 的 Pipeline 模型中存在三个核心术语:Step、Task 和 Pipeline
下面就是 Tekton 于 Jenkins 之间的组件功能对比
- Task :对应于 Jenkins 中的 stage
- Step:对应于 Jenkins 中的 Step
- Pipeline 对应于 Jenkins 中的 Pipeline
概念结论:
一个特定的流水线就应该定义为 pipeline ,在 pipeline 中的某一个阶段就定义为 task ,而在一个 task 中具体的执行任务就定义为 Step
也就是说如果我们定义了一个构建阶段,这个阶段(Step)中可能就包含了以下步骤:
- 克隆代码并存在至一个 volume 中
- 对改代码执行编译,以及单元测试
- 对改代码进行打包
- 将其打包的代码推送到构建仓库中(如镜像仓库)
如上图:
上图中是一次流水线(Pipeline)的执行次序,可以看到有多个 Task 分别是 A、B、C,然后在多个 Task 中又有多个 Step 的执行次序,这种就被称为有向无环图(也就是说有秩序方向,但是不会循环)
Step
- CI/CD 工作流中的一个具体操作,例如 Python web app的单元测试,或者是 Java 程序的编译操作
-
每个step都会通过一个特定Container(Pod中)运行
Task
- task 只是一个任务的具体代码,可以理解为就是程序代码,能够让我们运行一个 task ,但一个 Task 一次可以运行多次
- 在 Tekton 上每一个负责运行的 Task 有一个专门的称呼,叫做 Task Run
- Task Run 本身也是一个 CRD
- 由一组 Step 组成的序列,按照定义的顺序依次运行于同一个 Pod 内的不同容器中
-
可共享一组环境变量,以及存储卷
Pipeline
- Pipeline 本身也是一段代码,但是我们要是将 pipeline 运行起来之后就称为 Pipeline Run
-
由一组 Task 组成的集合,所以 pipeline 就是调用了一个又一个的 Task,因而如果 task 上有参数那么我们就需要通过 Pipeline 向对应的参数传值,从而完成 Pipeline 的实例化 ,可按照定义以不同的方式运行:串行、并行和DAG
-
一个Task的输出可由其后 Task 引用
Task 和 Pipeline 通常允许接受输入并对外输出,这些输入和输出我们可以定义为具体的口接受的参数,而后当我们运行 Task run 的时候就会给这些参数进行赋值,从而达成实例化
1.2.1 TaskRun 和 PipelineRun
- TaskRun 代表 Task 的一次具体执行过程,类似地,PipelineRun 代表 Pipeline 的一次具体执行过程
-
具体运行时,Task 和 Pipeline 连接至配置的 Resource 之上,进而创建出 TaskRun 和 PipelineRun
-
它们既可由用户手动创建,也可由 Trigger 自动触发
注意:
实际上,PipelineRun 自身并不执行任何具体任务,它是由按特定顺序运行的TaskRun组合而成
如上图:
我们可以将 TaskRun 和 PipelineRun 想象成一段代码,当着这个流水线真正执行的时候我们就可以在 K8S 集群之上看到同时运行着有一个或多个 Pod ,并分别代表着某一个 Task 的某一次特定运行(所谓的特定运行:当 pipeline 运行起来叫做 pipeline Run ,而 pipeline Run 就会对 pipeline 当中的 Task 创建为 Task Run)
对比来看,Pipeline 和 Task 更像是模板,而 PipelineRun 和 TaskRun 是以模板为基础实例化出的具体对象;这个实例化的过程中,最重要的一步就是参数赋值。
因此我们在定义 Pipeline 和 Task 的时候里面有很多参数,而我们需要向 Pipeline 的参数进行赋值从而创建出 Pipeline Run,而 Pipeline Run 将接收的值传递给 Task 从而创建出 Task Run ,然后来形成正真意义上 Pipeline 上具体的执行过程
Task Run 和 Pipeline Run 都是两个独立的 CRD
1.2.2 Input 和 Output resources
– Input 就是为 pipeline 提供输入资源
– 比如我们需要从代码仓库中加载源码生成镜像,并推送到镜像仓库(那么生成镜像的过程就是 input、而推送镜像的过程就是 output)
- 每个 task 或 pipeline 均可有其 Input 和 Output ,它们相应地可被称为 Input Resources 和 Output Resources ,例如
- 某个 Task 以 git repository 为 input,而 output 为 container image
- 该 Task 会从 git repository 中克隆代码、运行测试、执行构建并打包成容器镜像
- Tekton 支持如下几种类型的 resources
- git:一个特定的 git repository
- Pull Request:某 git repository 上的一次特定的 PR
- Image:容器镜像
- Cluster:Kubernetes 集群
- Storage:Blob 存储上的 object 或 directory
- CloudEvent
注意:
Input 和 Output Resources 已经被废弃,建议使用Parameters进行替代
1.3 Parameters
Parameters 是使得 Task 及 Pipeline 资源定义出的“模板”更加具有通用性的关键要素之一
具体到使用逻辑,例如:
- 大多数 CI Pipeline 的起始位置都是从 Git 仓库中克隆代码,而现在我们可以将这个仓库定义为 Parameters ,然后只需向 Parameters 传值即可,所以对应的这个代码克隆操作会对应一个 Task ,然后通过某个具体的 Step 执行,而这个 Step 执行的时候可能是执行的一条 Git 命令,那么最终的这个 Git URL 由拿来?
- 我们可以将 URL 写死在 Task 中,当然也可以通过参数来进行获取
- 显然,如若将 git 仓库的 url 硬编码在 Task 及其 Step 中,将使得该 Task 失去了绝大部分的通用性
-
于是,我们可以在 Task 中,将操作的目标(包括数据)定义为参数(Parameter),而后在 Step 的代码中引用这些参数作为操作对象
-
TaskRun 在针对该 Task 进行实例化时,通过向引用的 Task 中定义参数传值完成实例化
如下图:
如上图我们定义了一个 Task,并在该 Task 中定义了参数 (Params),而在参数中定义了 foo 和 bar ,随后创建两个 Task Run ;意思就是对该 Task 进行实例化。
随后引入了第一个 Task,然后向第一个 Task Run 实例中的 foo 传入了 zgy 和向 bar 传入值也是 zgy。
然后对第二个 Task 实例传入 foo 为 hi 、bar 的值为 hello。
以上就是两个不同的 task 运行实例
1.3.1 Parameters 多级调用
实际应用中,我们一般是通过 Pipeline 基于 Task 来创建 TaskRun 对象的,而非直接创建 TaskRun,所以这个时候我们要想使用 Parameters 该如何定义。如下图:
先创建 Pipeline Run 从而实现将 pipeline 运行起来,并在 pipeline Run 中实现对 Pipeline 的 parameter Declaration(参数发布)赋值,因此在 pipeline Run 中赋值被传递给 pipeline ,然后 Pipeline 基于 Task 来创建 TaskRun 并将从 Pipeline Run 中接收的值传递给 Task ,所以 Task Run 才会运行起来并得到对应的值
流程如下:
- Pipeline RUN 运行并赋值传递给 pipeline
- pipeline 将接收值并将值定义在要创建的 Task Run 中
- Task Run 运行并得到由 Pipeline RUN 提供的值
- Pipeline 可引用已有的 Task ,或者直接内嵌专有的 Task 代码,其目标在于创建 TaskRun
-
为了完成实例化,Pipeline 需要向 Task 的 Parameter 进行赋值,而该赋值也可以是对 Pipeline 级别的某个 Parameter 的引用
-
而对 Pipeline 上的 Parameter 的赋值,则由 PipelineRun 进行
1.4 Pipeline 和 Task 上的数据共享
Pipeline 上可能会存在数据共享的需要,例如:
- 一个 Task 的多个 Step 之间,靠前的 Step 生成的结果,需要由后面某个 Step 引用
-
一个 Pipeline 的多个 Task 之间,前面的 Task 处理的结果,需要由后面的某个 Task 引用
常见的解决方案有两种
- Results
- 由 Task 声明
- 它将 Task 中 Step 生成的结果保存于临时文件中(
/tekton/results/<NAME>
),而后由同一 Task 中后面的 Step 引用,或者由后面其它 Task 中的 Step 引用 - 文件名也能够以变量形式引用,例如
"$(results.<NAME>.path)"
- 由 Tekton 的 Results API 负责实现,仅可用于共享小于 4096 字节规模的小数据片
- Workspace
- 由 Task 声明的,且需要由 TaskRun 在运行时提供的文件系统
- 通常对应于 Kubernetes 上的 ConfigMap、Secret、emptyDir、静态 PVC 类型的卷,或者是 VolumeClaimTemplate 动态请求的 PVC
- emptyDir 的生命周期与 Pod 相同,因此仅能在一个 TaskRun 的各 Step 间共享数据
- 若要跨Task共享数据,则需要使用PVC