【Kubernetes】Cloud NativeなCICDツールTektonの使い方まとめ
内容
Kubernetes上で動作するCloud NativeなCICDツールTektonに入門したので、簡単な使い方やセットアップについてまとめます。
Tektonとは
Tektonはk8s上で動作するCICDツールです。k8s上で動作するため、kubernetesクラスターに対してTektonのCRDをinstallし、yamlファイルにてCICDパイプラインを構築していきます。
Tektonを使うメリット
Tektonを使うメリットを紹介します。
自由度の高さ
自分たちが運用するKubernetes上で自由にPipelineを構築できるため、自由にPipelineをカスタマイズすることができます。
セキュリティ
Kubernetes上でPipelineを実行するため、Productionで動作するWorkflowを同じクラスター内で実行できます。外部のCICDツールを利用する場合、Production環境へのアクセス権を外部ツールに付与しないといけないため、セキュリティリスクが向上します。
スケーラビリティ
Kubernetes上で実行環境を用意するため、nodeを追加するだけでスケールします。
コスト面
ちゃんと運用した場合に限りますが、Kubernetes上でリソースをシェアして使えるので、managedサービスにお金を払う場合に比べて、コストが低くなる可能性があります。
カタログ
Tekton Catalog ( https://tekton.dev/docs/catalog/ ) を利用することで、自分でコーディングすることなく、様々なコンポーネントを組み合わせてPipelineを構成することができます。
Tektonを使うデメリット
もちろん、github actionsといった完全マネージドなCICDツールを使用する場合に比べて、Tektonを使うデメリットも存在します。
情報の少なさ
github actionsやCircleCIと比べ、Tektonに関する情報は少ないです。特に日本語の情報はほぼ見当たりません。
運用コストの高さ
Tektonのメリットである自由度の高さに付随してついてくるデメリットが運用コストの高さです。
github actionsの場合、基盤のアップデートをgithubサイドに任せることが出来ますが、Tektonを使用する場合、Tektonの実行環境であるKubernetesを始め、Tekton自体の運用を自分たちで行わなければなりません。
Tektonの動作イメージ
大雑把にTektonの動作フローを図示すると上記のようになります。(参考: https://tekton.dev/docs/getting-started/ )
Tektonの構成要素
上記図に表記されている要素を解説します。
Step
Tektonを構成する最小要素で、一つの実行単位を表します。例えば、「githubからソースをcloneする」「CloneしたソースをDocker Buildする」といった一つの処理の実行単位がStepです。
Task
Taskは複数のStepを一つのまとまりにしたものです。
- githubからソースをcloneする
- CloneしたソースをDocker Buildする
- BuildしたDocker ImageをDocker HubにPushする
例えば、上記3つのStepの順番を制御し、実行する単位がTaskです。
Kubernetes上で、TaskはPodとして管理され、StepはそのPodの中のContainerとして管理されます。
Pipeline
複数のTaskを束ねたものがPipelineになります。
- githubからソースをcloneして、Buildして、Docker HubにPushするTask
- Docker HubにPushされたImageをProduction環境にDeployするTask
- Deploy後、正常に動作しているかテストするTask
こういった連続性が必要な複数のTaskの順番を制御し実行する単位がPipelineです。 Task間での値の受け渡しも可能です。
Shared Workspace
一つのPipelineを通して、複数のTaskで共通のファイルを参照できるようにする機能がWorkspaceです。 Kubernetes上ではPersistent Volumeとして管理されています。
TaskRun
Taskの実行を制御する要素です。Taskを作成しただけだとTaskは実行されず、TaskRunをKubernetes上に作成することで、対象のTaskを実行することができます。また、TaskRunでTaskに渡したいParameterを指定することで、汎用的なTaskを様々なTaskRunで使い回すことが可能です。
PipelineRun
TaskRun同様に、Pipeleinの実行を制御する要素です。 Schedule機能もあるため、毎日17時に集計バッチ用のPipelineを実行するといったことも可能です。
Tektonの周辺ツール
Tektonを使いやすくするツール群を紹介します。
Tekton CLI
Tektonの操作をしやすくするためのCLI(TektonはKubernetesAPIを操作する形で実行可能なので、CLIがなくても、kubectl等で構築・実行可能です。)
install方法
$ brew install tektoncd-cli
Tekton Dashboard
Tektonで実行したPipelineのログ等を可視化して表示できるツール
Tekton Triggers
github上のソースが変更された際に、githubからTektonで公開されたendpointをキックして、TektonのPipelineを実行する。といった、ユースケースを可能にするのがTekton Triggersです。
Tektonのinstall方法
TektonをKubernetesクラスターにinstallします。
Tekton Pipeline
Tektonを使用するために必要なコアファイルをinstallします。
$ kubectl apply --filename https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml
Tekton Triggers
TektonのEvent周りを制御するために必要なmanifestファイルをinstallします。
$ 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
Tekton Dashboard
Tektonで実行したPipeline等をUI上で確認するためのツールをinstallします。
$ kubectl apply --filename https://storage.googleapis.com/tekton-releases/dashboard/latest/release-full.yaml
ブラウザで確認する方法
$ kubectl port-forward -n tekton-pipelines service/tekton-dashboard 9097:9097
# http://localhost:9097 にてアクセス可能
実際にTektonでPipelineを構築してみる
実際にTektonでPipelineを構築します。
Taskの作成と実行
標準出力に Hello!
という文字列を出力するTaskを作成します。
hello-task.yaml
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: hello-task
spec:
steps:
- name: echo-hello
image: alpine
script: |
#!/bin/sh
echo "Hello!"
alpineのベースイメージ上でshellを実行するシンプルなTaskです。
$ kubectl apply -f hello-task.yaml
task.tekton.dev/hello-task created
$ kubectl get task
NAME AGE
hello-task 26s
正常にTektonのTaskが作成されました。次にこのTaskを実行するためにTaskRunを作成します。
hello-taskrun.yaml
apiVersion: tekton.dev/v1beta1
kind: TaskRun
metadata:
generateName: hello-taskrun-
spec:
taskRef:
name: hello-task
※generateName
を使用することで、suffixにランダム文字列を追加することができます。これを付与することで、同じTaskRunを何回実行しても名前が重複して実行できないということを防ぐことができます。
$ kubectl create -f hello-taskrun.yaml
taskrun.tekton.dev/hello-taskrun-nbqtb created
$ kubectl get taskrun
NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME
hello-taskrun-nbqtb True Succeeded 29s 19s
正常にTaskRunが作成されたので、hello-taskが実行されているはずです。正常に実行されたことを確認するために、logを確認します。
$ kubectl logs --selector=tekton.dev/taskRun=hello-taskrun-nbqtb
Defaulted container "step-echo-hello" out of: step-echo-hello, prepare (init), place-scripts (init)
Hello!
標準出力にHello!
という文字列が出力されています!
Taskにparamsを渡す
次に先ほど作成したTaskにparamsを渡せるようにし、Hello!
ではなく、指定した文字列を標準出力に出力できるようにします。
hello-task.yaml
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: hello-task
spec:
params:
- name: username
type: string
description: The name of user
default: Tom
steps:
- name: echo-hello
image: alpine
script: |
#!/bin/sh
echo "Hello $(params.username)!"
このTaskはusernameというparamsを引数に取り、その値を標準出力に出力します。
このTaskにusername paramを渡して呼び出せるようにTaskRunを修正します。
apiVersion: tekton.dev/v1beta1
kind: TaskRun
metadata:
generateName: hello-taskrun-
spec:
taskRef:
name: hello-task
params:
- name: username
value: Yossy
# apply task
$ kubectl apply -f hello-task.yaml
# create task run
$ kubectl create -f hello-taskrun.yaml
taskrun.tekton.dev/hello-taskrun-7c472 created
$ kubectl get taskrun
NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME
hello-taskrun-59v8r True Succeeded 20s 13s
$ kubectl logs --selector=tekton.dev/taskRun=hello-taskrun-59v8r
Defaulted container "step-echo-hello" out of: step-echo-hello, prepare (init), place-scripts (init)
Hello Yossy!
これで、paramsに値を受け取って、その値を処理に利用する汎用的なTaskが作成できました。
複数のTaskをPipelineで接続する
次に、複数のTaskをPipelineでまとめて、順に実行していきます。
まずは、Hello
ではなく、Goodbye
と出力するTaskを作成します。
goodbye-task.yaml
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: goodbye-task
spec:
params:
- name: username
type: string
description: The name of user
default: Belle
steps:
- name: echo-goodbye
image: alpine
script: |
#!/bin/sh
echo "GoodBye $(params.username)!"
$ kubectl apply -f goodbye-task.yaml
task.tekton.dev/goodbye-task created
次に、2つのTaskを順番に実行できるようにPipelineを作成します。
$ touch pipeline.yaml
pipeline.yaml
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: pipeline
spec:
params:
- name: hello-name
type: string
description: hello name
default: Bob
- name: goodbye-name
type: string
description: goodbye name
default: Kate
tasks:
- name: hello
taskRef:
name: hello-task
params:
- name: username
value: "$(params.hello-name)"
- name: goodbye
taskRef:
name: goodbye-task
params:
- name: username
value: "$(params.goodbye-name)"
runAfter:
- hello
$ kubectl apply -f pipeline.yaml
pipeline.tekton.dev/pipeline created
$ touch pipeline-run.yaml
pipeline-run.yaml
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
generateName: pipelinerun-
spec:
pipelineRef:
name: pipeline
$ kubectl create -f pipeline-run.yaml
pipelinerun.tekton.dev/pipelinerun-655k2 created
$ kubectl get pipelinerun
NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME
pipelinerun-655k2 True Succeeded 52s 15s
$ kubectl describe pipelinerun pipelinerun-655k2
Name: pipelinerun-655k2
Namespace: default
Labels: tekton.dev/pipeline=pipeline
Annotations: <none>
API Version: tekton.dev/v1
Kind: PipelineRun
Metadata:
Creation Timestamp: 2023-01-27T09:04:26Z
Generate Name: pipelinerun-
Generation: 1
...
Spec:
Pipeline Ref:
Name: pipeline
Task Run Template:
Service Account Name: default
Timeouts:
Pipeline: 1h0m0s
Status:
Child References:
API Version: tekton.dev/v1beta1
Kind: TaskRun
Name: pipelinerun-655k2-hello
Pipeline Task Name: hello
API Version: tekton.dev/v1beta1
Kind: TaskRun
Name: pipelinerun-655k2-goodbye
Pipeline Task Name: goodbye
Completion Time: 2023-01-27T09:05:03Z
Conditions:
Last Transition Time: 2023-01-27T09:05:03Z
Message: Tasks Completed: 2 (Failed: 0, Cancelled 0), Skipped: 0
Reason: Succeeded
Status: True
Type: Succeeded.
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Started 16m PipelineRun
Normal Running 16m PipelineRun Tasks Completed: 0 (Failed: 0, Cancelled 0), Incomplete: 2, Skipped: 0
Normal Running 15m (x2 over 15m) PipelineRun Tasks Completed: 1 (Failed: 0, Cancelled 0), Incomplete: 1, Skipped: 0
Normal Succeeded 15m PipelineRun Tasks Completed: 2 (Failed: 0, Cancelled 0), Skipped: 0
2つのTaskが実行されていることが確認できました。
Taskの結果を他のTaskに引き継ぐ
次に、連続したTaskで、先に実行したタスクの結果を次のTaskに引き渡す実装を行います。
Taskの結果を他のTaskに引き継ぎたい場合、result
をつかいます。
hello-taskの結果をresultで出力する
hello-task.yaml
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: hello-task
spec:
results:
- name: output-value
description: Output Value
params:
- name: username
type: string
description: The name of user
default: Tom
steps:
- name: echo-hello
image: alpine
script: |
#!/bin/sh
echo "Hello $(params.username)!" | tee $(results.output-value.path)
上記のような形で、reuslts
paramsを追加し、results.output-value.path
に出力したい値を代入することで、他のタスクにこのタスクの結果を引き渡すことができます。
これで、hello-taskの結果を他のタスクに引き渡す準備が出来ました。Pipelineの設定を変更して、出力内容をgoodbye-taskに引き渡します。
pipeline.yaml
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: pipeline
spec:
params:
- name: hello-name
type: string
description: hello name
default: Bob
- name: goodbye-name
type: string
description: goodbye name
default: Kate
tasks:
- name: hello
taskRef:
name: hello-task
params:
- name: username
value: "$(params.hello-name)"
- name: goodbye
taskRef:
name: goodbye-task
params:
- name: username
value: $(tasks.hello.results.output-value)
runAfter:
- hello
goobey-taskのusernameのvalueをhello-taskの出力に変更しました。この状態でこのPipelineを実行します。
$ kubectl apply -f hello-task.yaml
task.tekton.dev/hello-task configured
$ kubectl apply -f pipeline.yaml
pipeline.tekton.dev/pipeline configured
$ kubectl create -f pipeline-run.yaml
pipelinerun.tekton.dev/pipelinerun-jjjrx created
$ kubectl get pipelinerun
NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME
pipelinerun-dbkkq True Succeeded 22s 10s
# pipelineのgoodbyeタスクのlogを確認する
$ kubectl logs pipelinerun-dbkkq-goodbye-pod
Defaulted container "step-echo-goodbye" out of: step-echo-goodbye, prepare (init), place-scripts (init)
GoodBye Hello Bob!
!
以上のような、helloタスクの出力結果がgoodbyeタスクに引き渡されていることがわかります。
Dashboardで確認する
最後に、今回実験した内容をDashboardで確認します。
$ kubectl port-forward svc/tekton-dashboard -n tekton-pipelines 9097:9097
port-forwardでDashboardにローカルから接続できるようにします。
上記のように実行結果がブラウザで確認できます。(テスト実行したものがたくさんあってすみません...)
まとめ
CloudNativeなCICDパイプラインTektonの基本的な使い方を紹介しました。
github actionsやCircleCIといったマネージドなCICDツールと違い、自分でサーバーを管理する手間が増えますが、自分達でサーバーの面倒を見るからこそ得られる自由度の高さがTektonの魅力だと思います。
CICDパイプラインは開発効率に大きく影響する重要な工程です。最適なツールを選定することが重要かと思います。
note
勉強法やキャリア構築法など、エンジニアに役立つ記事をnoteで配信しています。
Discussion