Kubernetes 原生 CI/CD 构建框架 Tekton 详解!

K8SMeetup社区
作者:FogDong(Tekton 社区)
编辑:Bach(才云)

什么是流水线?

在计算机中,流水线是把一个重复的过程分解为若干个子过程,使每个子过程与其他子过程并行进行的技术,也叫 Pipeline。由于这种 s工作方式与工厂中的生产流水线十分相似, 因此也被称为流水线技术。从本质上讲,流水线技术是一种时间并行技术。以“构建镜像”过程为例:

图片

在每一次构建镜像中,我们都需要拉下代码仓库中的代码,进行代码编译,构建镜像,最后推往镜像仓库。在每一次代码更改过后,这一过程都是不变的。使用流水线工具可以极大的提升这一过程的效率,只需要进行简单的配置便可以轻松的完成重复性的工作。这样的过程也被称之为 CI。

上图流程中使用的是 Jenkins。Jenkins 作为老牌流水线框架被大家所熟知。在云原生时代,Jenkins 也推出了 Jenkins X 作为基于 Kubernetes 的新一代流水线,但今天本文主要介绍诞生于云原生时代的流水线框架——Tekton。

Tekton

Tekton 是一个基于 Kubernetes 的云原生 CI/CD 开源框架,属于 CD 基金会的项目之一。Tekton 通过定义 CRD 的方式,让用户以灵活的自定义流水线以满足自身 CI/CD 需求。

基本概念

四个基本概念

Tekton 最主要的四个概念为:Task、TaskRun、Pipeline 以及 PipelineRun。

图片

  • Task: Task 为构建任务,是 Tekton 中不可分割的最小单位,正如同 Pod 在 Kubernetes 中的概念一样。在 Task 中,可以有多个 Step,每个 Step 由一个 Container 来执行。
  • Pipeline: Pipeline 由一个或多个 Task 组成。在 Pipeline 中,用户可以定义这些 Task 的执行顺序以及依赖关系来组成 DAG(有向无环图)。
  • PipelineRun: PipelineRun 是 Pipeline 的实际执行产物,当用户定义好 Pipeline 后,可以通过创建 PipelineRun 的方式来执行流水线,并生成一条流水线记录。
  • TaskRun: PipelineRun 被创建出来后,会对应 Pipeline 里面的 Task 创建各自的 TaskRun。一个 TaskRun 控制一个 Pod,Task 中的 Step 对应 Pod 中的 Container。当然,TaskRun 也可以单独被创建。

综上可知:Pipeline 由多个 Task 组成,每次执行对应生成一条 PipelineRun,其控制的 TaskRun 将创建实际运行的 Pod。下面以一个简单例子来展示这些概念。

首先,创建一个最简单的 Task,里面仅有一个 Step。在一个 ubuntu 镜像中执行 ls 命令。

图片

接着创建一个 Pipeline,里面引用第一步中创建的 Task。

图片

在 Pipeline 存在的前提下,就可以通过创建 PipelineRun 来运行 Pipeline。

图片

这样就完成了一个最简单的 Tekton 流水线案例。每一个 PipelineRun 的创建,都会遵循 Pipeline 中的顺序规则去启动 Task 的 Pod。下面引入另外一个概念 PipelineResource 来完成一个稍微复杂的例子,也是 DevOps 中最常见的场景:从代码仓库拉取镜像、进行代码构建、并最终将构建好的镜像推往镜像仓库。

PipelineResource

PipelineResource 代表着一系列的资源,主要承担作为 Task 的输入或者输出的作用。它有以下几种类型:

  • git:代表一个 git 仓库,包含了需要被构建的源代码。将 git 资源作为 Task 的 Input,会自动 clone 此 git 仓库。
  • pullRequest:表示来自配置的 url(通常是一个 git 仓库)的 pull request 事件。将 pull request 资源作为 Task 的 Input,将自动下载 pull request 相关元数据的文件,如 base/head commit、comments 以及 labels。
  • image:代表镜像仓库中的镜像,通常作为 Task 的 Output,用于生成镜像。
  • cluster:表示一个除了当前集群外的 Kubernetes 集群。可以使用 Cluster 资源在不同的集群上部署应用。
  • storage:表示 blob 存储,它包含一个对象或目录。将 Storage 资源作为 Task 的 Input 将自动下载存储内容,并允许 Task 执行操作。目前仅支持 GCS。
  • cloud event:会在 TaskRun z执行完成后发送事件信息(包含整个 TaskRun) 到指定的 URI 地址,在与第三方通信的时候十分有用。

以上为 Tekton 目前支持的六大 PipelineResource 类型,具体的配置及使用方法详见 PipelineResource 文档。

文档地址:https://github.com/tektoncd/p...

