跳到主要内容

实战 Tekton 流水线

前面我们讲解了使用 Jenkins 流水线来实现 Kubernetes 应用的 CI/CD,现在我们来将这个流水线迁移到 Tekton 上面来,其实整体思路都是一样的,就是把要整个工作流划分成不同的任务来执行,前面工作流的阶段划分了以下几个阶段:Clone 代码 -> 单元测试 -> Golang 编译打包 -> Docker 镜像构建/推送 -> Kubectl/Helm 部署服务

在 Tekton 中我们就可以将这些阶段直接转换成 Task 任务,Clone 代码在 Tekton 中不需要我们主动定义一个任务,只需要在执行的任务上面指定一个输入的代码资源即可。下面我们就来将上面的工作流一步一步来转换成 Tekton 流水线,代码仓库同样还是 http://git.k8s.local/course/devops-demo.git

Clone 代码

虽然我们可以不用单独定义一个 Clone 代码的任务,直接使用 git 类型的输入资源即可,由于这里涉及到的任务较多,而且很多时候都需要先 Clone 代码然后再进行操作,所以最好的方式是将代码 Clone 下来过后通过 Workspace 共享给其他任务,这里我们可以直接使用 Catalog git-clone 来实现这个任务,我们可以根据自己的需求做一些定制,对应的 Task 如下所示:

# task-deploy.yaml
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: deploy-911
labels:
app.kubernetes.io/version: '0.3'
annotations:
tekton.dev/pipelines.minVersion: '0.12.1'
tekton.dev/categories: Deployment
tekton.dev/tags: helm
tekton.dev/platforms: 'linux/amd64,linux/s390x,linux/ppc64le,linux/arm64'
spec:
description: >-
These tasks will install / upgrade a helm chart into your Kubernetes /
OpenShift Cluster using Helm

params:
- name: charts_dir
description: The directory in source that contains the helm chart
- name: release_version
description: The helm release version in semantic versioning format
default: 'v1.0.0'
- name: release_name
description: The helm release name
default: 'helm-release'
- name: release_namespace
description: The helm release namespace
default: ''
- name: overwrite_values
description: 'Specify the values you want to overwrite, comma separated: autoscaling.enabled=true,replicas=1'
default: ''
- name: values_file
description: 'The values file to be used'
default: 'values.yaml'
- name: helm_image
description: 'helm image to be used'
default: 'registry.cn-beijing.aliyuncs.com/xxk8s/k8s-helm:v3.6.0'
- name: upgrade_extra_params
description: 'Extra parameters passed for the helm upgrade command'
default: ''
workspaces:
- name: source
results:
- name: helm-status
description: Helm deploy status
steps:
- name: upgrade
image: $(params.helm_image)
workingDir: /workspace/source
script: |
echo current installed helm releases
helm list --namespace "$(params.release_namespace)"

echo installing helm chart...
helm upgrade --install --wait --values "$(params.charts_dir)/$(params.values_file)" --namespace "$(params.release_namespace)" --version "$(params.release_version)" "$(params.release_name)" "$(params.charts_dir)" --debug --set "$(params.overwrite_values)" $(params.upgrade_extra_params)

status=`helm status $(params.release_name) --namespace "$(params.release_namespace)" | awk '/STATUS/ {print $2}'`
echo ${status} | tr -d "\n" | tee $(results.helm-status.path)

一般来说我们只需要提供 output 这个个用于持久化代码的 workspace,然后还包括 url 和 revision 这两个参数,其他使用默认的即可。

单元测试

单元测试阶段比较简单,正常来说也是只是单纯执行一个测试命令即可,我们这里没有真正执行单元测试,所以简单测试下即可,编写一个如下所示的 Task:

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: test-911
spec:
steps:
- name: test
image: registry.cn-beijing.aliyuncs.com/xxk8s/golang:1.14.2-alpine3.11
command: ['echo']
args: ['this is a test task']

编译打包

然后第二个阶段是编译打包阶段,因为我们这个项目的 Dockerfile 不是使用的多阶段构建,所以需要先用一个任务去将应用编译打包成二进制文件,然后将这个编译过后的文件传递到下一个任务进行镜像构建。

