Tekton 是一款功能非常强大而灵活的 CI/CD 开源的云原生框架。Tekton 的前身是 Knative 项目的 build-pipeline 项目,这个项目是为了给 build 模块增加 pipeline 的功能,但是随着不同的功能加入到 Knative build 模块中,build 模块越来越变得像一个通用的 CI/CD 系统,于是,索性将 build-pipeline 剥离出 Knative,就变成了现在的 Tekton,而 Tekton 也从此致力于提供全功能、标准化的云原生 CI/CD 解决方案。

安装

安装 Tekton 非常简单,可以直接通过 tektoncd/pipeline 的 GitHub 仓库中的 release.yaml 文件进行安装,如下所示的命令:

$ kubectl apply -f https://github.com/tektoncd/pipeline/releases/download/v0.12.0/release.yaml

由于官方使用的镜像是 gcr 的镜像,所以正常情况下我们是获取不到的,如果你的集群由于某些原因获取不到镜像,可以使用下面的资源清单文件,我已经将镜像替换成了 Docker Hub 上面的镜像:

$ kubectl apply -f https://www.qikqiak.com/k8strain/devops/manifests/tekton/release.yaml

上面的资源清单文件安装后,会创建一个名为 tekton-pipelines 的命名空间,在该命名空间下面会有大量和 tekton 相关的资源对象,我们可以通过在该命名空间中查看 Pod 并确保它们处于 Running 状态来检查安装是否成功:

$ kubectl get pods -n tekton-pipelines
NAME READY STATUS RESTARTS AGE
tekton-pipelines-controller-67f4dc98d8-pgxrq 1/1 Running 0 9m46s
tekton-pipelines-webhook-59df55445c-jw76v 1/1 Running 0 9m45s

Tekton 安装完成后,我们还可以选择是否安装 CLI 工具,有时候可能 Tekton 提供的命令行工具比 kubectl 管理这些资源更加方便,当然这并不是强制的,我这里是 Mac 系统,所以可以使用常用的 Homebrew 工具来安装:

$ brew tap tektoncd/tools
$ brew install tektoncd/tools/tektoncd-cli

安装完成后可以通过如下命令验证 CLI 是否安装成功:

$ tkn version
Client version: 0.9.0
Pipeline version: v0.12.0

此外,还可以安装一个 Tekton 提供的一个 Dashboard,我们可以通过 Dashboard 查看 Tekton 整个任务的构建过程,直接执行下面的命令直接安装即可:

$ kubectl apply -f kubectl apply -f https://www.qikqiak.com/k8strain/devops/manifests/tekton/dashboard.yaml

安装完成后我们可以通过 Dashboard 的 Service 的 NodePort 来访问应用。

概念

Tekton 为 Kubernetes 提供了多种 CRD 资源对象,可用于定义我们的流水线。

主要有以下几个资源对象:

  • Task:表示执行命令的一系列步骤,task 里可以定义一系列的 steps,例如编译代码、构建镜像、推送镜像等,每个 step 实际由一个 Pod 执行。
  • ClusterTask:覆盖整个集群的任务,而不是单一的某一个命名空间,这是和 Task 最大的区别,其他基本上一致的。
  • TaskRun:task 只是定义了一个模版,taskRun 才真正代表了一次实际的运行,当然你也可以自己手动创建一个 taskRun,taskRun 创建出来之后,就会自动触发 task 描述的构建任务。
  • Pipeline:一组任务,表示一个或多个 Task、PipelineResource 以及各种定义参数的集合。
  • PipelineRun:类似 task 和 taskRun 的关系,pipelineRun 也表示某一次实际运行的 pipeline,下发一个 pipelineRun CRD 实例到 Kubernetes 后,同样也会触发一次 pipeline 的构建。
  • PipelineResource:表示 pipeline 输入资源,比如 github 上的源码,或者 pipeline 输出资源,例如一个容器镜像或者构建生成的 jar 包等。

示例

在这里我们使用一个简单的 Golang 应用,可以在仓库 https://github.com/cnych/tekton-demo 下面获取应用程序代码,测试以及 Dockerfile 文件。

首先第一个任务就是 Clone 应用程序代码进行测试,创建一个 task-test.yaml 的资源文件,内容如下所示:

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: test
spec:
resources:
inputs:
- name: repo
type: git
steps:
- name: run-test
image: golang:1.14-alpine
workingDir: /workspace/repo
command: ["go"]
args: ["test"]

其中 resources 定义了我们的任务中定义的步骤所需的输入内容,这里我们的步骤需要 Clone 一个 Git 仓库作为 go test 命令的输入,目前支持 git、pullRequest、image、cluster、storage、cloudevent 等资源。

Tekton 内置的 git 资源类型,它会自动将代码仓库 Clone 到 /workspace/$input_name 目录中,由于我们这里输入被命名成 repo,所以代码会被 Clone 到 /workspace/repo 目录下面。

然后下面的 steps 就是来定义执行运行测试命令的步骤,这里我们直接在代码的根目录中运行 go test 命令即可,需要注意的是命令和参数需要分别定义。

定义完成后直接使用 kubectl 创建该任务:

$ kubectl apply -f task-test.yaml
task.tekton.dev/test created