继续分析较复杂的流水线案例:从代码仓库拉取镜像、进行代码构建、并将构建好的镜像推往镜像仓库。从已有的 PipelineResource 类型可判断,可以使用 git 类型作为代码资源作为输入,再用 image 类型作为镜像资源作为输出。有了输入输出后,我们可以直接使用 Kaniko 来构建镜像。

Kaniko 是 Google 开源的项目之一,可在 Kubernetes 上无需特权模式地构建 docker 镜像。

首先创建这两个 PipelineResource。在这个例子中,git-input 对应输入,image-output 对应输出。params 中的参数均为该资源类型的固定参数:如 git 中可以通过 revision 指定版本号,image 中可以通过 url 指定镜像仓库地址。

Git-input:

图片

Image-output:

图片

在配置 PipelineResource 时,如果使用了私有仓库,还需要配置 Service Account,详见 configuring-authentication-for-docker。

configuring-authentication-for-docker 地址:https://github.com/tektoncd/p...

产物传递

创建完 PipelineResource 后,需要在 Task 中引入它们作为输入输出。那么,这些资源是如何在 Task 间传递的呢?

在 Tekton 的分区下,我们可以看到一个叫做 config-artifact-pvc 和一个叫做 config-artifact-bucket 的 Config Map。从命名就可以看出,这二者分别代表了产物存储的两种配置方式—— PVC 和存储桶(目前支持 GCS 和 S3)

以 PVC 为例,修改 config-artifact-pvc 需要填写两个值:size 以及 storageClassName。size 默认为 5GiB,storage class name 默认为 default。这也意味着当我们使用 PipelineResource 进行资源传递时,会自动创建一个 5GiB 的存储卷挂载在 Task 上,供 PipelineResource 使用。

在需要进行 Task 间的资源传递时,这个存储卷会被挂载在 Task 的 /pvc 目录下。当 Task 执行完成并且需要进行资源传递(通过 inputs/outputs 指定)后,TaskRun controller 会自动添加一个拷贝文件的步骤容器,并将输出产物统一放到 /pvc/task_name/resource_name 命名规范的目录下。

上面是针对产物需要进行传递的情况下,对于目前例子而言,由于只需要一个 Task,虽然指定了 Inputs 和 Outputs,但并没有另一个 Task 来引用这些结果。因此,在这个例子中并不会去挂载 PVC。

对于 git 以及 storage 类型的 input,资源下载后会被 放在 /workspace/task_resource_name 下;对于 output 则会放在 /workspace/output/resource_name 下。image 类型的资源则会直接上传到镜像仓库。

了解了这些前置知识后,我们可以来创建 Task 了。Kaniko 需要三个参数来完成镜像构建:Dockerfile 的地址,context 的地址以及镜像仓库的地址。在下面这个例子中,我们大量使用了 params 以及 Tekton 中的变量替换。Params 用于在 TaskRun 和 Task 中传递参数,而变量替换的格式为 $(xxx)。使用这些变量可以让 Tekton 在运行过程中根据规则进行赋值。值得注意的是,Tekton 并不会提前去检查这些变量的内容,这就要求着我们在写的时候需要多加注意。具体的变量编写规则详见:Tekton variables。

Tekton variables 地址:https://github.com/tektoncd/p...

图片

有了 Task 后,就能创建 TaskRun 来执行 Task。注意,在 spec 中申明了 serviceAccountName 用于指定私有仓库的权限。

图片

至此,一个更为复杂的流水线也构建完成了。

DAG

在 Tekton 中,DAG(有向无环图)的功能是原生支持的。只需要通过申明 runAfter 及 from 便可以便利的使 Pipeline 以 DAG 方式运行。

  • from:当 Task 的 Inputs 依赖于上一个 Task 的 Outputs 时,可以通过 from 参数来指定
  • runAfter:当 Task 间没有资源依赖,但需要使一个 Task 在另外一个 Task 之后运行的话,可以使用 runAfter 来指定。

图片

例如在上面的例子中,任务会以下顺序运行:

图片

  • lint-repotest-app 中的 Task 没有 fromrunAfter 关键字,会同时开始执行。
  • 一旦 test-app 完成,build-appbuild-frontend 都会开始同时执行,因为它们 runAftertest-app
  • deploy-all 会在 build-appbuild-frontend 都完成后才执行,因为它需要的资源 from 于这二者。

再来看看 Tekton 是怎么样来实现这段逻辑的:

在 Pipeline 的 Controller 中,一旦监听到 Pipeline 的创建,在创建对应的 TaskRun 之前,会先检测 Pipeline 中的依赖顺序并构建 DAG 图:

图片

Step 执行顺序

Pipeline 中可以进行对 Task 的顺序控制,那么 Task 中呢?

在 Kubernetes 中,Pod 里的 Container 是并行启动的。而在 Tekton 中,虽然 Task 对应 Pod,Task 中的 Step 对应 Container,但 Task 中的 Step 却是顺序执行的。要了解 Tekton 是怎么完成这样的顺序控制,首先我们来看一下一个 Tekton 的 Pod。

