乔克视界 乔克视界
首页
  • 运维
  • 开发
  • 监控
  • 安全
  • 随笔
  • Docker
  • Golang
  • Python
  • AIOps
  • DevOps
  • Kubernetes
  • 心情杂货
  • 读书笔记
  • 面试
  • 实用技巧
  • 博客搭建
友链
关于
收藏
  • 分类
  • 标签
  • 归档

乔克

云原生爱好者
首页
  • 运维
  • 开发
  • 监控
  • 安全
  • 随笔
  • Docker
  • Golang
  • Python
  • AIOps
  • DevOps
  • Kubernetes
  • 心情杂货
  • 读书笔记
  • 面试
  • 实用技巧
  • 博客搭建
友链
关于
收藏
  • 分类
  • 标签
  • 归档
  • Docker

  • Golang

  • AIOps

  • Python

  • DevOps

    • Tekton

      • Tekton系列之安装篇
      • Tekton系列之理论篇
        • 实现原理
        • PipelineResources
        • Tasks
        • TaskRuns
        • Pipelines
        • PipelineRuns
        • Conditions
        • 鉴权管理
        • 参考文档
      • Tekton系列之实践篇-我的第一条Pipeline
      • Tekton系列之实践篇-把Jenkinsfile变成Tekton Pipeline
      • Tekton系列之实践篇-使用Jenkins来管理Tekton
      • Tekton系列之实践篇-使用Tekton Trigger使Tekton使用更简单
      • Tekton系列之实践篇-Tekton和Argocd的梦幻联动
    • ArgoWorkflow

    • Gitlab

    • Ansible

  • Kubernetes

  • 专栏
  • DevOps
  • Tekton
乔克
2025-07-19
目录

Tekton系列之理论篇

上一篇文章我们介绍了 Tekton 的安装并且做了简单的测试,但是我们并不知其所以然,而这篇文章主要带大家来了解以及学习所以然。

Tekton 是开源的云原生 CI/CD 项目,是基于 Kubernetes CRD 来定义 Pipeline,功能强大并且很容易扩展。

在上篇文章中,我们安装完 Tekton 之后,可以看到安装的 CRD 如下:

# kubectl get crd | grep tekton
clustertasks.tekton.dev                       2022-02-28T06:15:38Z
conditions.tekton.dev                         2022-02-28T06:15:38Z
extensions.dashboard.tekton.dev               2022-02-28T06:18:40Z
pipelineresources.tekton.dev                  2022-02-28T06:15:38Z
pipelineruns.tekton.dev                       2022-02-28T06:15:38Z
pipelines.tekton.dev                          2022-02-28T06:15:38Z
runs.tekton.dev                               2022-02-28T06:15:38Z
taskruns.tekton.dev                           2022-02-28T06:15:38Z
tasks.tekton.dev                              2022-02-28T06:15:38Z
1
2
3
4
5
6
7
8
9
10

其中Task、TaskRun、Pipeline、PipelineRun、PipelineResource、Condition作为其核心 CRD,这里主要介绍它们。

  • Task:定义构建任务,它由一系列有序 steps 构成。每个 step 可以定义输入和输出,且可以将上一个 step 的输出作为下一个 step 的输入。每个 step 都会由一个 container 来执行。
  • TaskRun:Task 用于定义具体要做的事情,并不会真正的运行,而 TaskRun 就是真正的执行者,并且会提供执行所需需要的参数,一个 TaskRun 就是一个 Pod。
  • Pipeline:顾名思义就是流水线,它由一系列 Tasks 组成。就像 Task 中的 step 一样,上一个 Task 的输出可以作为下一个 Task 的输入。
  • PipelineRun:Pipeline 的实际执行,创建后会创建 Pod 来执行 Task,一个 PipelineRun 中有多个 Task。
  • PipelineResource:主要用于定义 Pipeline 的资源,常见的如 Git 地址、Docker 镜像等。
  • Condition:它主要是在 Pipeline 中用于判断的,Task 的执行与否通过 Condition 的判断结果来决定。

Tips:PipelineResource 和 Condition 都会被废弃。但是在低版本中还是会继续使用,所以这里会简单介绍一下。

2694c4e35e8e79b0e9db9df669fbc9b2 MD5

如上图所示,一个 Pipeline 是由许多 Task 组成,每个 Task 又由许多 step 组成。在实际工作中,我们可以灵活定义各种 Task,然后根据需要任意组合 Task 形成各类 Pipeline 来完成不同的需求。

# 实现原理