现在我们定义完成了一个建的 Task 任务,但是该任务并不会立即执行,我们必须创建一个 TaskRun 引用它并提供所有必需输入的数据才行。这里我们就需要将 git 代码库作为输入,我们必须先创建一个 PipelineResource 对象来定义输入信息,创建一个名为 pipelineresource.yaml 的资源清单文件,内容如下所示:

apiVersion: tekton.dev/v1alpha1
kind: PipelineResource
metadata:
name: cnych-tekton-example
spec:
type: git
params:
- name: url
value: https://github.com/cnych/tekton-demo
- name: revision
value: master

直接创建上面的资源对象即可:

$ kubectl apply -f pipelineresource.yaml
pipelineresource.tekton.dev/cnych-tekton-example created

接下来我们就创建 TaskRun 对象了,创建一个名为 taskrun.yaml 的文件,内容如下所示:

apiVersion: tekton.dev/v1beta1
kind: TaskRun
metadata:
name: testrun
spec:
taskRef:
name: test
resources:
inputs:
- name: repo
resourceRef:
name: cnych-tekton-example

这里通过 taskRef 引用上面定义的 Task 和 git 仓库作为输入,resourceRef 也是引用上面定义的 PipelineResource 资源对象。现在我们创建这个资源对象过后,就会开始运行了:

$ kubectl apply -f taskrun.yaml
taskrun.tekton.dev/testrun created

创建后,我们可以通过查看 TaskRun 资源对象的状态来查看构建状态:

$ kubectl get taskrun
NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME
testrun Unknown Pending 21s
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
testrun-pod-mw9bt 0/2 Init:1/2 0 59s
$ kubectl describe pod testrun-pod-mw9bt
......
node.kubernetes.io/unreachable:NoExecute for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled <unknown> default-scheduler Successfully assigned default/testrun-pod-mw9bt to ydzs-node5
Normal Pulling 4m20s kubelet, ydzs-node5 Pulling image "busybox@sha256:a2490cec4484ee6c1068ba3a05f89934010c85242f736280b35343483b2264b6"
Normal Pulled 4m13s kubelet, ydzs-node5 Successfully pulled image "busybox@sha256:a2490cec4484ee6c1068ba3a05f89934010c85242f736280b35343483b2264b6"
Normal Created 4m13s kubelet, ydzs-node5 Created container working-dir-initializer
Normal Started 4m13s kubelet, ydzs-node5 Started container working-dir-initializer
Normal Pulling 4m12s kubelet, ydzs-node5 Pulling image "cnych/tekton-entrypoint:v0.12.0"
Normal Pulled 2m13s kubelet, ydzs-node5 Successfully pulled image "cnych/tekton-entrypoint:v0.12.0"
Normal Created 2m13s kubelet, ydzs-node5 Created container place-tools
Normal Started 2m12s kubelet, ydzs-node5 Started container place-tools
Normal Pulling 2m12s kubelet, ydzs-node5 Pulling image "cnych/tekton-git-init:v0.12.0"
Normal Pulled 33s kubelet, ydzs-node5 Successfully pulled image "cnych/tekton-git-init:v0.12.0"
Normal Created 32s kubelet, ydzs-node5 Created container step-git-source-cnych-tekton-example-d6mcz
Normal Started 32s kubelet, ydzs-node5 Started container step-git-source-cnych-tekton-example-d6mcz
Normal Pulling 32s kubelet, ydzs-node5 Pulling image "golang:1.14-alpine"

我们可以通过 kubectl describe 命令来查看任务运行的过程,首先就是通过 initContainer 中的一个 busybox 镜像将代码 Clone 下来,然后使用任务中定义的镜像来执行命令。

当任务执行完成后, Pod 就会变成 Completed 状态了:

$ kubectl get pods
NAME READY STATUS RESTARTS AGE
testrun-pod-mw9bt 0/2 Completed 0 4m27s
$ kubectl get taskrun
NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME
testrun True Succeeded 70s 57s

我们可以查看容器的日志信息来了解任务的执行结果信息:

$ kubectl logs testrun-pod-mw9bt --all-containers
{"level":"info","ts":1588751895.779908,"caller":"git/git.go:136","msg":"Successfully cloned https://github.com/cnych/tekton-demo @ f840e0c390be9a1a6edad76abbde64e882047f05 (grafted, HEAD, origin/master) in path /workspace/repo"}
{"level":"info","ts":1588751895.8844209,"caller":"git/git.go:177","msg":"Successfully initialized and updated submodules in path /workspace/repo"}
PASS
ok _/workspace/repo 0.006s

我们可以看到我们的测试已经通过了,当然我们也可以使用 Tekton CLI 工具来运行这个任务。Tekton CLI 提供了一种更快、更方便的方式来运行任务。

我们不用手动编写 TaskRun 资源清单,可以运行以下命令来执行,该命令引用我们的任务(名为 test),生成一个 TaskRun 资源对象并显示其日志:

$ tkn task start test --inputresource repo=cnych-tekton-example --showlog
Taskrun started: test-run-z56lj
Waiting for logs to be available...
[git-source-cnych-tekton-example-8q268] {"level":"info","ts":1588752677.350016,"caller":"git/git.go:136","msg":"Successfully cloned https://github.com/cnych/tekton-demo @ f840e0c390be9a1a6edad76abbde64e882047f05 (grafted, HEAD, origin/master) in path /workspace/repo"}
[git-source-cnych-tekton-example-8q268] {"level":"info","ts":1588752677.453426,"caller":"git/git.go:177","msg":"Successfully initialized and updated submodules in path /workspace/repo"} [run-test] PASS
[run-test] ok _/workspace/repo 0.007s