我们已经明确了这个阶段要做的事情,编写任务也就简单了,创建如下所的 Task 任务,首先需要通过定义一个 workspace 把 clone 任务里面的代码关联过来:

# task-build.yaml
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: build-911
spec:
workspaces:
- name: go-repo
mountPath: /workspace/repo
steps:
- name: build
image: registry.cn-beijing.aliyuncs.com/xxk8s/golang:1.14.2-alpine3.11
workingDir: /workspace/repo
script: |
go build -v -o demo-app
env:
- name: GOPROXY
value: https://goproxy.cn
- name: GOOS
value: linux
- name: GOARCH
value: amd64

这个构建任务也很简单,只是我们将需要用到的环境变量直接通过 env 注入了,当然直接写入到 script 中也是可以的,或者直接使用 command 来执行任务都可以,然后构建生成的 demo-app 这个二进制文件保留在代码根目录,这样也就可以通过 workspace 进行共享了。

Docker 镜像

接下来就是构建并推送 Docker 镜像了,前面我们介绍过使用 Kaniko、DooD、DinD 3 种模式的镜像构建方式,这里我们直接使用 DinD 这种模式,我们这里要构建的镜像 Dockerfile 非常简单:

FROM alpine
WORKDIR /home

# 修改alpine源为阿里云
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories && \
apk update && \
apk upgrade && \
apk add ca-certificates && update-ca-certificates && \
apk add --update tzdata && \
rm -rf /var/cache/apk/*

COPY demo-app /home/
ENV TZ=Asia/Shanghai

EXPOSE 8080

ENTRYPOINT ./demo-app

直接将编译好的二进制文件拷贝到镜像中即可,所以我们这里同样需要通过 Workspace 去获取上一个构建任务的制品,这里我们使用 sidecar 的方式来实现 DinD 模式构建镜像,创建一个如下所示的任务:

# task-docker.yaml
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: docker
spec:
workspaces:
- name: go-repo
params:
- name: image
description: Reference of the image docker will produce.
- name: registry_mirror
description: Specific the docker registry mirror
default: ''
- name: registry_url
description: private docker images registry url
steps:
- name: docker-build # 构建步骤
image: registry.cn-beijing.aliyuncs.com/xxk8s/docker:l24910
env:
- name: DOCKER_HOST # 用 TLS 形式通过 TCP 链接 sidecar
value: tcp://localhost:2376
- name: DOCKER_TLS_VERIFY # 校验 TLS
value: '1'
- name: DOCKER_CERT_PATH # 使用 sidecar 守护进程生成的证书
value: /certs/client
- name: DOCKER_PASSWORD
valueFrom:
secretKeyRef:
name: harbor-auth
key: password
- name: DOCKER_USERNAME
valueFrom:
secretKeyRef:
name: harbor-auth
key: username
workingDir: $(workspaces.go-repo.path)
script: | # docker 构建命令
docker login $(params.registry_url) -u $DOCKER_USERNAME -p $DOCKER_PASSWORD
docker build --no-cache -f ./Dockerfile -t $(params.image) .
docker push $(params.image)
volumeMounts: # 声明挂载证书目录
- mountPath: /certs/client
name: dind-certs
sidecars: # sidecar 模式,提供 docker daemon服务,实现真正的 DinD 模式
- image: registry.cn-beijing.aliyuncs.com/xxk8s/docker:dind
name: server
args:
- --storage-driver=vfs
- --userland-proxy=false
- --debug
- --insecure-registry=$(params.registry_url)
- --registry-mirror=$(params.registry_mirror)
securityContext:
privileged: true
env:
- name: DOCKER_TLS_CERTDIR # 将生成的证书写入与客户端共享的路径
value: /certs
volumeMounts:
- mountPath: /certs/client
name: dind-certs
- mountPath: /var/lib/docker
name: docker-root
readinessProbe: # 等待 dind daemon 生成它与客户端共享的证书
periodSeconds: 1
exec:
command: ['ls', '/certs/client/ca.pem']
volumes: # 使用 emptyDir 的形式即可
- name: dind-certs
emptyDir: {}
- name: docker-root
persistentVolumeClaim:
claimName: docker-root-pvc

这个任务的重点还是要去声明一个 Workspace,当执行任务的时候要使用和前面构建任务同一个 Workspace,这样就可以获得上面编译成的 demo-app 这个二进制文件了。

部署

接下来的部署阶段,我们同样可以参考之前 Jenkins 流水线里面的实现,由于项目中我们包含了 Helm Chart 包,所以直接使用 Helm 来部署即可,要实现 Helm 部署,当然我们首先需要一个包含 helm 命令的镜像,当然完全可以自己去编写一个这样的任务,此外我们还可以直接去 hub.tekton.dev 上面查找 Catalog,因为这上面就有很多比较通用的一些任务了,比如 helm-upgrade-from-source 这个 Task 任务就完全可以满足我们的需求了:

38871106b2b7

这个 Catalog 下面也包含完整的使用文档了,我们可以将该任务直接下载下来根据我们自己的需求做一些定制修改,如下所示:

# task-deploy.yaml
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: deploy-911
labels:
app.kubernetes.io/version: '0.3'
annotations:
tekton.dev/pipelines.minVersion: '0.12.1'
tekton.dev/categories: Deployment
tekton.dev/tags: helm
tekton.dev/platforms: 'linux/amd64,linux/s390x,linux/ppc64le,linux/arm64'
spec:
description: >-
These tasks will install / upgrade a helm chart into your Kubernetes /
OpenShift Cluster using Helm

params:
- name: charts_dir
description: The directory in source that contains the helm chart
- name: release_version
description: The helm release version in semantic versioning format
default: 'v1.0.0'
- name: release_name
description: The helm release name
default: 'helm-release'
- name: release_namespace
description: The helm release namespace
default: ''
- name: overwrite_values
description: 'Specify the values you want to overwrite, comma separated: autoscaling.enabled=true,replicas=1'
default: ''
- name: values_file
description: 'The values file to be used'
default: 'values.yaml'
- name: helm_image
description: 'helm image to be used'
default: 'registry.cn-beijing.aliyuncs.com/xxk8s/k8s-helm:v3.6.0'
- name: upgrade_extra_params
description: 'Extra parameters passed for the helm upgrade command'
default: ''
workspaces:
- name: source
results:
- name: helm-status
description: Helm deploy status
steps:
- name: upgrade
image: $(params.helm_image)
workingDir: /workspace/source
script: |
echo current installed helm releases
helm list --namespace "$(params.release_namespace)"

echo installing helm chart...
helm upgrade --install --wait --values "$(params.charts_dir)/$(params.values_file)" --namespace "$(params.release_namespace)" --version "$(params.release_version)" "$(params.release_name)" "$(params.charts_dir)" --debug --set "$(params.overwrite_values)" $(params.upgrade_extra_params)

status=`helm status $(params.release_name) --namespace "$(params.release_namespace)" | awk '/STATUS/ {print $2}'`
echo ${status} | tr -d "\n" | tee $(results.helm-status.path)

因为我们的 Helm Chart 模板就在代码仓库中,所以不需要从 Chart Repo 仓库中获取,只需要指定 Chart 路径即可,其他可配置的参数都通过 params 参数暴露出去了,非常灵活,最后我们还获取了 Helm 部署的状态,写入到了 Results 中,方便后续任务处理。

回滚

最后应用部署完成后可能还需要回滚,因为可能部署的应用有错误,当然这个回滚动作最好是我们自己去触发,但是在某些场景下,比如 helm 部署已经明确失败了,那么我们当然可以自动回滚了,所以就需要判断当部署失败的时候再执行回滚,也就是这个任务并不是一定会发生的,只在某些场景下才会出现,我们可以在流水线中通过使用 WhenExpressions 来实现这个功能。要只在满足某些条件时运行任务,可以使用 when 字段来保护任务执行,when 字段允许你列出对 WhenExpressions 的一系列引用。

WhenExpressionsInputOperatorValues 几部分组成:

  • InputWhenExpressions 的输入,它可以是一个静态的输入或变量(Params 或 Results),如果未提供输入,则默认为空字符串
  • Operator 是一个运算符,表示 Input 和 Values 之间的关系,有效的运算符包括 innotin
  • Values 是一个字符串数组,必须提供一个非空的 Values 数组,它同样可以包含静态值或者变量(Params、Results 或者 Workspaces 绑定)

当在一个 Task 任务中配置了 WhenExpressions,在执行 Task 之前会评估声明的 WhenExpressions,如果结果为 True,则执行任务,如果为 False,则不会执行该任务。

我们这里创建的回滚任务如下所示:

# task-rollback.yaml
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: rollback-911
spec:
params:
- name: release_name
description: The helm release name
- name: release_namespace
description: The helm release namespace
default: ''
- name: helm_image
description: 'helm image to be used'
default: 'registry.cn-beijing.aliyuncs.com/xxk8s/k8s-helm:v3.6.0' #tag: v3.6.0
steps:
- name: rollback
image: $(params.helm_image)
script: |
echo rollback current installed helm releases
helm rollback $(params.release_name) --namespace $(params.release_namespace)

流水线

现在我们的整个工作流任务都已经创建完成了,接下来我们就可以将这些任务全部串联起来组成一个 Pipeline 流水线了,将上面定义的几个 Task 引用到 Pipeline 中来,当然还需要声明 Task 中用到的 resources 或者 workspaces 这些数据:

# pipeline.yaml
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: pipeline-911
spec:
workspaces: # 声明 workspaces
- name: go-repo-pvc
- name: secret-gitee-auth
params:
# 定义代码仓库
- name: git_url
- name: revision
type: string
default: 'main'
# 定义镜像参数
- name: image
- name: registry_url
type: string
default: 'harbor.k8s.local'
- name: registry_mirror
type: string
default: 'https://mirror.baidubce.com'
# 定义 helm charts 参数
- name: charts_dir
- name: release_name
- name: release_namespace
default: 'default'
- name: overwrite_values
default: ''
- name: values_file
default: 'values.yaml'
tasks: # 添加task到流水线中
- name: 1-clone
taskRef:
name: git-clone-911
workspaces:
- name: output
workspace: go-repo-pvc
- name: ssh-directory
workspace: secret-gitee-auth
params:
- name: url
value: $(params.git_url)
- name: revision
value: $(params.revision)
- name: 2-test
taskRef:
name: test-911
runAfter:
- 1-clone
- name: 3-build # 编译二进制程序
taskRef:
name: build-911
runAfter: # 测试任务执行之后才执行 build task
- 2-test
- 1-clone
workspaces: # 传递 workspaces
- name: go-repo
workspace: go-repo-pvc
- name: 4-docker # 构建并推送 Docker 镜像
taskRef:
name: docker-911
runAfter:
- 3-build
workspaces: # 传递 workspaces
- name: go-repo
workspace: go-repo-pvc
params: # 传递参数
- name: image
value: $(params.image)
- name: registry_url
value: $(params.registry_url)
- name: registry_mirror
value: $(params.registry_mirror)
- name: 5-deploy # 部署应用
taskRef:
name: deploy-911
runAfter:
- 4-docker
workspaces:
- name: source
workspace: go-repo-pvc
params:
- name: charts_dir
value: $(params.charts_dir)
- name: release_name
value: $(params.release_name)
- name: release_namespace
value: $(params.release_namespace)
- name: overwrite_values
value: $(params.overwrite_values)
- name: values_file
value: $(params.values_file)
- name: 6-rollback # 回滚
taskRef:
name: rollback-911
when:
- input: '$(tasks.5-deploy.results.helm-status)'
operator: in
values: ['failed']
params:
- name: release_name
value: $(params.release_name)
- name: release_namespace
value: $(params.release_namespace)

整体流程比较简单,就是在 Pipeline 需要先声明使用到的 Workspace、Resource、Params 这些资源,然后将声明的数据传递到 Task 任务中去,需要注意的是最后一个回滚任务,我们需要根据前面的 deploy 任务的结果来判断是否需要执行该任务,所以这里我们使用了 when 属性,通过 $(tasks.deploy.results.helm-status) 获取部署状态。

执行流水线

现在我们就可以来执行下我们的流水线,看是否符合我们自身的要求,首先我们需要先创建关联的其他资源对象,比如 Workspace 对应的 PVC、还有 GitLab、Harbor 的认证信息:

# other.yaml
apiVersion: v1
kind: Secret
metadata:
name: gitlab-auth
annotations:
tekton.dev/git-0: http://git.k8s.local
type: kubernetes.io/basic-auth
stringData:
username: root
password: admin321

---
apiVersion: v1
kind: Secret
metadata:
name: harbor-auth
annotations:
tekton.dev/docker-0: http://harbor.k8s.local
type: kubernetes.io/basic-auth
stringData:
username: admin
password: Harbor12345

---
apiVersion: v1
kind: ServiceAccount
metadata:
name: tekton-build-sa
secrets:
- name: harbor-auth
- name: gitlab-auth
- name: gitlab-secret

---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: tekton-triggers-gitlab-minimal
rules:
# EventListeners need to be able to fetch all namespaced resources
- apiGroups: ['triggers.tekton.dev']
resources:
['eventlisteners', 'triggerbindings', 'triggertemplates', 'triggers']
verbs: ['get', 'list', 'watch']
- apiGroups: ['']
# configmaps is needed for updating logging config
resources: ['configmaps']
verbs: ['get', 'list', 'watch']
# Permissions to create resources in associated TriggerTemplates
- apiGroups: ['tekton.dev']
resources: ['pipelineruns', 'pipelineresources', 'taskruns']
verbs: ['create']
- apiGroups: ['']
resources: ['serviceaccounts']
verbs: ['impersonate']
- apiGroups: ['policy']
resources: ['podsecuritypolicies']
resourceNames: ['tekton-triggers']
verbs: ['use']
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: tekton-triggers-gitlab-binding
subjects:
- kind: ServiceAccount
name: tekton-build-sa
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: tekton-triggers-gitlab-minimal
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: tekton-triggers-gitlab-clusterrole
rules:
# EventListeners need to be able to fetch any clustertriggerbindings
- apiGroups: ['triggers.tekton.dev']
resources: ['clustertriggerbindings', 'clusterinterceptors']
verbs: ['get', 'list', 'watch']
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: tekton-triggers-gitlab-clusterbinding
subjects:
- kind: ServiceAccount
name: tekton-build-sa
namespace: default
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: tekton-triggers-gitlab-clusterrole

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: go-repo-pvc
spec:
resources:
requests:
storage: 1Gi
volumeMode: Filesystem
storageClassName: rook-cephfs # 使用 StorageClass 自动生成 PV
accessModes:
- ReadWriteMany

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: docker-root-pvc
spec:
resources:
requests:
storage: 20Gi
volumeMode: Filesystem
storageClassName: rook-cephfs # 使用 StorageClass 自动生成 PV
accessModes:
- ReadWriteMany

这些关联的资源对象创建完成后,还需要为上面的 ServiceAccount 绑定一个权限,因为在 Helm 容器中我们要去操作一些集群资源,必然需要先做权限声明,这里我们可以将 tekton-build-sa 绑定到 edit 这个 ClusterRole 上去。

我们接下来就可以创建一个 PipelineRun 资源对象来触发我们的流水线构建了:

# pipelinerun.yaml
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
name: pipelinerun-911
spec:
podTemplate:
securityContext:
fsGroup: 65532
serviceAccountName: github-bot
pipelineRef:
name: pipeline-911
workspaces:
- name: go-repo-pvc
persistentVolumeClaim:
claimName: go-repo-pvc
- name: secret-gitee-auth
secret:
secretName: git-ssh-secret-gee
params:
- name: git_url
value: git@gitee.com:ryanxin/devops-demo.git
- name: image
value: 'harbor.k8s.local/course/devops-demo:v0.1.0'
- name: charts_dir
value: './helm'
- name: release_name
value: devops-demo
- name: release_namespace
value: 'kube-ops'
- name: overwrite_values
value: 'image.repository=harbor.k8s.local/course/devops-demo,image.tag=v0.1.0'
- name: values_file
value: 'my-values.yaml'

直接创建上面的资源对象就可以执行我们的 Pipeline 流水线了:

$ kubectl apply -f pipelinerun.yaml
$ tkn pr describe pipelinerun
Name: pipelinerun
Namespace: default
Pipeline Ref: pipeline
Service Account: tekton-build-sa
Timeout: 1h0m0s
Labels:
tekton.dev/pipeline=pipeline

🌡️ Status

STARTED DURATION STATUS
4 minutes ago 2m30s Succeeded(Completed)

⚓ Params

NAME VALUE
∙ git_url http://git.k8s.local/course/devops-demo.git
∙ image harbor.k8s.local/course/devops-demo:v0.1.0
∙ charts_dir ./helm
∙ release_name devops-demo
∙ release_namespace kube-ops
∙ overwrite_values image.repository=harbor.k8s.local/course/devops-demo,image.tag=v0.1.0
∙ values_file my-values.yaml

📂 Workspaces

NAME SUB PATH WORKSPACE BINDING
∙ go-repo-pvc --- PersistentVolumeClaim (claimName=go-repo-pvc)

🗂 Taskruns

NAME TASK NAME STARTED DURATION STATUS
∙ pipelinerun-deploy deploy 3 minutes ago 1m14s Succeeded
∙ pipelinerun-docker docker 4 minutes ago 55s Succeeded
∙ pipelinerun-build build 4 minutes ago 11s Succeeded
∙ pipelinerun-test test 4 minutes ago 4s Succeeded
∙ pipelinerun-clone clone 4 minutes ago 6s Succeeded

⏭️ Skipped Tasks

NAME
∙ rollback

# 部署成功了
$ curl devops-demo.k8s.local
{"msg":"Hello DevOps On Kubernetes"}

在 Dashboard 上也可以看到可以流水线可以正常执行,由于部署成功了,所以 rollback 回滚的任务也就被忽略了:

e3e7a35febf2

9a1c3ba61873

触发器

整个流水线已经成功执行了,接下来最后一步就是将 Gitlab 和 Tekton 进行对接,也就是通过 Tekton Trigger 来自动触发构建。

关于 Tekton Trigger 的使用前面我们已经详细讲解过了,细节就不过多讨论。

首先添加一个用于 Gitlab Webhook 访问的 Secret Token,同样要将这个 Secret 关联到上面使用的 ServiceAccount 上面去,然后继续添加对应的 RBAC 权限:

# other.yaml
# ......
apiVersion: v1
kind: Secret
metadata:
name: gitlab-secret
type: Opaque
stringData:
secretToken: '1234567'

---
apiVersion: v1
kind: ServiceAccount
metadata:
name: tekton-build-sa
secrets:
- name: harbor-auth
- name: gitlab-auth
- name: gitlab-secret

---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: tekton-triggers-gitlab-minimal
rules:
# EventListeners need to be able to fetch all namespaced resources
- apiGroups: ['triggers.tekton.dev']
resources:
['eventlisteners', 'triggerbindings', 'triggertemplates', 'triggers']
verbs: ['get', 'list', 'watch']
- apiGroups: ['']
# configmaps is needed for updating logging config
resources: ['configmaps']
verbs: ['get', 'list', 'watch']
# Permissions to create resources in associated TriggerTemplates
- apiGroups: ['tekton.dev']
resources: ['pipelineruns', 'pipelineresources', 'taskruns']
verbs: ['create']
- apiGroups: ['']
resources: ['serviceaccounts']
verbs: ['impersonate']
- apiGroups: ['policy']
resources: ['podsecuritypolicies']
resourceNames: ['tekton-triggers']
verbs: ['use']
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: tekton-triggers-gitlab-binding
subjects:
- kind: ServiceAccount
name: tekton-build-sa
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: tekton-triggers-gitlab-minimal
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: tekton-triggers-gitlab-clusterrole
rules:
# EventListeners need to be able to fetch any clustertriggerbindings
- apiGroups: ['triggers.tekton.dev']
resources: ['clustertriggerbindings', 'clusterinterceptors']
verbs: ['get', 'list', 'watch']
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: tekton-triggers-gitlab-clusterbinding
subjects:
- kind: ServiceAccount
name: tekton-build-sa
namespace: default
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: tekton-triggers-gitlab-clusterrole

接着就可以来创建 EventListener 资源对象了,用来接收 Gitlab 的 Push Event 事件,如下所示:

# gitlab-listener.yaml
apiVersion: triggers.tekton.dev/v1beta1
kind: EventListener
metadata:
name: gitlab-listener # 该事件监听器会创建一个名为el-gitlab-listener的Service对象
spec:
serviceAccountName: tekton-build-sa
triggers:
- name: gitlab-push-events-trigger
interceptors:
- ref:
name: gitlab
params:
- name: secretRef # 引用 gitlab-secret 的 Secret 对象中的 secretToken 的值
value:
secretName: gitlab-secret
secretKey: secretToken
- name: eventTypes
value:
- Push Hook # 只接收 GitLab Push 事件
bindings: # 定义TriggerBinding,配置参数
- name: gitrevision
value: $(body.checkout_sha)
- name: gitrepositoryurl
value: $(body.repository.git_http_url)
template:
ref: gitlab-template

上面我们通过 TriggerBinding 定义了两个参数 gitrevisiongitrepositoryurl,这两个参数的值可以通过 Gitlab 发送过来的 POST 请求中获取到数据,然后我们就可以将这两个参数传递到 TriggerTemplate 对象中去,这里的模板其实也就是将上面我们定义的 PipelineRun 对象模板化而已,主要是替换 git_url 和镜像 TAG 这两个参数,如下所示:

# gitlab-template.yaml
apiVersion: triggers.tekton.dev/v1beta1
kind: TriggerTemplate
metadata:
name: gitlab-template
spec:
params: # 定义参数,和 TriggerBinding 中的保持一致
- name: gitrevision
- name: gitrepositoryurl
resourcetemplates: # 定义资源模板
- apiVersion: tekton.dev/v1beta1
kind: PipelineRun # 定义 pipeline 模板
metadata:
generateName: gitlab-run- # TaskRun 名称前缀
spec:
serviceAccountName: tekton-build-sa
pipelineRef:
name: pipeline
workspaces:
- name: go-repo-pvc
persistentVolumeClaim:
claimName: go-repo-pvc
params:
- name: git_url
value: $(tt.params.gitrepositoryurl)
- name: image
value: 'harbor.k8s.local/course/devops-demo:$(tt.params.gitrevision)'
- name: charts_dir
value: './helm'
- name: release_name
value: devops-demo
- name: release_namespace
value: 'kube-ops'
- name: overwrite_values
value: 'image.repository=harbor.k8s.local/course/devops-demo,image.tag=$(tt.params.gitrevision)'
- name: values_file
value: 'my-values.yaml'

直接创建上面新建的几个资源对象即可,这会创建一个 eventlistern 服务用来接收 Webhook 请求:

$ kubectl get eventlistener gitlab-listener
NAME ADDRESS AVAILABLE REASON READY REASON
gitlab-listener http://el-gitlab-listener.default.svc.cluster.local:8080 True MinimumReplicasAvailable True

所以一定还要记得在 Gitlab 仓库中配置上 Webhook:

771bfc2ad824

这样我们整个触发器和监听器就配置好了,接下来我们去修改下我们的项目代码,然后提交代码,正常提交过后就会在集群中创建一个 PipelinRun 对象用来执行我们的流水线了。

5d03e96085ca

$ kubectl get pipelinerun
NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME
gitlab-run-j77rx True Completed 4m46s 46s
$ curl devops-demo.k8s.local
{"msg":"Hello Tekton On Kubernetes"}

可以看到流水线执行成功后,应用已经成功部署了我们新提交的代码,到这里我们就完成了使用 Tekton 来重构项目的流水线。