上面大致介绍了 Tekton 的主要 CRD 以及它们所具备的能力,那么,Tekton 是如何把这些 CRD 串联起来的呢?

我们在安装完 Tekton 后,可以看到如下两个 Pod。

# kubectl get po -n tekton-pipelines
NAME                                           READY   STATUS    RESTARTS   AGE
tekton-pipelines-controller-75c456df85-qxvq2   1/1     Running   0          2d22h
tekton-pipelines-webhook-5bc8d6b7c4-w6pdn      1/1     Running   0          2d22h
1
2
3
4

一个是 tekton-pipelines-controller,一个是 tekton-pipelines-webhook。其实从命名方式就可以看出,一个是 tekton 的控制器,用于监听 CRD 对象,一个是 tekton 的网络钩子,用于做 CRD 校验,其中 tekton-pipelines-controller 就是 Tekton 的核心实现 Pod。

tekton-pipelines-controller 在启动的时候会初始化两个 Controller:PipelineRunController 以及 TaskRunController。我们可以通过 main.go(cmd/controller/main.go (opens new window))看到,如下:

	......
   go func() {
		// start the web server on port and accept requests
		log.Printf("Readiness and health check server listening on port %s", port)
		log.Fatal(http.ListenAndServe(":"+port, mux))
	}()

	ctx = filteredinformerfactory.WithSelectors(ctx, v1beta1.ManagedByLabelKey)
	sharedmain.MainWithConfig(ctx, ControllerLogKey, cfg,
		taskrun.NewController(opts, clock.RealClock{}),
		pipelinerun.NewController(opts, clock.RealClock{}),
	)
}
1
2
3
4
5
6
7
8
9
10
11
12
13

如上所示会通过taskrun.NewController和pipelinerun.NewController来进行初始化,然后通过sharedmain.MainWithConfig调用controller.StartAll来启动所有 Controller。

PipelineRunController 通过监听 PipelineRun 对象的变化,然后从 PipelineSpec 中获取 Task 列表并构建成一张有向无环图(DAG),然后通过遍历 DAG 找到可被调度的 Task 节点创建对应的 TaskRun 对象。具体可以通过(pkg/reconciler/pipelinerun/pipelinerun.go)中的reconcile方法进行查看。

TaskRunController 监听到 TaskRun 对象的变化,就会将 TaskRun 中的 Task 转化为 Pod,由 Kubernetes 调度执行。可以通过(pkg/reconciler/taskrun/taskrun.go)中的reconcile方法进行查看。

利用 Kubernetes 的 OwnerReference 机制, PipelineRun Own TaskRun、TaskRun Own Pod、Pod 状态变更时,触发 TaskRun 的 reconcile 逻辑, TaskRun 状态变更时触发 PipelineRun 的 reconcile 逻辑。

df7d8783f635ce128aacdca10746f4cc MD5

图片来自网络

当 TaskRun 的 Pod 变成 running 过后,就会通知第一个 step 容器来执行(通过一个名叫<font style="color:rgb(34, 34, 34);">entrypoint</font>的二进制文件来完成)。

当然这个entrypoint二进制文件也有运行条件的,当且仅当 pipeline 的状态的 annotation 通过

Kubernetes Download Api以文件的方式注入到 step container 后才会启动提供的命令。这句话是不是有点绕?按照官方的说法是:Tekton Pipeline 是通过 Kubernetes Annotation 来跟踪 Pipeline 的状态,而且这些 annotations 会通过Kubernetes Download Api以文件的方式注入到 Step Container 中,Step Container 中的entrypoint会监听着这些文件,当特定的 annotation 以文件的形式注入进来过后,entrypoint才会去执行命令。比方说,一个 Task 中有两个 step,第二个 step 中的entrypoint会等待,直到 annotation 以文件的形式告诉它第一个 step 已经完成。

我们来梳理一下整体的流程,如下:

  1. 用户通过 client 创建 PipelineRun 资源
  2. PipelineRunController 监听到 PipelineRun 资源,就把里面的 Task 组成 DAG(有向无环图),遍历 DAG 得到 Task,并创建 TaskRun
  3. TaskRunController 监听到 TaskRun 资源,就会通过 Kubernetes 将 Task 转化为 Pod 启动(Task 受 Condition 条件控制)
  4. Pod 启动后会运行 Task 中的每一个 Step 完成具体的指令
  5. 运行完成后 Pod 会变成Completed状态,同时也会更新 PipelineRun 的状态

到此一个 Pipeline 就运行完成了。