Docker Hub 配置

为了能够构建 Docker 镜像,一般来说我们需要使用 Docker 来进行,我们这里是容器,所以可以使用 Docker In Docker 模式,但是这种模式安全性不高,除了这种方式之外,我们还可以使用 Google 推出的 Kaniko 工具来进行构建,该工具可以在 Kubernetes 集群中构建 Docker 镜像而无需依赖 Docker 守护进程。

使用 Kaniko 构建镜像和 Docker 命令基本上一致,所以我们可以提前设置下 Docker Hub 的登录凭证,方便后续将镜像推送到镜像仓库。登录凭证可以保存到 Kubernetes 的 Secret 资源对象中,创建一个名为 secret.yaml 的文件,内容如下所示:

apiVersion: v1
kind: Secret
metadata:
name: docker-auth
annotations:
tekton.dev/docker-0: https://index.docker.io/v1/
type: kubernetes.io/basic-auth
stringData:
username: myusername
password: mypassword

记得将 myusername 和 mypassword 替换成你的 Docker Hub 登录凭证。

我们这里在 Secret 对象中添加了一个 tekton.dev/docker-0 的 annotation,该注解信息是用来告诉 Tekton 这些认证信息所属的 Docker 镜像仓库。

然后创建一个 ServiceAccount 对象来使用上面的 docker-auth 这个 Secret 对象,创建一个名为 serviceaccount.yaml 的文件,内容如下所示:

apiVersion: v1
kind: ServiceAccount
metadata:
name: build-sa
secrets:
- name: docker-auth

然后直接创建上面两个资源对象即可:

$ kubectl apply -f secret.yaml
secret/docker-auth created
$ kubectl apply -f serviceaccount.yaml
serviceaccount/build-sa created

创建完成后,我们就可以在运行 Tekton 的任务或者流水线的时候使用上面的 build-sa 这个 ServiceAccount 对象来进行 Docker Hub 的登录认证了。

创建镜像任务

现在我们创建一个 Task 任务来构建并推送 Docker 镜像,我们这里使用的示例应用根目录下面已经包含了一个 Dockerfile 文件了,所以我们直接 Clone 代码就可以获得:

FROM golang:1.14-alpine

WORKDIR /go/src/app
COPY . . RUN go get -d -v ./...
RUN go install -v ./... CMD ["app"]

创建一个名为 task-build-push.yaml 的文件,文件内容如下所示:

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: build-and-push
spec:
resources:
inputs:
- name: repo
type: git
steps:
- name: build-and-push
image: cnych/kaniko-executor:v0.22.0
env:
- name: DOCKER_CONFIG
value: /tekton/home/.docker
command:
- /kaniko/executor
- --dockerfile=Dockerfile
- --context=/workspace/repo
- --destination=cnych/tekton-test:latest

和前面的测试任务类似,这里我们同样将 git 作为输入,也只定义了一个名为 build-and-push 的步骤,Kaniko 会在同一个命令中构建并推送,所以不需要多个步骤了,执行的命令就是 /kaniko/executor,通过 --dockerfile 指定 Dockerfile 路径,--context 指定构建上下文,我们这里当然就是项目的根目录了,然后 --destination 参数指定最终我们的镜像名称。其实 Tekton 也支持参数化的形式,这样就可以避免我们这里直接写死了,不过由于我们都还是初学者,为了避免复杂,我们直接就直接硬编码了。

此外我们定义了一个名为 DOCKER_CONFIG 的环境变量,这个变量是用于 Kaniko 去查找 Docker 认证信息的。

同样直接创建上面的资源对象即可:

$ kubectl apply -f task-build-push.yaml

task.tekton.dev/build-and-push created

创建了 Task 任务过后,要想真正去执行这个任务,我们就需要创建一个对应的 TaskRun 资源对象。

执行任务

和前面一样,现在我们来创建一个 TaskRun 对象来触发任务,不同之处在于我们需要指定 Task 时需要的 ServiceAccount 对象。创建一个名为 taskrun-build-push.yaml 的文件,内容如下所示:

apiVersion: tekton.dev/v1beta1
kind: TaskRun
metadata:
name: build-and-push
spec:
serviceAccountName: build-sa
taskRef:
name: build-and-push
resources:
inputs:
- name: repo
resourceRef:
name: cnych-tekton-example

注意这里我们通过 serviceAccountName 属性指定了 Docker 认证信息的 ServiceAccount 对象,然后通过 taskRef 引用我们的任务,以及下面的 resourceRef 关联第一部分我们声明的输入资源。

然后直接创建这个资源对象即可:

$ kubectl apply -f taskrun-build-push.yaml
taskrun.tekton.dev/build-and-push created

创建完成后就会触发任务执行了,我们可以通过查看 Pod 对象状态来了解进度:

$ kubectl get pods
NAME READY STATUS RESTARTS AGE
build-and-push-pod-pv6mv 0/2 Init:0/2 0 32s
$ kubectl get taskrun
NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME
build-and-push Unknown Pending 108s

