tektonのGetStartedをやりました
tektonとは
tektonはk8s上でCICD環境を構築することができるミドルウェア。
以下のような概念がある。
- task: 最小の実行単位で
- pipeline: taskを束ねる単位
- trigger: Webhookエンドポイントで待ち受けて、それを契機にpipelineを実行する
なお、taskやpipelineを実行する場合は、taskrunやpipelinerunのリソースを作ることで実行される。そのrun系リソースは実行ステータスやログなどを管理している。
詰まることろ、毎回pipeline本体を作成するとかそういうことはしないということ。
また、GithubActionsと比べると、以下のようになると思います。
- step: step
- task : job
- pipeline: workflow
tekton実行環境を構築
操作コマンドをインストール
yay -S tkn
ローカルクラスタを起動
kind create cluster
tekton pipelineに必要なリソース一式をデプロイする
kubectl apply --filename https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml
kubectl get pods --namespace tekton-pipelines --watch
❯ kubectl get pods --namespace tekton-pipelines
NAME READY STATUS RESTARTS AGE
tekton-events-controller-6d5d95b4-wplsd 1/1 Running 0 6m12s
tekton-pipelines-controller-585bbf56fb-82k9p 1/1 Running 0 6m12s
tekton-pipelines-webhook-5886749497-sdppr 1/1 Running 0 6m12s
Taskを実行
echoするだけのタスクを作成
cat <<EOF > hello-world.yaml
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: hello
spec:
steps:
- name: echo
image: alpine
script: |
#!/bin/sh
echo "Hello World"
EOF
kubectl apply --filename hello-world.yaml
$ kubectl get task hello
NAME AGE
hello 5m8s
cat <<EOF > hello-world-run.yaml
apiVersion: tekton.dev/v1beta1
kind: TaskRun
metadata:
name: hello-task-run
spec:
taskRef:
name: hello
EOF
kubectl apply --filename hello-world-run.yaml
taskrunを見ると、Successedになっている。
$ kubectl get taskrun
NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME
hello-task-run True Succeeded 2m52s 75s
logを見ると、Hello Worldと出ている。
$ kubectl logs --selector=tekton.dev/taskRun=hello-task-run
Defaulted container "step-echo" out of: step-echo, prepare (init), place-scripts (init)
Hello World
Pipelineを実行
2つめのタスクを追加で作成
cat <<EOF > goodbye-world.yaml
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: goodbye
spec:
params:
- name: username
type: string
steps:
- name: goodbye
image: ubuntu
script: |
#!/bin/bash
echo "Goodbye \$(params.username)!"
EOF
kubectl apply --filename goodbye-world.yaml
$ kubectl get task
NAME AGE
goodbye 33s
hello 15m
cat <<EOF > hello-goodbye-pipeline.yaml
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: hello-goodbye
spec:
params:
- name: username
type: string
tasks:
- name: hello
taskRef:
name: hello
- name: goodbye
runAfter:
- hello
taskRef:
name: goodbye
params:
- name: username
value: \$(params.username)
EOF
cat <<EOF > hello-goodbye-pipeline-run.yaml
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
name: hello-goodbye-run
spec:
pipelineRef:
name: hello-goodbye
params:
- name: username
value: "Tekton"
EOF
pipelineを実行する
kubectl apply --filename hello-goodbye-pipeline-run.yaml
$ kubectl get pipelineruns.tekton.dev
NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME
hello-goodbye-run Unknown Running 3s
tkn pipelinerun logs hello-goodbye-run -f -n default
ログを見ると実行ログが確認できる
$ tkn pipelinerun logs hello-goodbye-run -f -n default
[hello : echo] Hello World
[goodbye : goodbye] Goodbye Tekton!
Triggerの実行
このようなイメージ。
-
EventListenerがEventを待ち受ける
-
TriggerTemplateが発生したときに、PipelineRunを構成する
-
TriggerBindingは、TriggerTemaplateによって作成されたPipelineRunにデータを渡す
TektonTriggersのインストール
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
kubectl get pods --namespace tekton-pipelines --watch
$ kubectl get pods --namespace tekton-pipelines
NAME READY STATUS RESTARTS AGE
tekton-events-controller-6d5d95b4-wplsd 1/1 Running 0 50m
tekton-pipelines-controller-585bbf56fb-82k9p 1/1 Running 0 50m
tekton-pipelines-webhook-5886749497-sdppr 1/1 Running 0 50m
tekton-triggers-controller-7d8cf85c9f-g66k5 1/1 Running 0 9m21s
tekton-triggers-core-interceptors-ff9879694-gzlhq 1/1 Running 0 9m20s
tekton-triggers-webhook-cdc9d576f-9m7hp 1/1 Running 0 9m21s
TriggerTemplateの作成
cat <<EOF > trigger-template.yaml
apiVersion: triggers.tekton.dev/v1beta1
kind: TriggerTemplate
metadata:
name: hello-template
spec:
params:
- name: username
default: "Kubernetes"
resourcetemplates:
- apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
generateName: hello-goodbye-run-
spec:
pipelineRef:
name: hello-goodbye
params:
- name: username
value: \$(tt.params.username)
EOF
kubectl apply -f trigger-template.yaml
$ kubectl get triggertemplates.triggers.tekton.dev
NAME AGE
hello-template 11s
cat <<EOF > trigger-binding.yaml
apiVersion: triggers.tekton.dev/v1beta1
kind: TriggerBinding
metadata:
name: hello-binding
spec:
params:
- name: username
value: \$(body.username)
EOF
kubectl apply -f trigger-binding.yaml
$ kubectl get triggerbindings.triggers.tekton.dev
NAME AGE
hello-binding 12s
EventListenerの作成
cat <<EOF > event-listener.yaml
apiVersion: triggers.tekton.dev/v1beta1
kind: EventListener
metadata:
name: hello-listener
spec:
serviceAccountName: tekton-robot
triggers:
- name: hello-trigger
bindings:
- ref: hello-binding
template:
ref: hello-template
EOF
cat <<EOF > rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: tekton-robot
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: triggers-example-eventlistener-binding
subjects:
- kind: ServiceAccount
name: tekton-robot
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: tekton-triggers-eventlistener-roles
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: triggers-example-eventlistener-clusterbinding
subjects:
- kind: ServiceAccount
name: tekton-robot
namespace: default
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: tekton-triggers-eventlistener-clusterroles
EOF
kubectl apply -f rbac.yaml
Triggerの起動
EventListnerの作成
kubectl apply -f event-listener.yaml
ポートフォワーディング
kubectl port-forward service/el-hello-listener 8080
$ kubectl port-forward service/el-hello-listener 8080
Forwarding from 127.0.0.1:8080 -> 8080
Forwarding from [::1]:8080 -> 8080
Triggerの実行
curlを投げる
curl -v \
-H 'content-Type: application/json' \
-d '{"username": "Tekton"}' \
http://localhost:8080
出力結果
$ curl -v \
-H 'content-Type: application/json' \
-d '{"username": "Tekton"}' \
http://localhost:8080
* Host localhost:8080 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
* Trying [::1]:8080...
* Connected to localhost (::1) port 8080
* using HTTP/1.x
> POST / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/8.11.0
> Accept: */*
> content-Type: application/json
> Content-Length: 22
>
* upload completely sent off: 22 bytes
< HTTP/1.1 202 Accepted
< Content-Type: application/json
< Date: Mon, 10 Feb 2025 00:52:30 GMT
< Content-Length: 164
<
{"eventListener":"hello-listener","namespace":"default","eventListenerUID":"234e23dd-b8ae-4edc-bb7c-8b071a55a83b","eventID":"88e68f48-3f6d-4be2-98d9-3569f6059e11"}
* Connection #0 to host localhost left intact
hello-goodbye-run-w5mhsが新しく作成され実行された
$ kubectl get pipelineruns
NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME
hello-goodbye-run True Succeeded 21m 20m
hello-goodbye-run-w5mhs True Succeeded 95s 82s
ログ出力もされている
$ tkn pipelinerun logs hello-goodbye-run-w5mhs -f
[hello : echo] Hello World
[goodbye : goodbye] Goodbye Tekton!
Supply Chain
private registoryが必要なのようで、チュートリアルどおり、minikubeを利用する。
minikube start --insecure-registry "10.0.0.0/24"
minikube addons enable registry
tekton pipelineをインストール
kubectl apply --filename \
https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml
❯ kubectl get po -n tekton-pipelines
NAME READY STATUS RESTARTS AGE
tekton-events-controller-74c57cd84d-4sc6p 1/1 Running 0 18m
tekton-pipelines-controller-595966dff6-s2s5l 1/1 Running 0 18m
tekton-pipelines-webhook-76796bd7bf-ksjbf 1/1 Running 0 18m
tekton chainをインストール
kubectl apply --filename \
https://storage.googleapis.com/tekton-releases/chains/latest/release.yaml
❯ kubectl get po -n tekton-chains
NAME READY STATUS RESTARTS AGE
tekton-chains-controller-8454f7d7d-s5gvq 1/1 Running 0 14m
metadataの構成
kubectl patch configmap chains-config -n tekton-chains \
-p='{"data":{"artifacts.oci.storage": "", "artifacts.taskrun.format":"in-toto", "artifacts.taskrun.storage": "tekton"}}'
pipeline.yamlを作成
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: build-push
spec:
params:
- name: image-reference
type: string
results:
- name: image-ARTIFACT_OUTPUTS
description: Built artifact.
value:
uri: $(tasks.kaniko-build.results.IMAGE_URL)
digest: sha1:$(tasks.kaniko-build.results.IMAGE_DIGEST)
workspaces:
- name: shared-data
tasks:
- name: dockerfile
taskRef:
name: create-dockerfile
workspaces:
- name: source
workspace: shared-data
- name: kaniko-build
runAfter: ["dockerfile"]
taskRef:
name: kaniko
workspaces:
- name: source
workspace: shared-data
params:
- name: IMAGE
value: $(params.image-reference)
---
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: create-dockerfile
spec:
workspaces:
- name: source
steps:
- name: add-dockerfile
workingDir: $(workspaces.source.path)
image: bash
script: |
cat <<EOF > Dockerfile
FROM alpine:3.16
RUN echo "hello world" > hello.log
EOF
---
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: kaniko
labels:
app.kubernetes.io/version: "0.6"
annotations:
tekton.dev/pipelines.minVersion: "0.17.0"
tekton.dev/categories: Image Build
tekton.dev/tags: image-build
tekton.dev/displayName: "Build and upload container image using Kaniko"
tekton.dev/platforms: "linux/amd64,linux/arm64,linux/ppc64le"
spec:
description: >-
This Task builds a simple Dockerfile with kaniko and pushes to a registry.
This Task stores the image name and digest as results, allowing Tekton Chains to pick up
that an image was built & sign it.
params:
- name: IMAGE
description: Name (reference) of the image to build.
- name: DOCKERFILE
description: Path to the Dockerfile to build.
default: ./Dockerfile
- name: CONTEXT
description: The build context used by Kaniko.
default: ./
- name: EXTRA_ARGS
type: array
default: []
- name: BUILDER_IMAGE
description: The image on which builds will run (default is v1.5.1)
default: gcr.io/kaniko-project/executor:v1.5.1@sha256:c6166717f7fe0b7da44908c986137ecfeab21f31ec3992f6e128fff8a94be8a5
workspaces:
- name: source
description: Holds the context and Dockerfile
- name: dockerconfig
description: Includes a docker `config.json`
optional: true
mountPath: /kaniko/.docker
results:
- name: IMAGE_DIGEST
description: Digest of the image just built.
- name: IMAGE_URL
description: URL of the image just built.
steps:
- name: build-and-push
workingDir: $(workspaces.source.path)
image: $(params.BUILDER_IMAGE)
args:
- $(params.EXTRA_ARGS)
- --dockerfile=$(params.DOCKERFILE)
- --context=$(workspaces.source.path)/$(params.CONTEXT) # The user does not need to care the workspace and the source.
- --destination=$(params.IMAGE)
- --digest-file=$(results.IMAGE_DIGEST.path)
# kaniko assumes it is running as root, which means this example fails on platforms
# that default to run containers as random uid (like OpenShift). Adding this securityContext
# makes it explicit that it needs to run as root.
securityContext:
runAsUser: 0
- name: write-url
image: docker.io/library/bash:5.1.4@sha256:c523c636b722339f41b6a431b44588ab2f762c5de5ec3bd7964420ff982fb1d9
script: |
set -e
image="$(params.IMAGE)"
echo -n "${image}" | tee "$(results.IMAGE_URL.path)"
❯ kubectl get service --namespace kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 20m
registry ClusterIP 10.110.186.77 <none> 80/TCP,443/TCP 15m
pipelinerun.yamlを作成
spec.params.valueのIPアドレスをregistryのCLUSTER-IPに置き換える
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
generateName: build-push-run-
spec:
pipelineRef:
name: build-push
params:
- name: image-reference
value: 10.110.186.77/tekton-test
workspaces:
- name: shared-data
volumeClaimTemplate:
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
パイプラインをデプロイ
kubectl apply -f pipeline.yaml
パイプラインを実行
kubectl create -f pipelinerun.yaml
❯ tkn pr logs build-push-run-vz9xj
task kaniko-build has failed: "step-build-and-push" exited with code 1
[kaniko-build : build-and-push] error building image: parsing dockerfile: Dockerfile parse error line 3: unknown instruction: EOF
failed to get logs for task kaniko-build : container step-build-and-push has failed : [{"key":"StartedAt","value":"2025-02-11T06:01:52.884Z","type":3}]
Tasks Completed: 2 (Failed: 1, Cancelled 0), Skipped: 0
scriptセクションが変に解釈されていたみたいなので、editで直した。
❯ k get task create-dockerfile -o yaml
apiVersion: tekton.dev/v1
kind: Task
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"tekton.dev/v1","kind":"Task","metadata":{"annotations":{},"name":"create-dockerfile","namespace":"default"},"spec":{"steps":[{"image":"bash","name":"add-dockerfile","script":"cat \u003c\u003cEOF \u003e Dockerfile\n FROM alpine:3.16\n RUN echo \"hello world\" \u003e hello.log\nEOF \n","workingDir":"$(workspaces.source.path)"}],"workspaces":[{"name":"source"}]}}
creationTimestamp: "2025-02-11T05:50:49Z"
generation: 5
name: create-dockerfile
namespace: default
resourceVersion: "8645"
uid: 13c70d78-8019-4500-bf71-9ae84f1a9027
spec:
steps:
- computeResources: {}
image: bash
name: add-dockerfile
script: |
cat <<EOF > Dockerfile
FROM alpine:3.16
RUN echo "hello world" > hello.log
EOF
workingDir: $(workspaces.source.path)
workspaces:
- name: source
修正後、再度pipelinerunを実行したところ、Trueで完了した。
❯ k get pipelineruns.tekton.dev
NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME
build-push-run-cm9bx True Succeeded 87s 62s
ログも正しく出力されている
❯ tkn pr logs build-push-run-cm9bx
[kaniko-build : build-and-push] INFO[0000] Retrieving image manifest alpine:3.16
[kaniko-build : build-and-push] INFO[0000] Retrieving image alpine:3.16 from registry index.docker.io
[kaniko-build : build-and-push] INFO[0003] Built cross stage deps: map[]
[kaniko-build : build-and-push] INFO[0003] Retrieving image manifest alpine:3.16
[kaniko-build : build-and-push] INFO[0003] Returning cached image manifest
[kaniko-build : build-and-push] INFO[0003] Executing 0 build triggers
[kaniko-build : build-and-push] INFO[0003] Unpacking rootfs as cmd RUN echo "hello world" > hello.log requires it.
[kaniko-build : build-and-push] INFO[0006] RUN echo "hello world" > hello.log
[kaniko-build : build-and-push] INFO[0006] Taking snapshot of full filesystem...
[kaniko-build : build-and-push] INFO[0006] cmd: /bin/sh
[kaniko-build : build-and-push] INFO[0006] args: [-c echo "hello world" > hello.log]
[kaniko-build : build-and-push] INFO[0006] Running: [/bin/sh -c echo "hello world" > hello.log]
[kaniko-build : build-and-push] INFO[0006] Taking snapshot of full filesystem...
[kaniko-build : build-and-push] INFO[0006] Pushing image to 10.110.186.77/tekton-test
[kaniko-build : build-and-push] INFO[0009] Pushed image to 1 destinations
[kaniko-build : write-url] 10.110.186.77/tekton-test
参考
Discussion