# PipelineResources

这里将 PipelintResource 提到最前面来说明,主要是后面的操作有需要它的地方。

PipelineResource 用于定义资源的信息,虽然会被弃用,但是在旧版本中依然会使用。

PipelineResource 的定义很简单,如下:

apiVersion: tekton.dev/v1alpha1
kind: PipelineResource
metadata:
  name: hello-word-resource
spec:
  type: git
  params:
    - name: url
      value: https://gitee.com/coolops/springboot-helloworld.git
1
2
3
4
5
6
7
8
9

在 TaskRun 中就可以引用 hello-word-resource 资源得到具体的 git 地址。

# Tasks

Task 就是一个任务模板,Task 的定义中可以包含变量,在真正执行的时候需要给变量赋值。

Task 通过 input.params 定义入参,每一个入参还可以指定默认值,在每一个 step 中可以$(params.A)引用变量。steps 字段表示当前 Task 有哪些步骤组成,每一个 step 都会通过定义一个 container 来执行具体的操作。

Task 主要包括以下元素:

  • Parameters:用于定义 params 参数
  • Resources:定义输入、输出资源,老版本由 PipelineResources 定义,不过在新版本中 PipelineResources 将被弃用
  • Steps:定义具体的操作步骤
  • Workspaces:定义工作区,Task 可以共享工作区
  • Results:定义结果输出,可以用于展示或者给另外的 Task 使用

Task 的定义如下:

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: maven-build
spec:
  resources:
    inputs:
      - name: repo
        type: git
  steps:
    - name: build
      image: maven:3.3-jdk-8
      command:
        - mvn clean package
      workingDir: /workspace/repo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

再定义一个构建 Dokcer 镜像并推送到 Hub 的 Task,如下:

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: build-and-push-image
spec:
  params:
  - name: pathToDockerfile
    type: string
    default: /workspace/repo/Dockerfile
    description: define Dockerfile path
  - name: pathToContext
    type: string
    default: /workspace/repo
    description: 	Docker deamon build context
  - name: imageRepo
    type: string
    default: registry.cn-hangzhou.aliyuncs.com
    description: docker image repo
  resources:
    inputs:
    - name: repo
      type: git
    outputs:
    - name: builtImage
      type: image
  steps:
  - name: build-image
    image: docker:stable
    scripts: |
      #!/usr/bin/env sh
      docker login $(params.imageRepo)
      docker build -t $(resources.outputs.builtImage.url) -f $(params.pathToDockerfile) $(params.pathToContext)
      docker push $(resources.outputs.builtImage.url)
    volumeMounts:
    - name: dockersock
      mountPath: /var/run/docker.sock
  volumes:
  - name: dockersock
    hostPath:
      path: /var/run/docker.sock
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

如上,我们可以通过直接编写 shell 脚本的方式来实现需求,而且使用 docker 构建镜像需要 sock 文件,可以像 pod 挂载那样挂载需要的东西。

step 还有其他的配置,比如为某个 step 设置超时时间,如下:

steps:
  - name: sleep-then-timeout
    image: ubuntu
    script: |
      #!/usr/bin/env bash
      echo "I am supposed to sleep for 60 seconds!"
      sleep 60
    timeout: 5s
1
2
3
4
5
6
7
8