现在任务执行的 Pod 还在初始化容器阶段,我们可以看到 TaskRun 的状态处于 Pending,隔一会儿正常构建就会成功了,我们可以查看构建任务的 Pod 日志信息:

$ kubectl get pods
NAME READY STATUS RESTARTS AGE
build-and-push-pod-pv6mv 0/2 Completed 0 16m
$ kubectl logs -f build-and-push-pod-pv6mv --all-containers
{"level":"info","ts":1588907241.1187525,"caller":"creds-init/main.go:44","msg":"Credentials initialized."}
{"level":"info","ts":1588907420.917656,"caller":"git/git.go:136","msg":"Successfully cloned https://github.com/cnych/tekton-demo @ f840e0c390be9a1a6edad76abbde64e882047f05 (grafted, HEAD, origin/master) in path /workspace/repo"}
{"level":"info","ts":1588907421.0128217,"caller":"git/git.go:177","msg":"Successfully initialized and updated submodules in path /workspace/repo"}
INFO[0010] Retrieving image manifest golang:1.14-alpine
INFO[0016] Retrieving image manifest golang:1.14-alpine
INFO[0020] Built cross stage deps: map[]
INFO[0020] Retrieving image manifest golang:1.14-alpine
INFO[0023] Retrieving image manifest golang:1.14-alpine
INFO[0029] Executing 0 build triggers
INFO[0029] Unpacking rootfs as cmd COPY . . requires it.
INFO[0259] WORKDIR /go/src/app
INFO[0259] cmd: workdir
INFO[0259] Changed working directory to /go/src/app
INFO[0259] Creating directory /go/src/app
INFO[0259] Resolving 1 paths
INFO[0259] Taking snapshot of files...
INFO[0259] COPY . .
INFO[0259] Resolving 56 paths
INFO[0259] Taking snapshot of files...
INFO[0259] RUN go get -d -v ./...
INFO[0259] Taking snapshot of full filesystem...
INFO[0265] Resolving 11667 paths
INFO[0272] cmd: /bin/sh
INFO[0272] args: [-c go get -d -v ./...]
INFO[0272] Running: [/bin/sh -c go get -d -v ./...]
INFO[0272] Taking snapshot of full filesystem...
INFO[0275] Resolving 11667 paths
INFO[0280] RUN go install -v ./...
INFO[0280] cmd: /bin/sh
INFO[0280] args: [-c go install -v ./...]
INFO[0280] Running: [/bin/sh -c go install -v ./...]
app
INFO[0281] Taking snapshot of full filesystem...
INFO[0284] Resolving 11666 paths
INFO[0288] CMD ["app"]
$ kubectl get taskrun
NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME
build-and-push True Succeeded 15m 2m24s

我们可以看到 TaskRun 任务已经执行成功了。 这个时候其实我们可以在 Docker Hub 上找到我们的镜像了,当然也可以直接使用这个镜像进行测试:

创建流水线

到这里前面我们的两个任务 test 和 build-and-push 都已经完成了,我们还可以创建一个流水线来将这两个任务组织起来,首先运行 test 任务,如果通过了再执行后面的 build-and-push 这个任务。

创建一个名为 pipeline.yaml 的文件,内容如下所示:

apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: test-build-push
spec:
resources:
- name: repo
type: git
tasks:
# 运行应用测试
- name: test
taskRef:
name: test
resources:
inputs:
- name: repo # Task 输入名称
resource: repo # Pipeline 资源名称
# 构建并推送 Docker 镜像
- name: build-and-push
taskRef:
name: build-and-push
runAfter:
- test # 测试任务执行之后
resources:
inputs:
- name: repo # Task 输入名称
resource: repo # Pipeline 资源名称

首先我们需要定义流水线需要哪些资源,可以是输入或者输出的资源,在这里我们只有一个输入,那就是命名为 repo 的应用程序源码的 GitHub 仓库。接下来定义任务,每个任务都通过 taskRef 进行引用,并传递任务需要的输入参数。

同样直接创建这个资源对象即可:

$ kubectl apply -f pipeline.yaml
pipeline.tekton.dev/test-build-push created

前面我们提到过和通过创建 TaskRun 去触发 Task 任务类似,我们可以通过创建一个 PipelineRun 对象来运行流水线。这里我们创建一个名为 pipelinerun.yaml 的 PipelineRun 对象来运行流水线,文件内容如下所示:

apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
name: test-build-push-run
spec:
serviceAccountName: build-sa
pipelineRef:
name: test-build-push
resources:
- name: repo
resourceRef:
name: cnych-tekton-example

定义方式和 TaskRun 几乎一样,通过 serviceAccountName 属性指定 ServiceAccount 对象,pipelineRef 关联流水线对象。同样直接创建这个资源,创建后就会触发我们的流水线任务了:

