点击上方蓝字关注我们
Background
尽管我们现在没有把 Tekton 应用到项目中,但是我希望通过这篇文章,让大家有一个对 Tekton 这个工具的使用有个基本概念,希望我下一篇文章是写 Flux 的 CD 让大家对比下这两个工具。
Overview
Tekton 提供很强大而且灵活的开源组件,可以帮助我们去标准化 CI/CD (持续集成和持续发布)工具和流程。并且,它允许我们跨多个云提供商例如亚马逊云/微软云/谷歌云或者本地系统构建,测试和部署。 Tekton 提供的 Pipelines、 Release、Workflows 跟其他现有的工具例如 Jenkins 都可以集成。利用 Tekton ,我们可以实现类似 Rolling、Blue/green、Canary deployment 或者 GitOps 工作流。
Tekton特点
持续交付是现代软件开发的一个关键部分,但是现在这个领域非常分散。 Tekton 项目通过与开源社区跟其他几个主要供应商合作来解决这个问题,建立一个充分利用容器和更广法的云原生生态系统以及跟供应商无关的基础来创建规范和变化的 CI/CD。Tekton 是包含了 Tekton pipeline, Tekton dashboard, Tekton triggers 的一个开源框架,Tekton 的特点如下:
自动化部署
Tekton Trigger 是 Tekton 其中一个开源框架之一,允许我们从 Event 中提取信息来自动化创建 Kubernetes 资源,可以做到类似 GitOps 一样,提交代码到 Git Repo,然后触发部署资源。
在 Kubernetes 内置的最佳实践
Tekton 内置最佳实践是允许快速创建基于云的 CI/CD pipeline。可以快速让我们创建镜像和部署容器,管理 infrastructure 的版本控制或者执行简单的回滚,还可以实现 Rolling,、Blue/green、Canary 的部署。
灵活
Tekton 相比 Flux 来说,更加灵活,他同时兼容在本地环境或者云环境的CI/CD。他可以支持单独一个 Task,也可以支持 Pipeline (管道)的多个 Task 部署,可以让我们选择需要的输入或者输出。
使用容器作为构建块
在 Jenkins 里面,我们如果要构建基于 nodejs 或者 JAVA 的应用的时候,我们需要在基于 VM 的或者基于容器 Jenkins Slave 里面安装对应的应用程序去支持构建,而在 Tekton 里面,是基于 Container 来作为构建块,我们可以打一个 common 或者基于某个语言或者应用的 Container,来传递给 Tekton 去构建或者部署我们的应用。
支持跨平台
Tekton 支持让我们跨平台去部署我们的应用,包括 VM, Kubernetes 去执行构建、测试和部署。并且我们可以使用 Tekton 管道跨多个云提供商或者混合环境进行部署。
Concept
Task
Task 其实简单来说呢是一个构建任务,包含了一系列的 steps,就 GoCD 里面的一个 Job 一样。Tekton Pipeline 的主要是运行单独一个Task任务,或者作为Pipeline 的一部分去运行。每个 step 在 Kubernetes 集群中是由一个 Pod 运行。
TaskRun
TaskRun 是运行我们定义的 Task,我们可以手动创建一个 taskRun,当 taskRun被创建之后,就会触发 Task 中的构建任务。下面是一个简单例子
apiVersion: tekton.dev/v1alpha1
kind: TaskRun
metadata:
name: run-our-ut-task
spec:
taskRef:
name: ci-ut
我们可以使用 Tekton 的 CLI 去查看我们 TaskRun
执行输出
tkn taskrun describe run-our-ut-task
查看实际结果,可以使用以下命令
tkn taskrun logs run-our-ut-task
Pipeline Resource
在很多场景下,我们都需要更多的输入跟输出的流程,例如我们的 Task 需要从我们的 Git 里面拉去代码并且构建 Image。PipelineResource 就是用来定义 Artifacts 去传递给 Task 输入或者输出。
Pipeline Resource 支持的类型有 Git、Image。通常情况下我们会使用 Git Repo 的代码作为 Input,然后构建生成的 tar或者 image 来作为 output
apiVersion: tekton.dev/v1alpha1
kind: PipelineResource
metadata:
name: fetch-code
spec:
type: git
params:
- name: revision
value: master
- name: url
value: https://xx.gerrit.xx/project-name/component-name
Pipeline
Pipeline 类似 GoCD 里面的 Template, 在 GoCD 里面一个 Template 会有多个 Job,在 Tekton 里面一个 Pipeline 会有一个或者多个 Task。
Pipeline 通过 runAfter 参数来定义了一系列的 Task 的执行顺序,也有 condition 的参数让 Task 在满足条件后运行。
PipelineRun
同理,PipelineRun 就是去运行我们的 Pipeline 的。这里不细说。
实践
Tekton 允许你跨多个环境例如 VM,Kubernetes 构建、测试和部署,还可以跨多个云提供商或者混合环境进行部署。
安装
安装比较简单,使用 kubectl apply
命令即可把 Tekton pipelines 跟对应的依赖包安装好即可。
kubectl apply --filename https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml
部署 Tekton 之后,我们可以看见 Tekton Pipeline 是由一个 controller 控制器来监听 CRD 并且执行逻辑,webhook 是用来检验创建的 CRD 资源。