更多的操作可以通过(https://tekton.dev/docs/pipelines/tasks/ (opens new window))进行学习研究。

# TaskRuns

Task 在定义好之后,并不会被执行,就像我们定义了一个函数,如果没被调用的话,这个函数就不会被执行一样。而 TaskRun 就可以就好似调用方,用它来执行 Task 里的具体内容。

TaskRun 会设置 Task 需要的参数,并通过 taskRef 字段来引用 Task,如下:

apiVersion: tekton.dev/v1beta1
kind: TaskRun
metadata:
  name: build-and-push-image
spec:
  params:
    - name: imageRepo
      value: registry.cn-zhangjiakou.aliyuncs.com
  taskRef:
    name: build-and-push-image # 关联定义好的task
  resources:
    inputs:
      - name: repo # 指定输入的仓库资源
        resourceRef:
          name: hello-word-resource
    outputs: # 指定输出的镜像资源
      - name: builtImage
        resourceRef:
          name: hello-word-image
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

通过如上的定义,就将 build-and-push-image 的 Task 进行关联,并且通过 resources 定义 Task 需要的 sources 参数,然后通过 parms 来定义参数,该参数会替代掉 Task 中的默认参数。

在实际中,基本不会去定义 TaskRun,除非自己去测试某个 Task 是否正常。

# Pipelines

一个 TaskRun 只能执行一个 Task,当我们需要同时编排许多 Task 的时候,就需要使用 Pipeline 了,就像使用 Jenkinsfile 来编排不同的任务一样。

Pipeline 是一个编排 Task 的模板,通过 spec.params 来声明执行时需要的入参,通过 spec.tasks 来编排具体的 task,除此之外还可以通过 runAfter 来控制 Task 的先后顺序。

先定义一个简单的 Pipeline,如下:

apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: build-and-push-image
spec:
  resources:
    - name: repo
      type: git
    - name: builtImage
      type: image
  tasks:
    # 构建并推送 Docker 镜像
    - name: build-and-push-image
      taskRef:
        name: build-and-push-image
      resources:
        inputs:
          - name: repo # Task 输入名称
            resource: repo # Pipeline 资源名称
        outputs:
          - name: builtImage
            resource: builtImage
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

上面定义的 Pipeline 关联了build-and-push-image Task,该 Task 所需要的输入输出参数,通过 Pipeline 的spec.resources定义,这里的spec.resources依然依赖 PipelineResources 中定义的具体资源。

上面提到过,如果要在 Pipeline 中控制 Task 顺序,则要使用 runAfter 参数,如下:

- name: test-app
  taskRef:
    name: make-test
  resources:
    inputs:
      - name: workspace
        resource: my-repo
- name: build-app
  taskRef:
    name: kaniko-build
  runAfter:
    - test-app
  resources:
    inputs:
      - name: workspace
        resource: my-repo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

如上 build-app 的 Task 依赖 test-app 的 Task。

除此之外,还可以将上个 Task 的输出作为下一个 Task 的输入,如下。

- name: build-app
  taskRef:
    name: build-push
  resources:
    outputs:
      - name: image
        resource: my-image
- name: deploy-app
  taskRef:
    name: deploy-kubectl
  resources:
    inputs:
      - name: image
        resource: my-image
        from:
          - build-app
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

如上通过from关键字来引入其他 Task 的输出。

如果要在 Pipeline 中使用条件判断,也可以像以下方式使用when关键字。

tasks:
  - name: deploy-to-dev
    when:
      - input: "$(params.branch)"
        operator: in
        values: ["dev"]
    taskRef:
      name: deploy-to-dev
---
tasks:
  - name: deploy-to-test
    when:
      - input: "$(params.branch)"
        operator: in
        values: ["test"]
    taskRef:
      name: deploy-to-test
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

注意:when 和 condition 不能同时在一个 Task 中使用,不然会被认定为无效。

还有一个关键字和 when 效果一样,就是 condition。

condition 的作用就是用一些条件来保护 Task,只有在满足条件的情况下才会运行 Task。在 Task 运行之前,会对所有的条件进行判断,只有全部条件成功,才会运行 Task,否则不会允许。

如下定义一个简单的条件语句。

tasks:
  - name: deploy-if-branch-is-master
    conditions:
      - conditionRef: is-master-branch
        params:
          - name: branch-name
            value: my-value
    taskRef:
      name: deploy
1
2
3
4
5
6
7
8
9

当然条件约束仅针对当前的 Task,如果其他 Task 不受当前 Task 影响,则不受约束。

更多的使用方式见(https://tekton.dev/docs/pipelines/pipelines/ (opens new window))。

# PipelineRuns

Pipeline 和 Task 一样,单纯的定义完并不会运行,Pipeline 需要借助 PipelineRun 来完成真正的执行。

PipelineRun 会自动为 Pipeline 中定义的 Task 创建对应的 TaskRun。

下面定义一个简单的 PipelineRun。

apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
  name: build-and-push-image
spec:
  pipelineRef:
    name: build-and-push-image
  resources:
    - name: repo
      resourceRef:
        name: demo-git
    - name: builtImage
      resourceRef:
        name: harbor-image
1
2
3
4
5
6
7
8
9
10
11
12
13
14

其中spec.pipelineRef用来关联定义的 Pipeline,spec.resources用来给 Pipeline 传递参数。

上面的 repo 和 builtImage 参数依然需要通过PipelineResources定义。不过在新版本,也可以通过resourceSpec来进行定义,如下。

apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
  name: build-and-push-image
spec:
  pipelineRef:
    name: build-and-push-image
  resources:
    - name: repo
      resouorceSpec:
        type: git
        params:
          - name: url
            value: https://gitee.com/coolops/springboot-helloworld.git
    - name: builtImage
      resouorceSpec:
        type: image
        params:
          - name: url
            value: registry.cn-hangzhou.aliyuncs.com/coolops/helloworld:latest
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# Conditions

condition 用于在 Pipeline 中进行条件判断,不过在新版本中会被废弃,使用上面介绍的when替代,这里不再做多的介绍了。

# 鉴权管理

上面介绍了主要的 CRD 以及它们的使用方式,但是还有一种是需要我们关注的,比如代码仓库的密码怎么管理?镜像仓库的密码怎么管理?因为这些都是在实际工作中需要使用的。

Tekton 通过在PipelineRun中指定ServiceAccount来实现。不过 Tekton 要求定义的每个 Secret 都需要指定对应的 annotation。目前支持的 annotation 有以下两种:

  • Git:tekton.dev/git-**<font style="color:rgb(0, 0, 207);">0</font>****<font style="color:rgb(0, 0, 0);">:</font>**<u><font style="color:rgb(248, 248, 248);"> </font></u>https**<font style="color:rgb(0, 0, 0);">:</font>**//github.com
  • Docker:tekton.dev/docker-**<font style="color:rgb(0, 0, 207);">0</font>****<font style="color:rgb(0, 0, 0);">:</font>**<u><font style="color:rgb(248, 248, 248);"> </font></u>https**<font style="color:rgb(0, 0, 0);">:</font>**//gcr.io

目前这两种分别支持以下类型。

Git Docker
kubernetes.io/basic-authkubernetes.io/ssh-auth kubernetes.io/basic-authkubernetes.io/dockercfgkubernetes.io/dockerconfigjson

Tekton 到底是如何使用到这些 secret 的呢?

原来,为了使用这些 Secret,Tekton 在实例化 Pod 的时候就会执行凭证初始化, Tekton 会将具体的 Secret 进行关联并聚合到/tekton/creds 目录中,之后才会执行具体的 Task 步骤。

下面我们具体操作一下,以镜像仓库为例。

(1)创建 secret

apiVersion: v1
kind: Secret
metadata:
  name: docker-registry-secret
  annotations:
    tekton.dev/docker-0: https://gcr.io # Described below
type: kubernetes.io/basic-auth
stringData:
  username: <cleartext username>
  password: <cleartext password>
1
2
3
4
5
6
7
8
9
10

其中tekton.dev/docker-0: [https://gcr.io](https://gcr.io)用来指定对应的仓库地址。

(2)创建 seviceaccount

apiVersion: v1
kind: ServiceAccount
metadata:
  name: docker-registry-sa
secrets:
  - name: docker-registry-secret
1
2
3
4
5
6

(3)在 PipelineRun 中引用

apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
  name: demo-pipeline
  namespace: default
spec:
  serviceAccountName: docker-registry-sa
  pipelineRef:
    name: demo-pipeline
1
2
3
4
5
6
7
8
9

如果需要同时使用多个 serviceaccount 怎么办呢?比如我们在一条完成的 Pipeline 中,在拉取代码的时候会用到 Git 的账户,在推送镜像的时候会用到镜像仓库的账户。

这时候我们就不能用serviceAccountName了,而是需要使用serviceAccountNames。serviceAccountNames是一个 List,可以指定 Task 关联具体的 serviceaccount,如下。

apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
  name: demo-pipeline
  namespace: default
spec:
  serviceAccountNames:
    - taskName: build-app
      serviceAccountName: gitlab-sa
    - taskName: push-image
      serviceAccountName: docker-registry-sa
  pipelineRef:
    name: demo-pipeline
1
2
3
4
5
6
7
8
9
10
11
12
13

到这里基本的资源以及介绍完了,弄懂这篇文章,写一个简单的 Pipeline 应该不成问题,后续的文章会分享具体的实践。

# 参考文档

【1】https://www.infoq.cn/article/aRAYxTo19Bd6AVBmXFQz (opens new window)

【2】https://cloudnative.to/blog/how-tekton-works/ (opens new window)

上次更新: 2025/07/19, 11:33:23
Tekton系列之安装篇
Tekton系列之实践篇-我的第一条Pipeline

← Tekton系列之安装篇 Tekton系列之实践篇-我的第一条Pipeline→

最近更新
01
读《额尔古纳河右岸》有感
07-19
02
读《命运》有感
07-19
03
读《月亮与六便士》有感
07-19
更多文章>
Theme by Vdoing | Copyright © 2019-2025 乔克 | MIT License | 渝ICP备20002153号 |
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式