$ kubectl apply -f pipelinerun.yaml
pipelinerun.tekton.dev/test-build-push-run created
$ kubectl get pods | grep test-build-push-run
test-build-push-run-build-and-push-xl7wp-pod-hdnbl 0/2 Completed 0 5m27s
test-build-push-run-test-4s6qh-pod-tkwzk 0/2 Completed 0 6m5s
$ kubectl logs -f test-build-push-run-build-and-push-xl7wp-pod-hdnbl --all-containers
{"level":"info","ts":1588908934.442572,"caller":"git/git.go:136","msg":"Successfully cloned https://github.com/cnych/tekton-demo @ f840e0c390be9a1a6edad76abbde64e882047f05 (grafted, HEAD, origin/master) in path /workspace/repo"}
{"level":"info","ts":1588908934.577377,"caller":"git/git.go:177","msg":"Successfully initialized and updated submodules in path /workspace/repo"}
{"level":"info","ts":1588908927.469531,"caller":"creds-init/main.go:44","msg":"Credentials initialized."}
INFO[0004] Retrieving image manifest golang:1.14-alpine
......
app
INFO[0281] Taking snapshot of full filesystem...
INFO[0287] Resolving 11666 paths
INFO[0291] CMD ["app"]
$ kubectl get taskrun |grep test-build-push-run
test-build-push-run-build-and-push-xl7wp True Succeeded 6m21s 65s
test-build-push-run-test-4s6qh True Succeeded 6m58s 6m21s

到这里证明我们的流水线执行成功了。我们将 Tekton 安装在 Kubernetes 集群上,定义了一个 Task,并通过 YAML 清单和 Tekton CLI 创建 TaskRun 对其进行了测试。我们创建了由两个任务组成的 Tektok 流水线,第一个任务是从 GitHub 克隆代码并运行应用程序测试,第二个任务是构建一个 Docker 镜像并将其推送到 Docker Hub 上。到这里我们就完成了使用 Tekton 创建 CI/CD 流水线的一个简单示例,不过这个示例还比较简单,接下来我们再通过一个稍微复杂点的应用来完成我们前面的 Jenkins 流水线。

触发器和事件监听器

上面我们都是通过创建一个 TaskRun 或者一个 PipelineRun 对象来触发任务。但是在实际的工作中更多的是开发人员提交代码过后来触发任务,这个时候就需要用到 Tekton 里面的 Triggers 了。

Triggers 同样通过下面的几个 CRD 对象对 Tekton 进行了一些扩展:

  • TriggerTemplate: 创建资源的模板,比如用来创建 PipelineResource 和 PipelineRun
  • TriggerBinding: 校验事件并提取相关字段属性
  • ClusterTriggerBinding: 和 TriggerBinding 类似,只是是全局的
  • EventListener: 连接 TriggerBinding 和 TriggerTemplate 到事件接收器,使用从各个 TriggerBinding 中提取的参数来创建 TriggerTemplate 中指定的 resources,同样通过 interceptor 字段来指定外部服务对事件属性进行预处理

同样要使用 Tekton Triggers 就需要安装对应的控制器,可以直接通过 tektoncd/triggers 的 GitHub 仓库说明进行安装,如下所示的命令:

$ kubectl apply --filename https://storage.googleapis.com/tekton-releases/triggers/latest/release.yaml

同样由于官方使用的镜像是 gcr 的镜像,所以正常情况下我们是获取不到的,如果你的集群由于某些原因获取不到镜像,可以使用下面的资源清单文件,我已经将镜像替换成了 Docker Hub 上面的镜像:

$ kubectl apply -f https://www.qikqiak.com/k8strain/devops/manifests/tekton/trigger.yaml

可以使用如下命令查看 Triggers 的相关组件安装状态,直到都为 Running 状态:

$ kubectl get pods --namespace tekton-pipelines
NAME READY STATUS RESTARTS AGE
tekton-dashboard-69656879d9-7bbkl 1/1 Running 0 2d6h
tekton-pipelines-controller-67f4dc98d8-pgxrq 1/1 Running 0 22d
tekton-pipelines-webhook-59df55445c-jw76v 1/1 Running 0 22d
tekton-triggers-controller-779fc9f557-vj6xs 1/1 Running 0 17m
tekton-triggers-webhook-c77f8dbd6-ctmlm 1/1 Running 0 17m

现在我们来将前面的 Jenkins Pipeline 流水线转换成使用 Tekton 来构建,代码我们已经推送到了私有仓库 GitLab,地址为:http://git.k8s.local/course/devops-demo.git。

首先我们需要完成触发器的配置,当我们提交源代码到 GitLab 的时候,需要触发 Tekton 的任务运行。所以首先需要完成这个触发器。这里就可以通过 EventListener 这个资源对象来完成,创建一个名为 gitlab-listener 的 EventListener 资源对象,文件内容如下所示:(gitlab-push-listener.yaml)

apiVersion: triggers.tekton.dev/v1alpha1
kind: EventListener
metadata:
name: gitlab-listener
spec:
serviceAccountName: tekton-triggers-gitlab-sa
triggers:
- name: gitlab-push-events-trigger
interceptors:
- gitlab:
secretRef: # 引用 gitlab-secret 的 Secret 对象中的 secretToken 的值
secretName: gitlab-secret
secretKey: secretToken
eventTypes:
- Push Hook # 只接收 GitLab Push 事件
bindings:
- name: gitlab-push-binding # TriggerBinding 对象
template:
name: gitlab-echo-template # TriggerTemplate 对象
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: el-gitlab-listener
spec:
routes:
- match: Host(`tekton-trigger.k8s.local`)
kind: Rule
services:
- name: el-gitlab-listener # 关联 EventListener 服务
port: 8080