在这个 Pod 中,除了用户需要运行的 Container,还被注入了一个 InitContainer:

图片

这个 InitContainer copy 了一个 entrypoint 的二进制到 Pod 中。再看下用户的 container,我们可以看到 Pod 的执行命令被 Tekton 改写了一下:

图片

可以看到 command 被改写为了 entrypoint 命令,这个二进制包在 initContainer 中被导入,另外还有一些启动参数:

  • -post_file:指定了 Step 完成后的文件写入路径。如果 Step 失败,则写入到 {{post_file}}.err。可以看到上面的写入路径为 /tekton/tools/0,最后的这个数字即为 Step 的编号。
  • -wait_file:指定了在启动下一个 Step 之前要查看的文件路径。它将监听 {{wait_file}}{{wait_file}}.err。若有错误则跳过执行写入 {{post_file}}.err 并返回错误(exitCode >= 0);若无错误则执行下一个 Step。如上例子为第一个 step,若为第二个 step,wait_ file 的地址会是 /tekton/tools/0,也就是上一个 step 的 post_file 地址。

资源控制

在 Kubernetes 中,一个 Pod 被调度需要节点满足 Pod 中的所有 Container 的资源。如下图:

图片

这个 Pod 有 4 个容器,总共需要 9 个 CPU。Kubernetes 将把这个 Pod 调度到一个拥有 9 个可用 CPU 的节点上。如果没有节点有 9 个可用 CPU,Pod 将被调度失败并无法启动。

而对于 Tekton 而言,因为 Pod 中的 Container 会顺序执行,所以只需要满足这个 Pod 中资源最大的 Container 即可。对于同一个TaskRun,Tekton 会获取最大请求,并让一个 Container 去请求这些资源,其他都设为 0。

如下,该 Pod 请求 4 个 CPU,而不是 9 个。这样的资源控制方式更为合理且所有的 Step 容器仍保留所需要的资源。

在有 LimitRange 限制 Container 必须有资源的的情况下,每个 Container 最小会设置为 LimitRange 的设置。

图片

源码部分逻辑如下:

图片

数据传递

除了 PipelineResource 以外,Tekton 还提供了其他数据传递的方式。

PipelineResource 仍处于 Alpha 版本,它有可能会被重新设计、替换、弃用或者完全删除。Tekton 社区鼓励用户用 Task 代替 PipelineResources。

Workspace

Workspace 与 Kubernetes 中 Volume 概念几乎保持一致,只不过并不是 Pod 层级的而是作用于 Tekton 资源层级的。Workspace 在 Pipeline 中使用时是一个抽象的概念,实际的存储类型需要在 PipelineRun 中指定。详见:Workspaces。

Workspaces 地址:https://github.com/tektoncd/p...

Results

Tekton 提供了一个固定目录用于存放 Task 的输出:/tekton/results

图片

如上,该 task 将日期输出到了 /tekton/results/current-date 中。同时,也会被作为 Results 字段加到 TaskRun 的 Status 中。这样,其他的 Task 便可以通过 $(tasks..results.) 来获取到该 Task 的 results。(变量替换将会实际从 TaskRun 中获取到 Results 的值)

其他流程控制功能

条件判断

低版本可以使用 Conditions,高版本推荐使用 WhenExpressions(Conditions 将在不久后废弃,完全替换为 WhenExpressions)。WhenExpressions 由 Input、Operator、Values 三部分组成,其中 Input 可以使用 Tekton 的 Parameter 或者 Results,Operator 目前仅支持 in 和 notin:

图片

错误重尝

通过 retries 来指定任务失败后重新尝试的次数:

图片

退出处理

通过 finally 指定在 pipeline 结束时执行的 task,无论 pipeline 的结果是成功或失败。

图片

取消执行

要取消当前正在执行的 PipelineRun,可以在其 Spec 中更新 Status 为取消。当 PipelineRun 被取消时,所有相关的 Pods 都被删除。例如:

图片

Pipeline 暂停的逻辑与之类似,但暂停 PR 尚未合入,暂停功能也在 Tekton 今年的 Roadmap 中。

Runs

Runs 是一个进行中的 feature,Run 允许实例化和执行一个 Custom Task,这个 Custom Task 可以通过用户自定义的 controller 来执行。这对于用户来说是一个非常实际的功能,可以通过自己写的 Controller 来定义 Task 的逻辑,而不再拘泥于 Tekton 定义的 Task。

阅读 1.5k

K8sMeetup
致力于提供最权威的 Kubernetes 技术、案例与 Meetup!

致力于提供最权威的 Kubernetes 技术、案例与 Meetup!

78 声望
336 粉丝
0 条评论

致力于提供最权威的 Kubernetes 技术、案例与 Meetup!

78 声望
336 粉丝
文章目录
宣传栏