GitLab 觸發(fā) Tekton 任務(wù)構(gòu)建
前面我們都是通過創(chuàng)建一個 TaskRun 或者一個 PipelineRun 對象來觸發(fā)任務(wù),但是在實際的工作中更多的是開發(fā)人員提交代碼過后來觸發(fā)任務(wù),這個時候就需要用到 Tekton 里面的 Triggers 概念了。
Tekton Triggers Workflow
Triggers 同樣通過下面的幾個 CRD 對象對 Tekton 進(jìn)行了一些擴(kuò)展:
- TriggerTemplate: 創(chuàng)建資源的模板,比如用來創(chuàng)建 PipelineResource 和 PipelineRun
- TriggerBinding: 校驗事件并提取相關(guān)字段屬性
- ClusterTriggerBinding: 和 TriggerBinding 類似,只是是全局的
- Interceptor: 處理事件以進(jìn)行自定義驗證或過濾
- EventListener: 連接 TriggerBinding 和 TriggerTemplate 到事件接收器,使用從各個 TriggerBinding 中提取的參數(shù)來創(chuàng)建 TriggerTemplate 中指定的 resources,同樣通過 interceptor 字段來指定外部服務(wù)對事件屬性進(jìn)行預(yù)處理
同樣要使用 Tekton Triggers 就需要安裝對應(yīng)的控制器,可以直接通過 tektoncd/triggers 的 GitHub 倉庫說明進(jìn)行安裝,如下所示的命令(需要注意 v0.14.2 版本需要安裝兩個資源清單):
- kubectl apply --filename https://storage.googleapis.com/tekton-releases/triggers/previous/v0.14.2/release.yaml
- kubectl apply -f https://storage.googleapis.com/tekton-releases/triggers/previous/v0.14.2/interceptors.yaml
可以使用如下命令查看 Triggers 的相關(guān)組件安裝狀態(tài),直到都為 Running 狀態(tài):
- $ kubectl get pods --namespace tekton-pipelines
- NAME READY STATUS RESTARTS AGE
- tekton-dashboard-6fcbd956f4-vvlrz 1/1 Running 4 15d
- tekton-pipelines-controller-795dd94d96-lkbxt 1/1 Running 5 17d
- tekton-pipelines-webhook-6b8964445d-mp4k6 1/1 Running 5 17d
- tekton-triggers-controller-989875ff7-tlc4v 1/1 Running 0 3h16m
- tekton-triggers-core-interceptors-6494c8cfc4-hb8sk 1/1 Running 0 2m10s
- tekton-triggers-webhook-787849d8db-ch28w 1/1 Running 0 3h16m
現(xiàn)在我們來將前面的 Jenkins Pipeline 流水線轉(zhuǎn)換成使用 Tekton 來構(gòu)建,代碼我們已經(jīng)推送到了私有倉庫 GitLab,地址為:http://git.k8s.local/course/devops-demo.git。
首先我們需要完成觸發(fā)器的配置,當(dāng)我們提交源代碼到 GitLab 的時候,需要觸發(fā) Tekton 的任務(wù)運(yùn)行,所以首先需要完成這個觸發(fā)器。這里就可以通過 EventListener 這個資源對象來完成,創(chuàng)建一個名為 gitlab-listener 的 EventListener 資源對象,文件內(nèi)容如下所示:
- # gitlab-push-listener.yaml
- apiVersion: triggers.tekton.dev/v1alpha1
- kind: EventListener
- metadata:
- name: gitlab-listener # 該事件監(jiān)聽器會創(chuàng)建一個名為el-gitlab-listener的Service對象
- spec:
- serviceAccountName: tekton-triggers-gitlab-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:
- - ref: devops-demo-binding
- template:
- ref: devops-demo-template
由于 EventListener 創(chuàng)建完成后會生成一個 Listener 的服務(wù),用來對外暴露用于接收事件響應(yīng),比如上面我們創(chuàng)建的對象名為 gitlab-listener,創(chuàng)建完成后會生成一個名為 el-gitlab-listener 的 Service 對象,由于我們 GitLab 本身就在集群內(nèi)部,所以我們用 Service 的 DNS 形式來訪問 EventListener 即可,如果你想暴露到集群外部則可以使用 NodePort 或者 Ingress 的形式。
另外需要注意的是在上面的 EventListener 對象中我們添加了 interceptors 屬性,其中有一個內(nèi)置的 gitlab 攔截器,GitLab 攔截器包含驗證和過濾來自 GitLab 的請求邏輯, 比如我們可以配置 WebHook 的 Secret Token,可以通過 Secret 對象引入進(jìn)來:
- interceptors:
- - ref:
- name: gitlab
- params:
- - name: secretRef # 引用 gitlab-secret 的 Secret 對象中的 secretToken 的值
- value:
- secretName: gitlab-secret
- secretKey: secretToken
- - name: eventTypes
- value:
- - Push Hook # 只接收 GitLab Push 事件
對應(yīng)的 Secret 資源對象如下所示,一個用于 WebHook 的 Secret Token,另外一個是用于 GitLab 登錄認(rèn)證使用的:
- # 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: root
- password: admin321
由于 EventListener 對象需要訪問其他資源對象,所以需要聲明 RBAC,如下所示:
- # event-listener-rbac.yaml
- apiVersion: v1
- kind: ServiceAccount
- metadata:
- name: tekton-triggers-gitlab-sa
- secrets:
- - name: gitlab-secret
- - name: gitlab-auth
- - name: harbor-auth
- ---
- 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-triggers-gitlab-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-triggers-gitlab-sa
- namespace: default
- roleRef:
- apiGroup: rbac.authorization.k8s.io
- kind: ClusterRole
- name: tekton-triggers-gitlab-clusterrole
然后接下來就是最重要的 TriggerBinding 和 TriggerTemplate 對象了,我們在上面的 EventListener 對象中將兩個對象組合在一起:
- bindings:
- - ref: gitlab-push-binding # TriggerBinding 對象
- template:
- ref: gitlab-echo-template # TriggerTemplate 對象
這樣就可以將 TriggerBinding 中的參數(shù)傳遞到 TriggerTemplate 對象中進(jìn)行模板化。比如這里我們定義一個如下所示的 TriggerBinding 對象:
- apiVersion: triggers.tekton.dev/v1alpha1
- kind: TriggerBinding
- metadata:
- name: devops-demo-binding
- spec:
- params:
- - name: gitrevision
- value: $(body.checkout_sha)
- - name: gitrepositoryurl
- value: $(body.repository.git_http_url)
這里需要注意的是參數(shù)的值我們是通過讀取 GitLab WebHook 發(fā)送過來的數(shù)據(jù)值,通過 $() 包裹的 JSONPath 表達(dá)式來提取的,關(guān)于表達(dá)式的更多用法可以查看官方文檔說明,至于能夠提取哪些參數(shù)值,則可以查看 WebHook 的說明,比如這里我們是 GitLab Webhook 的 Push Hook,對應(yīng)的請求體數(shù)據(jù)如下所示:
- {
- "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": []
- },
- {
- "id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
- "message": "fixed readme",
- "title": "fixed readme",
- "timestamp": "2012-01-03T23:36:29+02:00",
- "url": "http://example.com/mike/diaspora/commit/da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
- "author": {
- "name": "GitLab dev user",
- "email": "gitlabdev@dv6700.(none)"
- },
- "added": ["CHANGELOG"],
- "modified": ["app/controller/application.rb"],
- "removed": []
- }
- ],
- "total_commits_count": 4
- }
請求體中的任何屬性都可以提取出來,作為 TriggerBinding 的參數(shù),如果是其他的 Hook 事件,對應(yīng)的請求體結(jié)構(gòu)可以查看 GitLab 文檔說明。
這樣我們就可以在 TriggerTemplate 對象中通過參數(shù)來讀取上面 TriggerBinding 中定義的參數(shù)值了,定義一個如下所示的 TriggerTemplate 對象,聲明一個 TaskRun 的模板,定義的 Task 任務(wù)也非常簡單,只需要在容器中打印出代碼的目錄結(jié)構(gòu)即可:
- apiVersion: triggers.tekton.dev/v1alpha1
- kind: TriggerTemplate
- metadata:
- name: devops-demo-template
- spec:
- params: # 定義參數(shù),和 TriggerBinding 中的保持一致
- - name: gitrevision
- - name: gitrepositoryurl
- resourcetemplates: # 定義資源模板
- - apiVersion: tekton.dev/v1beta1
- kind: TaskRun # 定義 TaskRun 模板
- metadata:
- generateName: gitlab-run- # TaskRun 名稱前綴
- spec:
- serviceAccountName: tekton-triggers-gitlab-sa
- taskSpec: # Task 任務(wù)聲明
- resources:
- inputs: # 定義一個名為 source 的 git 輸入資源
- - name: source
- type: git
- steps:
- - name: show-path
- image: ubuntu # 定義一個執(zhí)行步驟,列出代碼目錄結(jié)構(gòu)
- script: |
- #! /bin/bash
- ls -la $(resources.inputs.source.path)
- resources: # 聲明具體的輸入資源參數(shù)
- inputs:
- - name: source # 和 Task 中的資源名保持一直
- resourceSpec: # 資源聲明
- type: git
- params:
- - name: revision
- value: $(tt.params.gitrevision) # 讀取參數(shù)值
- - name: url
- value: $(tt.params.gitrepositoryurl)
需要注意在最后的 pipelineresource 中引用參數(shù)值的時候使用了一個 tt 的前綴。定義完過后,直接創(chuàng)建上面的資源對象,創(chuàng)建完成后會自動生成 EventListener 的 Pod 和 Service 對象:
- $ kubectl get svc -l eventlistener=gitlab-listener
- NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
- el-gitlab-listener ClusterIP 10.108.146.82 <none> 8080/TCP 7m56s
- $ kubectl get pod -l eventlistener=gitlab-listener
- NAME READY STATUS RESTARTS AGE
- el-gitlab-listener-6b84cc6d8f-ppfhp 1/1 Running 0 7m9s
- $ kubectl get eventlistener
- NAME ADDRESS AVAILABLE REASON READY REASON
- gitlab-listener http://el-gitlab-listener.default.svc.cluster.local:8080 False MinimumReplicasUnavailable False
接下來我們就可以到 GitLab 的項目中配置 WebHook,注意需要配置 Secret Token,我們在上面的 Secret 對象中聲明過:
Secret Token
創(chuàng)建完成后,我們可以測試下該 WebHook 的 Push events 事件,直接點(diǎn)擊測試即可(如果使用自定義的域名則需要在 coredns 中添加映射),正常會返回 Hook executed successfully: HTTP 202 的提示信息,這個時候在 Kubernetes 集群中就會出現(xiàn)如下所示的任務(wù) Pod:
- $ kubectl get pods -l triggers.tekton.dev/eventlistener=gitlab-listener
- NAME READY STATUS RESTARTS AGE
- gitlab-run-lnqzv-pod-lp8lw 0/2 Completed 0 3m18s
- $ kubectl get taskrun -l triggers.tekton.dev/eventlistener=gitlab-listener
- NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME
- gitlab-run-lnqzv True Succeeded 3m11s 2m48s
- $ tkn taskrun logs gitlab-run-lnqzv
- [git-source-source-hfrw6] {"level":"info","ts":1624446684.0399015,"caller":"git/git.go:169","msg":"Successfully cloned http://git.k8s.local/course/devops-demo.git @ 581b1986b6c038ca98a362e6a0b8e9acb55893e8 (grafted, HEAD) in path /workspace/source"}
- [git-source-source-hfrw6] {"level":"info","ts":1624446684.0660462,"caller":"git/git.go:207","msg":"Successfully initialized and updated submodules in path /workspace/source"}
- [show-path] total 36
- [show-path] drwxr-xr-x 4 root root 163 Jun 23 11:11 .
- [show-path] drwxrwxrwx 3 root root 20 Jun 23 11:11 ..
- [show-path] -rw-r--r-- 1 root root 1804 Jun 23 11:11 .drone.yml
- [show-path] drwxr-xr-x 8 root root 177 Jun 23 11:11 .git
- [show-path] -rw-r--r-- 1 root root 192 Jun 23 11:11 .gitignore
- [show-path] -rw-r--r-- 1 root root 375 Jun 23 11:11 Dockerfile
- [show-path] -rw-r--r-- 1 root root 5101 Jun 23 11:11 Jenkinsfile
- [show-path] -rw-r--r-- 1 root root 174 Jun 23 11:11 README.md
- [show-path] -rw-r--r-- 1 root root 97 Jun 23 11:11 go.mod
- [show-path] -rw-r--r-- 1 root root 3370 Jun 23 11:11 go.sum
- [show-path] drwxr-xr-x 3 root root 101 Jun 23 11:11 helm
- [show-path] -rw-r--r-- 1 root root 471 Jun 23 11:11 main.go
到這里我們就完成了通過 GitLab 的 Push 事件來觸發(fā) Tekton 的一個任務(wù)。
gitlab push events
接下來我們再來實現(xiàn)將我們的應(yīng)用通過 Tekton 來自動部署到 Kubernetes 集群中。