由于 EventListener 创建完成后会生成一个 Listener 的服务,用来对外暴露用于接收事件响应,比如上面我们创建的对象名为 gitlab-listener,创建完成后会生成一个名为 el-gitlab-listener 的 Service 对象,这里我们通过 IngressRoute 对象来暴露该服务,当然直接修改 Service 为 NodePort 类型也可以,如果 GitLab 也在集群内部,当然我们用 Service 的 DNS 形式来访问 EventListener 当然也可以。

另外需要注意的是在上面的 EventListener 对象中我们添加了 interceptors 属性,其中有一个内置的 gitlab 属性可以用来配置 GitLab 相关的信息,比如配置 WebHook 的 Secret Token,可以通过 Secret 对象引入进来:

interceptors:
- gitlab:
secretRef: # 引用 gitlab-secret 的 Secret 对象中的 secretToken 的值
secretName: gitlab-secret
secretKey: secretToken
eventTypes:
- Push Hook # 只接收 GitLab Push 事件

对应的 Secret 资源对象如下所示,一个用于 WebHook 的 Secret Token,另外一个是用于 GitLab 登录认证使用的:(secret.yaml)

apiVersion: v1
kind: Secret
metadata:
name: gitlab-secret
type: Opaque
stringData:
secretToken: "1234567"
---
apiVersion: v1
kind: Secret
metadata:
name: gitlab-auth
annotations:
tekton.dev/git-0: http://git.k8s.local
type: kubernetes.io/basic-auth
stringData:
username: myusername
password: mypassword

由于 EventListener 对象需要访问其他资源对象,所以需要声明 RBAC,如下所示:(rbac.yaml)

