Tekton Triggerを作成しCurlでビルドを実行してみる
はじめに
前々回及び前回に引き続きGKE AutopilotとTektonを組合わせてスケーラブルなCIを作っていきます。今まではtaskrunマニフェストを手動で投げる実行していましたが、実際は外部からリクエストされたり、コードがPushされたとき、などイベントに応じてリアクティブに実行したいですよね? 継続的ではないビルドはCI(Continuous Untegration) ではありません><
もちろん、Tektonでもそのあたりはサポートしています。今回はまずはCurlでWebhookを叩くサンプルで基本的な挙動を理解し、次回にGitHubへのPushに反応するCIを構築したいと思います。
環境準備
まずは以前と同じくGKE Autopilotで環境を作成します。
追加でTekton Trigerに必要な2つのマニフェストも適用します。
kubectl apply --filename https://storage.googleapis.com/tekton-releases/triggers/latest/release.yaml
kubectl apply --filename https://storage.googleapis.com/tekton-releases/triggers/latest/interceptors.yaml
最後に前回まではNamespaceはデフォルトにしていましたが、より実用的な構成を目指して今回からはNamespaceを指定します。k8sにおけるNamespaceは簡単に言えばリソースの論理的な仕切りなので、複数のワークロードを一つのクラスタに乗せる時には必須の設定かと思います。
以下のようにNamespaceを作成しデフォルトで利用するようにコンテキストの作成とデフォルトの切替えをします。
kubectl create ns tekton-workers
kubectl config set-context tekton-workers --namespace tekton-workers --cluster $(kubectl config current-context) --user=$(kubectl config current-context)
kubectl config use-context tekton-workers
kubectl config get-contexts
これでtekton-workers
というネームスペースが作られ、特に指定しなければこちらにTaskやTriggerといったワーカー系のリソースが登録されます。
$ kubectl get ns
NAME STATUS AGE
default Active 30m
kube-node-lease Active 30m
kube-public Active 30m
kube-system Active 30m
tekton-pipelines Active 11m
tekton-workers Active 2m23s
TaskとPipelineの作成
まずは、トリガーで実行する対象を作成します。今回は非常にシンプルな構成で、単一のTaskを持つPipelineで、Taskは渡されたパラメータを元に標準出力にメッセージ吐くだけとなっています。
Taskは以下の内容をtask-myecho.yaml
という名前で保存します。
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: my-echo
description: A simple echo task.
spec:
params:
- name: name
type: string
description: Your Name for print.
- name: message
type: string
description: This is a message for print.
steps:
- name: echo
image: alpine
script: |
#!/usr/bin/env sh
echo "================================"
echo "My Echo Task"
echo "================================"
echo "Hi, $(params.name)"
echo "$(params.message)"
続いて上記のTaskを含むPipelineは以下の内容でpipeline.yaml
という名前で保存します。
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: pipeline-echo
spec:
description: |
This pipeline is a simple echo to STDOUT.
params:
- name: name
type: string
description: Your Name for print.
- name: message
type: string
description: This is a message for print.
tasks:
- name: echo
taskRef:
name: my-echo
params:
- name: name
value: "$(params.name)"
- name: message
value: "$(params.message)"
以下のようにTaskとPipelineを登録します。
$ kubectl apply -f task-myecho.yaml
$ kubectl apply -f pipeline.yaml
$ tkn task list
NAME DESCRIPTION AGE
my-echo 37 seconds ago
$ tkn pipeline list
NAME AGE LAST RUN STARTED DURATION STATUS
pipeline-echo 29 seconds ago --- --- --- ---
サービスアカウントの作成とRBACの設定
今回は以下の2つのサービスアカウントを利用します。
- sa-buildbot
- sa-trigger
sa-buildbot
はPipelineやTaskといったビルダーに付与するアカウントとして作成します。通常はGitHubなどのクレデンシャルを紐づけますが、今回は特に意味のある権限は付けません。以下をsa-buildbot.yaml
の名前で保存してください。
apiVersion: v1
kind: ServiceAccount
metadata:
name: sa-buildbot
sa-trigger
はTriggerの実行のためのサービスアカウントです。こちらはRBACによりトリガーの実行に必要な各種権限を付けておきます。以下をsa-tekton-el.yaml
の名前で保存します。
#
# Service Account
#
apiVersion: v1
kind: ServiceAccount
metadata:
name: sa-trigger
---
#
# Role
#
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: role-trigger
rules:
- apiGroups:
- triggers.tekton.dev
resources:
- eventlisteners
- triggers
- triggerbindings
- triggertemplates
verbs:
- get
- list
- watch
- apiGroups:
- tekton.dev
resources:
- pipelineruns
- pipelineresources
verbs:
- create
- apiGroups:
- ""
resources:
- configmaps
verbs:
- get
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: triggers-role-binding
subjects:
- kind: ServiceAccount
name: sa-trigger
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: role-trigger
---
#
# Cluster Role
#
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: clusterrole-trigger
rules:
- apiGroups: ["triggers.tekton.dev"]
resources: ["clustertriggerbindings", "clusterinterceptors"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: clusterbinding-trigger
subjects:
- kind: ServiceAccount
name: sa-trigger
namespace: tekton-workers
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: clusterrole-trigger
上記のマニフェストを適用してサービスアカウントを確認します。
$ kubectl apply -f sa-buildbot.yaml
$ kubectl apply -f sa-tekton-el.yaml
$ kubectl get sa -n tekton-workers
NAME SECRETS AGE
default 1 160m
sa-buildbot 1 15s
sa-trigger 1 10s
トリガー/イベントリスナーの登録
いよいよ本題のトリガーの登録です。今回使うのは以下の3つです。
- TriggerTemplate
- TriggerBinding
- EventListener
TriggerTemplate
はPipelineを実行する実態やコンテキストを書きます。TaskRunやPipelineRunに似ていますね。TriggerBinding
はHTTPで受けとった値をTektonのパラメータに変換する事が出来ます。最後にEventListener
は、前者2つをマッピングした常駐のサービスです。
今回は最終的には、こちらのtrigger-curl.yamlに1ファイルに統合しています。
TriggerTemplate
TriggerTemplate
ではPipelineを実行するためのPipelineRunやその名前の自動生成、実行するサービスアカウントを記述する事が出来ます。resourcetemplates
側でパラメータを参照する際にtt(trigger template).
を付ける必要があることに注意をしてください。
apiVersion: triggers.tekton.dev/v1alpha1
kind: TriggerTemplate
metadata:
name: template-curl-trigger
spec:
params:
- name: name
description: Your Name for print.
- name: message
description: This is a message for print.
default: hello, world
resourcetemplates:
- apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
# Autogenerate Pipeline Run Name with unique ID as pipeline-run-curl-{Rondom ID}.
generateName: pipeline-run-curl-
spec:
pipelineRef:
name: pipeline-echo
params:
# `tt` is `trigger template`
- name: name
value: $(tt.params.name)
- name: message
value: $(tt.params.message)
serviceAccountName: sa-buildbot
TriggerBinding
TriggerBinding
ではHTTPで受け取った値をTektonのパラメータにマッピングします。下記では以下のようなJSONをBody要素にとった場合に変換しています。
{
"user": {
"name": "koduki"
},
"greeting": {
"message": "Hello"
}
}
マッピングするさいはシンプルに.
で繋げるだけです。Body以外にもHeader情報なども取得でき、詳しくは公式ページを参考にすると良いと思います。
apiVersion: triggers.tekton.dev/v1alpha1
kind: TriggerBinding
metadata:
name: binding-curl-trigger
spec:
params:
# Binding from HTTP Rquests to Tekton values
- name: name
value: $(body.user.name)
- name: message
value: $(body.greeting.message)
EventListener
EventListener
はTriggerTemplateとBindingをマッピングし、イベントを受け付けるServiceを作成します。このマニフェストから自動的にel-{EventListener Name}
でServiceも作られるのでHTTPリクエスト等はそこに投げる事になります。
apiVersion: triggers.tekton.dev/v1alpha1
kind: EventListener
metadata:
name: curl-for-echo
spec:
serviceAccountName: sa-trigger
triggers:
- bindings:
- ref: binding-curl-trigger
template:
ref: template-curl-trigger
トリガーの登録
さて、それでは登録していきましょう。3つのマニフェストファイルを作ってそれぞれ実行しても良いのですが関連性が深いのでtrigger-curl.yamlとして1ファイルにまとめています。
$ kubectl apply -f trigger-curl.yaml
$ tkn eventlistener list
NAME AGE URL AVAILABLE
curl-for-echo 11 minutes ago http://el-curl-for-echo.tekton-workers.svc.cluster.local:8080 True
$ tkn triggerbinding list
NAME AGE
binding-curl-trigger 11 minutes ago
$ tkn triggertemplate list
NAME AGE
template-curl-trigger 12 minutes ago
tkn-cliで各種登録された事が分かりますね! また以下のようにel-{イベントリスナー名}
でサービスが作成されているのも分かります。
$ kubectl get services -n tekton-workers
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
el-curl-for-echo ClusterIP 10.71.202.119 <none> 8080/TCP,9000/TCP 16m
これでトリガーのための準備は完了です。
curlでWebhookを実行
それではいよいよ、curl
でWebhookをキックしてみます。まず、サービスのURLをアクセス可能にしてやる必要がありますので今回はkubectlでport-fowardします。本来はEgressなどの設定を行い外部から見えるようにしてやる必要があります。
$ kubectl port-forward service/el-curl-for-echo 8081:8080 -n tekton-workers
Forwarding from 127.0.0.1:8081 -> 8080
Forwarding from [::1]:8081 -> 8080
続いてcurlを実行します。以下のようなJSONをデータとして持ちそれが先ほどのTriggerBinding
のインプットとなります。
$ curl -X POST -H 'Content-Type: application/json' http://localhost:8081 -d '{"user":{"name": "koduki"},"greeting":{"message": "Hello"}}'
{"eventListener":"curl-for-echo","namespace":"tekton-workers","eventListenerUID":"7ae72c57-a190-45e9-90d4-d736d216e2db","eventID":"6308c090-b1f7-4d46-ac33-b95cfe3ae988"}
$ tkn pipeline list
NAME AGE LAST RUN STARTED DURATION STATUS
pipeline-echo 48 minutes ago pipeline-run-curl-9cd9f 36 seconds ago --- Running
無事pipeline-echo
が実行中になりました。pipelinerunの名前がpipeline-run-curl-{自動ID}
と先ほど指定したルール通りに作成されているのが分かります。
実行が完了すると以下のように表示されます。ちゃんとcurlで指定したJSONの中身が反映されている事が分かりますね。
$ tkn pipeline logs
================================
My Echo Task
================================
Hi, koduki
Hello
まとめ
今回はTekton Triggerを使って外部からのリクエストを受け取ってビルドをする方法に関して記載しました。これが出来れば大抵の仕組みは作りこみが出来そうなので、社内システムやマイナーなパッケージとの連携もしやすくなると思います。
とはいえ 「簡単なことを簡単に」 のポリシーで良く使うGithubとの連携などは最初から用意された仕組みがあります。次回はその辺を記事に出来ればと思います。
それではHappy Hacking!
参考
Discussion