CI
Jenkins 的确很强,但是 Jenkins 并不是设计为了给容器环境去构建的,并且开销很大,不适合微服务团队结构等。这里我想使用 Tekton 来给大家看看怎么使用它来做 CI。
先定义一个 ServiceAccount 去解决代码仓库以及镜像仓库的权限问题。
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: tekton-account
namespace: ci-test
secrets:
- name: fetch-git
- name: docker-registry
---
apiVersion: v1
kind: Secret
metadata:
name: fetch-git
namespace: ci-test
annotations:
xx: xx
type: kubernetes.io/ssh-auth
data:
ssh-privatekey: xx...
known_hosts: xx...
---
apiVersion: v1
kind: Secret
metadata:
name: docker-registry
namespace: ci-test
annotations:
tekton.dev/docker-0: https://xx.artifactory.com
type: kubernetes.io/basic-auth
stringData:
username: <non-encoded>
password: <non-encoded>
从 Git 里面拉取代码,下例是使用了 revision 获取了 master 的代码,同样可以指定 tag 或者 commit
---
apiVersion: Tekton.dev/v1alpha1
kind: PipelineResource
metadata:
name: git-source
spec:
type: git
params:
- name: url
value: https://xx.gerrit.com/project/test.git
- name: revision
value: master
拉取代码后,我们需要执行 maven 编译打包以及构建镜像,我们通过 input 来把刚刚定义的拉取代码资源作为输入传递给 Task,这里有三个步骤( steps ),第一个步骤是执行 maven 编译,第二个步骤是打成镜像,第三个步骤则是推送至镜像仓库
apiVersion: tekton.dev/v1alpha1
kind: Task
metadata:
name: build-image
spec:
inputs:
resources:
- name: git-source
type: git
params:
- name: component-imagenametag
description: The component image name to build
default: application-mgmt:1.0.0
serviceAccount: tekton-account
outputs:
resources:
- name: output-buildimage
type: image
steps:
- name: maven-install
image: maven:3.5.0-jdk-8-alpine
workingDir: "${inputs.params.directory}"
args:
[
"mvn",
"clean",
"install",
"-D maven.test.skip=true",
]
volumeMounts:
- name: m2
mountPath: /root/.m2
- name: build-component-image
image: xx-artifactory.xx/build-image:1.0.0
command:
- docker
args:
- build . -t
- $(inputs.params.component-imagenametag)
- $(outputs.resources.output-buildimage.url)
- name: promote-component-image
image: xx-artifactory.xx/build-image:1.0.0
command:
- docker
args:
- push $(outputs.resources.output-buildimage.url)/$(inputs.params.component-imagenametag)
如果要做到我们现在的 image name tag 的话,首先要在容器里面运行 sed ,然后用镜像更新部署 YAML 文件,YAML 文件必须在更新的时候有一个 __IMAGE__
字符串。然后需要借助 lAchlan Evenson 的流行的 k8s-kubectl 容器来运行 kubectl, 去实现更新集群中提取 commit id 来作为 image name tag,如下代码所示
set-e
if[ $# -lt 4 ]; then
echo "Usage: <resources> <container> <docker_url> <source_code_path>"
exit 1
fi
resource=$1
container=$2
docker_url=$3
source_code_path=$4
commitid=$(cd $source_code_path && git rev-parse HEAD)
kubectl set image deployment $resource $container=$docker_url:$commitid
然后我们开始定义一个 TaskRun 去执行我们的构建
apiVersion: Tekton.dev/v1alpha1
kind: TaskRun
metadata:
generateName: whatever-name
spec:
inputs:
resources:
- name: gitssh
resourceRef:
name: git-source
taskRef:
name: build-image
至此为止,构建镜像以及推镜到仓库的流程已经完成。
CD
在 Kubernetes 中,我们都知道如果要发布应用到编排器中,我们可以使用 Helm 也可以直接使用 manifest 的方法将应用部署。现在我们把我们的 manifest 文件放入到 Git Repo,假设这个 Git Repo 是叫 project-manifest/deploy-app,然后 example 的 manifest 如下:
apiVersion: v1
kind: Service
metadata:
name: app
labels:
app: app
spec:
type: NodePort
ports:
- port: 3300
name: app
nodePort: 32426
selector:
app: app
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
labels:
app: app
spec:
replicas: 1
selector:
matchLabels:
app: app
template:
metadata:
labels:
app: app
spec:
containers:
- name: app
image: IMAGE
ports:
- containerPort: 3300
然后我们定义PipelineResource是指定到我们存储manifest的Git Reo中
apiVersion: tekton.dev/v1alpha1
kind: PipelineResource
metadata:
name: git-source
spec:
type: git
params:
- name: revision
value: master
- name: url
value: https://xx.gerrit.com/project-manifest/deploy-app
task 如下
apiVersion: tekton.dev/v1alpha1
kind: Task
metadata:
name: deploy-application
spec:
inputs:
resources:
- name: git-source
type: git
params:
- name: app-deploy-file-name
default: app-deploy.yaml
steps:
- name: deploy-app
image: kabanero/kabanero-utils
command: ['/bin/sh']
args: ['-c', 'cd /workspace/$gitsource && kubectl apply -f $(inputs.params.app-deploy-file-name)']
env:
- name: gitsource
value: git-source
在把上面的 deploy-application
的 Task 部署之前,我们需要配置 tekton-account 获取 tekton-ns
的管理权限,用来部署应用
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: tekton-cluster-admin
subjects:
- kind: ServiceAccount
name: tekton-account
namespace: tekton-ns
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
接下来我们创建一个部署应用的 Pipeline:
apiVersion: tekton.dev/v1alpha1
kind: Pipeline
metadata:
name: application-pipeline
spec:
resources:
- name: git-source
type: git
params:
- name: pathToContext
description: The path to the build context, used byKaniko- within the workspace
default: .
- name: pathToYamlFile
description: The path to the yaml file to deploy within the git source
default: config.yaml
- name: imageUrl
description: Url of image repository
default: deploy_target
- name: imageTag
description: Tag to apply to the built image
default: latest
tasks:
# 这里build-image就是我们CI上定义的task
- name: build-image
params:
- name: pathToContext
value: "$(params.pathToContext)"
- name: imageUrl
value: "$(params.imageUrl)"
- name: imageTag
value: "$(params.imageTag)"
resources:
inputs:
- name: git-source
resource: git-source
- name: deploy-application
taskRef:
name: deploy-application
runAfter:
- build-image
params:
- name: pathToContext
value: "$(params.pathToContext)"
- name: pathToYamlFile
value: "$(params.pathToYamlFile)"
- name: imageUrl
value: "$(params.imageUrl)"
- name: imageTag
value: "$(params.imageTag)"
resources:
inputs:
- name: git-source
resource: git-source
最后我们定义一个 PipelineRun 去运行上面定义的 Pipeline:
apiVersion: tekton.dev/v1alpha1
kind: PipelineRun
metadata:
name: application-pipeline-run
spec:
pipelineRef:
name: application-pipeline
resources:
- name: git-source
resourceRef:
name: git
params:
- name: pathToContext
value: "src"
- name: pathToYamlFile
value: "deploy.yaml"
- name: "imageUrl"
value: "IMAGE_URL"
- name: "imageTag"
value: "IMAGE_TAG"
serviceAccount: tekton-account
现在我们使用 kubectl apply-f
把我们定义的 Pipeline 以及 Pipelinerun 部署至 Kubernetes 上。
触发
上面我们说了这么多,都是要手工的使用 kubectl apply-f
然后把我们定义的 PipelineRun/TaskRun 部署到 Kubernetes 上,这样明显是不可以满足我们自动化CI/CD的。而 Tekton 提供了一个叫 Triggers 的框架可以让我们做到从触发事件中提取信息去创建 Kubernetes 资源。
Tekton API 允许我们将功能从配置中分离除了包括 Pipeline、PipelineRuns,这些步骤可以重用,Tekton Trigger 提供了一种生成动态封装这些配置。基本的 Tekton Trigger 组件这里简单说一下:
TriggerTemplate: 定义了要创建的模板资源,例如创建要使用他们的PipelineRun。
TriggerBinding: 验证事件并且提取有效字段。
EventListener: 将 TriggerBindings 连接到 TriggerTemplates 并提供可寻址端点,这个是 webhook /事件定向的地方。
Tekton Trigger 可以让我们做到类似 GitOps 的工作流,以下代码是来自 Tekton Trigger 的官网,当我们提交代码到我们的 Git Repo 里面就可以看到我们定义的 Pipeline/Task 自动部署到 Kubernetes 里面。(除了支持 Github 同样也支持 GitLab 去做类似的事情)
安装配置略,此处我只想重点说一下如何配置 Event 实现提交代码到 Git repo 就可以触发 PipelineRun,需要替换变量才能使用。
首先会使用一个 EventListener
去接收 Push 事件
然后定义 TriggerTemplate
会为 template 的 PipelineRun 来接收事件
apiVersion: tekton.dev/v1alpha1
kind: TriggerTemplate
metadata:
name: tekton-terigger-triggertemplate
namespace: tekton-terigger
spec:
params:
- name: gitrevision
description: The git revision
default: master
- name: gitrepositoryurl
description: The git repository url
- name: namespace
description: Thenamespace to create the resources
resourcetemplates:
- apiVersion: tekton.dev/v1alpha1
kind: PipelineRun
metadata:
name: tekton-terigger-pipeline-run-$(uid)
namespace: $(params.namespace)
spec:
serviceAccountName: tekton-triggers-admin
pipelineRef:
name: tekton-terigger-pipeline
resources:
- name: source-repo
resourceSpec:
type: git
params:
- name: revision
value: $(params.gitrevision)
- name: url
value: $(params.gitrepositoryurl)
- name: image-source
resourceSpec:
type: image
params:
- name: url
value: DOCKERREPO-REPLACEME # docker-repo-location.com/repo:tekton-terigger
- name: event-to-sink
resourceSpec:
type: cloudEvent
params:
- name: targetURI
value: http://event-display.tekton-terigger.svc.cluster.local
---
apiVersion: tekton.dev/v1alpha1
kind: TriggerBinding
metadata:
name: tekton-terigger-pipelinebinding
namespace: tekton-terigger
spec:
params:
- name: gitrevision
value: $(body.head_commit.id)
- name: namespace
value: tekton-terigger
- name: gitrepositoryurl
value: "https://github.com/$(body.repository.full_name)"
---
apiVersion: tekton.dev/v1alpha1
kind: EventListener
metadata:
name: tekton-terigger-listener
namespace: tekton-terigger
spec:
serviceAccountName: tekton-triggers-admin
triggers:
- bindings:
- name: tekton-terigger-pipelinebinding
template:
name: tekton-terigger-triggertemplate
Tekton Dashboard
这里快速说一下 Tekton dashboard,这个是 Tekton 提供的可视化 dashboard,可以查看 PipelineRun 的日志或者执行结果。
第一步,安装
kubectl apply --filename https://github.com/tektoncd/dashboard/releases/download/v0.4.1/dashboard_latest_release.yaml
第二步,创建用于 Demo 的 task
apiVersion: tekton.dev/v1alpha1
kind: Task
metadata:
name: echo-hello-world
spec:
steps:
- name: echo
image: ubuntu
command:
- echo
args:
- "hello world"
创建用于 Demo 的 pipeline
apiVersion: tekton.dev/v1alpha1
kind: Pipeline
metadata:
name: tutorial-pipeline
spec:
tasks:
- name: echo-hello-world-n
taskRef:
name: echo-hello-world
浏览器访问 Tekton dashboard,创建 PipelineRun 以及查看这次 PipelineRun 的执行结果


查看 TaskRun 的执行结果

后话
(我本来想说,Tekton 已经得到了 Google, IBM 跟 RedHat 的支持。)
好像不止一个人跟我讲想用 GitLab 代替 Jenkins 成为我们的 CI 呢,不管是 GitLab 还是 Jenkins,其实我觉得都不适合,因为他们都不是以 K8S 为依托的, Kubernetes 已经是必然,而选择 CRD+operator 的 tekton 必然成为新一代 CICD 的标准,尽管现在还不算很成熟,但是我很看好 tekton.
Tekton & Flux
我放了一个链接在这里,就是 Tekton 跟 Flux 集成的消息,有兴趣的同学可以订阅下这个消息。
https://github.com/tektoncd/pipeline/issues/1259
Reference
https://cd.foundation/
https://tekton.dev/
https://github.com/tektoncd/pipeline
https://github.com/tektoncd/triggers
https://github.com/tektoncd/dashboard