apiVersion: v1
kind: ServiceAccount
metadata:
name: tekton-triggers-gitlab-sa
secrets:
- name: gitlab-secret
- name: gitlab-auth
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: tekton-triggers-gitlab-minimal
rules:
# Permissions for every EventListener deployment to function
- apiGroups: ["triggers.tekton.dev"]
resources: ["eventlisteners", "triggerbindings", "triggertemplates"]
verbs: ["get"]
- apiGroups: [""]
# secrets are only needed for Github/Gitlab interceptors, serviceaccounts only for per trigger authorization
resources: ["configmaps", "secrets", "serviceaccounts"]
verbs: ["get", "list", "watch"]
# Permissions to create resources in associated TriggerTemplates
- apiGroups: ["tekton.dev"]
resources: ["pipelineruns", "pipelineresources", "taskruns"]
verbs: ["create"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: tekton-triggers-gitlab-binding
subjects:
- kind: ServiceAccount
name: tekton-triggers-gitlab-sa
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: tekton-triggers-gitlab-minimal

然后接下来就是最重要的 TriggerBinding 和 TriggerTemplate 对象了,我们在上面的 EventListener 对象中将两个对象组合在一起:

bindings:
- name: gitlab-push-binding # TriggerBinding 对象
template:
name: gitlab-echo-template # TriggerTemplate 对象

这样就可以将 TriggerBinding 中的参数传递到 TriggerTemplate 对象中进行模板化。比如这里我们定义一个如下所示的 TriggerBinding 对象:

apiVersion: triggers.tekton.dev/v1alpha1
kind: TriggerBinding
metadata:
name: gitlab-push-binding
spec:
params:
- name: gitrevision
value: $(body.checkout_sha)
- name: gitrepositoryurl
value: $(body.repository.git_http_url)

这里需要注意的是参数的值我们是通过读取 GitLab WebHook 发送过来的数据值,通过 $() 包裹的 JSONPath 表达式来提取的,关于表达式的更多用法可以查看官方文档说明,至于能够提取哪些参数值,则可以查看 WebHook 的说明,比如这里我们是 GitLab Webhook 的 Push Hook,对应的请求体数据如下所示:

{
"object_kind": "push",
"before": "95790bf891e76fee5e1747ab589903a6a1f80f22",
"after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
"ref": "refs/heads/master",
"checkout_sha": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
"user_id": 4,
"user_name": "John Smith",
"user_username": "jsmith",
"user_email": "john@example.com",
"user_avatar": "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80",
"project_id": 15,
"project":{
"id": 15,
"name":"Diaspora",
"description":"",
"web_url":"http://example.com/mike/diaspora",
"avatar_url":null,
"git_ssh_url":"git@example.com:mike/diaspora.git",
"git_http_url":"http://example.com/mike/diaspora.git",
"namespace":"Mike",
"visibility_level":0,
"path_with_namespace":"mike/diaspora",
"default_branch":"master",
"homepage":"http://example.com/mike/diaspora",
"url":"git@example.com:mike/diaspora.git",
"ssh_url":"git@example.com:mike/diaspora.git",
"http_url":"http://example.com/mike/diaspora.git"
},
"repository":{
"name": "Diaspora",
"url": "git@example.com:mike/diaspora.git",
"description": "",
"homepage": "http://example.com/mike/diaspora",
"git_http_url":"http://example.com/mike/diaspora.git",
"git_ssh_url":"git@example.com:mike/diaspora.git",
"visibility_level":0
},
"commits": [
{
"id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",
"message": "Update Catalan translation to e38cb41.\n\nSee https://gitlab.com/gitlab-org/gitlab for more information",
"title": "Update Catalan translation to e38cb41.",
"timestamp": "2011-12-12T14:27:31+02:00",
"url": "http://example.com/mike/diaspora/commit/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",
"author": {
"name": "Jordi Mallach",
"email": "jordi@softcatala.org"
},
"added": ["CHANGELOG"],
"modified": ["app/controller/application.rb"],
"removed": []
}
],
"total_commits_count": 4
}

请求体中的任何属性都可以提取出来,作为 TriggerBinding 的参数,如果是其他的 Hook 事件,对应的请求体结构可以查看 GitLab 文档说明

这样我们就可以在 TriggerTemplate 对象中通过参数来读取上面 TriggerBinding 中定义的参数值了,定义一个如下所示的 TriggerTemplate 对象,声明一个 TaskRun 的模板,定义的 Task 任务也非常简单,只需要在容器中打印出代码的目录结构即可:

apiVersion: triggers.tekton.dev/v1alpha1
kind: TriggerTemplate
metadata:
name: gitlab-echo-template
spec:
params: # 定义参数,和 TriggerBinding 中的保持一致
- name: gitrevision
- name: gitrepositoryurl
resourcetemplates:
- apiVersion: tekton.dev/v1alpha1
kind: TaskRun # 定义 TaskRun 模板
metadata:
generateName: gitlab-run- # TaskRun 名称前缀
spec:
serviceAccountName: tekton-triggers-gitlab-sa
taskSpec: # Task 任务声明
inputs:
resources: # 定义一个名为 source 的 git 输入资源
- name: source
type: git
steps:
- name: show-path
image: ubuntu # 定义一个执行步骤,列出代码目录结构
script: |
#! /bin/bash
ls -la $(inputs.resources.source.path)
inputs: # 声明具体的输入资源参数
resources:
- name: source # 和 Task 中的资源名保持一直
resourceSpec: # 资源声明
type: git
params:
- name: revision
value: $(params.gitrevision) # 读取参数值
- name: url
value: $(params.gitrepositoryurl)

定义完过后,直接创建上面的资源对象,创建完成后会自动生成 EventListener 的 Pod 和 Service 对象:

$ kubectl get svc -l eventlistener=gitlab-listener
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
el-gitlab-listener ClusterIP 10.98.145.130 <none> 8080/TCP 91m
$ kubectl get pod -l eventlistener=gitlab-listener
NAME READY STATUS RESTARTS AGE
el-gitlab-listener-6bff9b55bc-qdpjh 1/1 Running 0 91m
$ kubectl get ingressroute
NAME AGE
el-gitlab-listener 74m

接下来我们就可以到 GitLab 的项目中配置 WebHook,注意需要配置 Secret Token,我们在上面的 Secret 对象中声明过:

创建完成后,我们可以测试下该 WebHook 的 Push events 事件,直接点击测试即可,正常会返回 Hook executed successfully: HTTP 201 的提示信息,这个时候在 Kubernetes 集群中就会出现如下所示的任务 Pod:

$ kubectl get pod -l triggers.tekton.dev/eventlistener=gitlab-listener
NAME READY STATUS RESTARTS AGE
gitlab-run-2jfcf-pod-8smb7 0/2 Completed 0 5m22s
$ kubectl get taskrun -l triggers.tekton.dev/eventlistener=gitlab-listener
NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME
gitlab-run-2jfcf True Succeeded 12h 12h
$ kubectl logs -f gitlab-run-2jfcf-pod-8smb7 --all-containers
{"level":"info","ts":1590718126.8466494,"caller":"git/git.go:136","msg":"Successfully cloned http://git.k8s.local/course/devops-demo.git @ e8b2dd4cac9bfaa79f1998215b4c3fd0e98ea84d (grafted, HEAD) in path /workspace/source"}
{"level":"info","ts":1590718126.9744687,"caller":"git/git.go:177","msg":"Successfully initialized and updated submodules in path /workspace/source"}
{"level":"info","ts":1590717929.8761587,"caller":"creds-init/main.go:44","msg":"Credentials initialized."}
total 36
drwxr-xr-x 4 root root 136 May 29 10:08 .
drwxrwxrwx 3 root root 19 May 29 10:08 ..
drwxr-xr-x 8 root root 4096 May 29 10:08 .git
-rw-r--r-- 1 root root 192 May 29 10:08 .gitignore
-rw-r--r-- 1 root root 376 May 29 10:08 Dockerfile
-rw-r--r-- 1 root root 4788 May 29 10:08 Jenkinsfile
-rw-r--r-- 1 root root 42 May 29 10:08 README.md
-rw-r--r-- 1 root root 97 May 29 10:08 go.mod
-rw-r--r-- 1 root root 3370 May 29 10:08 go.sum
drwxr-xr-x 3 root root 96 May 29 10:08 helm
-rw-r--r-- 1 root root 444 May 29 10:08 main.go

到这里我们就完成了通过 GitLab 的 Push 事件来触发 Tekton 的一个任务。

Kubernetes DevOps: Tekton的更多相关文章

  1. 基于jenkins,tekton等工具打造kubernetes devops平台

    本贴为目录贴,将不断更新 目录 1.Docker在centos下安装以及常见错误解决 2.使用kubernetes 官网工具kubeadm部署kubernetes(使用阿里云镜像) 3.无法访问gcr ...

  2. Kubernetes DevOps: Jenkins Pipeline (流水线)

    要实现在 Jenkins 中的构建工作,可以有多种方式,我们这里采用比较常用的 Pipeline 这种方式.Pipeline,简单来说,就是一套运行在 Jenkins 上的工作流框架,将原来独立运行于 ...

  3. Windows下搭建Docker与Kubernetes(DevOps一)

    Docker与Kubernetes (二)搭建 开通  Hyper-V 安装Docker for Windows 开通Kubernetes 3.关键概念 PodKubernetes 中的最小单元,一个 ...

  4. Kubernetes DevOps: Harbor

    Harbor 是一个 CNCF 基金会托管的开源的可信的云原生 docker registry 项目,可以用于存储.签名.扫描镜像内容,Harbor 通过添加一些常用的功能如安全性.身份权限管理等来扩 ...

  5. Kubernetes DevOps: Gitlab

    Gitlab 官方提供了 Helm 的方式在 Kubernetes 集群中来快速安装,但是在使用的过程中发现 Helm 提供的 Chart 包中有很多其他额外的配置,所以我们这里使用自定义的方式来安装 ...

  6. Kubernetes DevOps: Jenkins

    提到基于 Kubernete 的CI/CD,可以使用的工具有很多,比如 Jenkins.Gitlab CI 以及新兴的 drone 之类的,我们这里会使用大家最为熟悉的 Jenkins 来做 CI/C ...

  7. CD 基金会、Jenkins、Jenkins X、Spinnaker 和 Tekton 的常问问题

    转载:https://mp.weixin.qq.com/s/bQLqGrCM9NZYI0Njlu4N-w FAQ 什么是持续交付(CD)? CD是一种软件工程方法,团队在短周期内生成软件,确保软件可以 ...

  8. 阳明-K8S训练营全部文档-2020年08月11日14:59:02更新

    阳明-K8S训练营全部文档 Docker 基础 简介 安装 基本操作 Dockerfile Dockerfile最佳实践 Kubernetes 基础 简介 安装 资源清单 Pod 原理 Pod 生命周 ...

  9. Kubernetes 会不会“杀死” DevOps?

    作者丨孙健波(天元)  阿里巴巴技术专家 导读:DevOps 这个概念最早是在 2007 年提出的,那时云计算基础设施的概念也才刚刚提出没多久,而随着互联网的逐渐普及,应用软件的需求爆发式增长,软件开 ...

随机推荐

  1. JTable和MVC设计模式

    JTable: 用JTable类可以以表格的形式显示和编辑数据 . JTable类的对象并不存储数据,它只是数据的表现 data MVC ~数据,表现和控制三者分离,各负其责 ~M=Model(模型) ...

  2. 记一次 Druid 超时配置的问题 → 引发对 Druid 时间配置项的探究

    开心一刻 一天在路边看到一个街头采访 记者:请问,假如你儿子娶媳妇,给多少彩礼合适呢 大爷:一百万吧,再给一套房,一辆车 大爷沉思一下,继续说到:如果有能力的话再给老丈人配一辆车,毕竟他把女儿养这么大 ...

  3. windows配置skywalking集群

    一.zookeeper 准备配置三个zookeeper,因为我是单台模拟,所以需要使用不同的端口,使用版本是apache-zookeeper-3.6.3-bin (必须是3.5+) 1.第1个zook ...

  4. surging作者出具压测结果

    前言 首先回应下@wen-wen 所贴的压测报告,我也把我和客户压测碰到的问题,和压测结果贴出来,这个结果是由客户提供的.不会有任何的舞弊手脚问题 问题一:Task.Run慎用 首先在最新的社区版本已 ...

  5. 09 MySQL_SQL日期函数和聚合函数

    日期相关的函数 seclect 'helloworld'; 1. 获取当前时间 now(); select now(); 2.获取当前的日期 curdate(); select curdate(); ...

  6. 类型转换_str()函数与int()函数

    数据类型转换 需要将不同数据类型拼接在一起的时候就需要先进行数据类型转换 str+str//这里的+叫做连接字符,有点类似C++中的操作符重载,老对象里面的内容了 在python中整型和字符串类型不能 ...

  7. java 桥接方法

    1.桥接方法简介 桥接方法是jdk1.5引入泛型后,为使java泛型方法生成的字节码与jdk1.5版本之前的字节码兼容由编译器自动生成的. 可用method.isBridge() 判断method是否 ...

  8. Arraylist集合的概述和基本使用与常用方法

    什么是ArrayList类 java.util.ArrayList 是大小可变的数组实现的,存储在内的数据称为元素,此类提供一些方法来操作内部存储的元素.ArrayList中可不断添加元素,其大小也自 ...

  9. YII模块化处理

    config.php $config = [ 'modules' => [ 'comment' => [ 'class' => 'frontend\modules\comment\C ...

  10. Java学习 (五)基础篇 数据类型

    目录 数据类型 强类型语言 弱类型语言 Java数据类型分为两大类 八大字节类型(重点) Java数据类型拓展 整数拓展:进制 浮点数拓展 字符类型 布尔值拓展 数据类型 强类型语言 要求变量的使用要 ...