はじめに
最近Kubernetes上でCI/CDパイプラインを構築するTektonというツールについて調べる機会があったので、本記事で簡単にまとめておきます。
Tektonとは
TektonはKubernetes NativeなCI/CDソリューションとして開発され、Kubernetesクラスター上でのアプリケーションのビルド・テスト・デプロイなどを目的としたパイプラインの構築を実現します。
Tektonは主に4つのコンポーネントを利用しています。
- Pipelines: CI/CDワークフローの基礎となるブロック。後述の
Task
やPipeline
などを含む。Tektonのコア機能や他の機能の基盤を提供する。 - Triggers: CI/CDワークフロー向けのイベントトリガー機能を提供。
- CLI: TektonのCI/CDワークフローを管理するCLIツール(
tkn
)を提供。 - Dashboard: Pipeline向けWeb UIを提供。
Tektonのコンセプト
Pipelinesは複数のコンセプトで構成されています。こちらはConcept Modelとして公式ドキュメントでも記載されていますが、以下のようなコンセプトから成り立っています。
step:CI/CDワークフローでの操作は
step
という単位で実行されます。step
では、ユーザーの指定したコンテナイメージを利用してコマンドを実行します。task:複数のStepをまとめたものが
Task
になります。各TaskはPod内で実行され、stepはコンテナとして実行されます。TaskをPodとして実行することで、各stepに対して共通の環境を提供することができます。また、Taskで利用するVolume
を用意すれば、各stepからvolumeを利用し、ファイルの共有などが実現できます。pipeline:複数のTaskをまとめたものが
Pipeline
になります。複数のTask
Podを作成して、各Podを順番に実行します。Pipelinesは各Stepコンテナにentrypointバイナリを注入し、ユーザーの指定したコマンドを実行します。各Pipelineでのワークフローの実行や状態は、Kubernetes annotationsを利用して実行・追跡をします。Annotationsは各Stepコンテナにおいて、Kubernetes downward APIのファイルフォームで導入されます。entrypointバイナリは導入されたファイル群をチェックし、特定のannotationがファイルに出現した時のみコマンドを実行します。
Tekton Pipelinesでは複数のCustom Resourceを組み合わせてCI/CDパイプラインを構築します。利用する主なCustom Resourceは以下の通りです。
Custom Resource名 | 利用用途の概要 |
---|---|
Task | Taskを定義する。各Stepの内容や使用するコンテナイメージ、パラメータなどを指定する |
TaskRun | 定義したTaskを実行するために利用する。指定のTask内にあるStepを順番に実行し、処理が完了・失敗するまで動作する。TaskRunでは、実行するTaskの指定やTask内で利用するInput/Outputリソースを指定する |
Pipeline | Pipelineを定義する。Pipelineで利用するStepやパラメータなどを指定する |
PipelineRun | 定義したPipelineを実行するために利用する。指定したPipeline内のTaskに含まれるStepを順番に実行し、処理が完了・失敗するまで動作する。PipelineRunでは、実行するPipelineやPieline内で利用するInput/Outputリソースなどを指定する |
PipelineResource | Task・Pipelineで利用するリソースを定義する。利用できるリソース種別としてはgit image cluster storage など ※7月15日時点でalphaのみ提供 |
※参考ドキュメント:
Kubernetes Docs - Expose Pod Information to Containers Through Files
Tekton Docs - PipelineResources # Why Aren’t PipelineResources in Beta?
Tektonを利用する流れ
Tektonを利用する際の大きな流れとしては以下のようになります。各CRを作成後にTaskRun
PipelineRun
を実行することで、Task
を実行するPodが順次作成され、処理が実行されます。
Tekton Custom Resource間の関連性
次に、CRの定義ファイル間でどのような関連があるかを図示しておきます。ここではPipelineResource
を利用する・しない場合で分けております。なお、ここでは各リソースの定義ファイルの例を載せておりますが、書き方は複数あるのであくまで一例です。また内容は説明用のものなので、実際のリソース作成に使うことはできません。
PipelineResourceを利用する場合(v1alpha1
)
TaskRun
を利用する場合、まずはPipelineResource
Task
を作成します。PipelineResource
には利用するリソースURLなどを、Task
には実行する各Stepでのコンテナイメージやコマンドを設定します。
PipelineResource
Task
を作成後はTaskRun
リソースを作成し、Taskを実行します。実行中はPodの状態やtkn
コマンドを利用することで状況を確認することができます。
次にPipelineRun
を利用する場合、PipelineResource
Task
に加えてPipeline
リソースを作成します。Pipeline
では利用するTaskやTask中で使用するリソースなどを指定します。
3種類のリソースを作成した後はPipelineRun
を作成し、Pipelineを実行します。こちらも実行中はPodの状態やtkn
コマンドにより状況を確認することができます。
PipelineResourceを利用しない場合(v1beta1
)
PipelineResource
を利用しない場合も、大きな流れは変わりません。TaskRun
を利用する場合は事前にTask
を作成しておき、TaskRun
によってそれを実行します。Task
で利用するGitHub URLなどのパラメータはTask
TaskRun
それぞれで指定することが可能です。例えばTask
で利用するのと同じ名称のパラメータをTaskRun
で指定することで、同じTask
を別々のTaskRun
で利用する際にTaskRun
側で必要なパラメータを指定することができます。
PipelineRun
を利用する場合も同様で、Task
Pipeline
リソースをあらかじめ作成してからPipelineRun
によって実行します。
Task/Pipelineの構成
今回は以下のようなPipelineの構築を行います。大まかな流れは以下の通りです。
- GitHubからソースコードをclone
- ソースコードをDockerイメージにbuild
- buildしたDockerイメージをDocker Hubにpush
- Docker Hubのイメージをpull
- pullしたイメージをKubernetes上にdeploy
今回はPipelineResources
は利用せず、各CR内で利用するリソースのURL等を直接指定しています。
また上図中にWorkspace
というものが見えます。Workspace
は複数のTask
が共有することのできる領域を表しており、例えばKubernetesのSecret
やConfigMap
、PersistentVolumeClaim
などを共有することができます。これにより、あるTaskで取得した情報(今回で言えばGitHubから取得したソースコードなど)を別のTaskに渡すことが可能です。
Workspace
を利用する場合は、PersistentVolumeClaim
emptyDir
ConfigMap
Secret
のいずれかのKubernetesリソースを指定する必要があります。
TektonにはWorkspace
と似たような機能としてVolume
というものが存在しますが、あるTaskが複数のWorkspaceを利用する場合、TaskRun
PipelineRun
から、どのWorkspaceに対してどの保存領域(PersistentVolumeClaim
など)を利用するかを指定することができます。そのため、Taskの再利用を考えた場合はWorkspace
のほうが使いやすい機能となります。
なお、今回利用しているPipelineの構成やyamlファイルの多くはこちらの記事を参考にしている部分が多いため、合わせて読んでいただければと思います。
TektonのTasks/Pipelineの利用方法
構築環境情報
今回構築で利用した環境は以下の通りです。Pipeline中で利用するアプリケーションは、簡単なJSONのメッセージを返すだけのWebアプリです。
- Kubernetes Cluster: Alibaba Cloud Standard Managed Clusterを利用
- Kubernetes version: v1.16.9
- Tekton version: v0.14
- GitHub Repository: こちらを利用(Public)
- Docker Repository: Docker Hub (Public)を利用
今回はGitHub、Docker Hubともにパブリックなものを利用しましたが、プライベートリポジトリを利用する場合は以下のリンク先を参照してください。
※参考ドキュメント:
Tektonの構築
まずはTektonの構築を行います。またTekton Pipelinesを利用するうえで必要となる設定も合わせて行います。なお構築・検証等で利用した定義ファイルはこちらに置いてあります。
Tekton Controller / CRDのデプロイ
まずはTektonを利用するうえで必要なController
Webhook
Deployment、各種CustomResourceDefinition
などをKubernetesクラスター上にデプロイします。Tektonのデプロイは、以下のコマンドを実行するだけで完了します。
# Tekton pipelineのデプロイ $ kubectl apply --filename https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml namespace/tekton-pipelines created podsecuritypolicy.policy/tekton-pipelines created clusterrole.rbac.authorization.k8s.io/tekton-pipelines-controller-cluster-access created clusterrole.rbac.authorization.k8s.io/tekton-pipelines-controller-tenant-access created clusterrole.rbac.authorization.k8s.io/tekton-pipelines-webhook-cluster-access created clusterrole.rbac.authorization.k8s.io/tekton-pipelines-leader-election created role.rbac.authorization.k8s.io/tekton-pipelines-controller created role.rbac.authorization.k8s.io/tekton-pipelines-webhook created serviceaccount/tekton-pipelines-controller created serviceaccount/tekton-pipelines-webhook created clusterrolebinding.rbac.authorization.k8s.io/tekton-pipelines-controller-cluster-access created clusterrolebinding.rbac.authorization.k8s.io/tekton-pipelines-controller-leaderelection created clusterrolebinding.rbac.authorization.k8s.io/tekton-pipelines-controller-tenant-access created clusterrolebinding.rbac.authorization.k8s.io/tekton-pipelines-webhook-cluster-access created clusterrolebinding.rbac.authorization.k8s.io/tekton-pipelines-webhook-leaderelection created rolebinding.rbac.authorization.k8s.io/tekton-pipelines-controller created rolebinding.rbac.authorization.k8s.io/tekton-pipelines-webhook created customresourcedefinition.apiextensions.k8s.io/clustertasks.tekton.dev created customresourcedefinition.apiextensions.k8s.io/conditions.tekton.dev created customresourcedefinition.apiextensions.k8s.io/images.caching.internal.knative.dev created customresourcedefinition.apiextensions.k8s.io/pipelines.tekton.dev created customresourcedefinition.apiextensions.k8s.io/pipelineruns.tekton.dev created customresourcedefinition.apiextensions.k8s.io/pipelineresources.tekton.dev created customresourcedefinition.apiextensions.k8s.io/tasks.tekton.dev created customresourcedefinition.apiextensions.k8s.io/taskruns.tekton.dev created secret/webhook-certs created validatingwebhookconfiguration.admissionregistration.k8s.io/validation.webhook.pipeline.tekton.dev created mutatingwebhookconfiguration.admissionregistration.k8s.io/webhook.pipeline.tekton.dev created validatingwebhookconfiguration.admissionregistration.k8s.io/config.webhook.pipeline.tekton.dev created clusterrole.rbac.authorization.k8s.io/tekton-aggregate-edit created clusterrole.rbac.authorization.k8s.io/tekton-aggregate-view created configmap/config-artifact-bucket created configmap/config-artifact-pvc created configmap/config-defaults created configmap/feature-flags created configmap/config-leader-election created configmap/config-logging created configmap/config-observability created deployment.apps/tekton-pipelines-controller created service/tekton-pipelines-controller created deployment.apps/tekton-pipelines-webhook created service/tekton-pipelines-webhook created # 実行後の確認 $ kubectl get pods -n tekton-pipelines NAME READY STATUS RESTARTS AGE tekton-pipelines-controller-548c8fb7b5-hbb9m 1/1 Running 0 15s tekton-pipelines-webhook-9cfc4b4df-vw8ss 1/1 Running 0 15s
Docker HubへPushするためのSecret作成
KubernetesからDocker HubへコンテナイメージをPushするため、Docker Hubのログイン情報をSecret
として用意します。ここで作成したSecretを利用するには、Service Account・各Pipelineリソースのどちらかに関連付ける必要があります。今回作成したSecretは、後ほどService Accountを作成する際に指定することで、Service Accountと関連付けます。
annotations
に記載しているtekton.dev/docker-0: https://index.docker.io/v1/
は、このSecretで指定している秘匿情報がどのWebアドレスに属したものかを表します。
apiVersion: v1 kind: Secret metadata: name: basic-user-pass annotations: tekton.dev/docker-0: https://index.docker.io/v1/ type: kubernetes.io/basic-auth stringData: username: <username> password: <password>
# Secretの作成 $ kubectl apply -f docker-secret.yml secret/basic-user-pass created # 作成後の確認 $ kubectl describe secret basic-user-pass Name: basic-user-pass Namespace: default Labels: <none> Annotations: tekton.dev/docker-0: https://index.docker.io/v1/ Type: kubernetes.io/basic-auth Data ==== password: <byte number> username: <byte number>
※参考ドキュメント:
Tekton Pipeline実行用Service Accountの作成とRoleの付与
次にTekton Pipelineを実行するためのService Accountを用意します。作成の定義ファイルはこちらのファイルを利用しました(Service Accountと関連付けるSecretのみ変更しています)。
今回使用するpipeline-account
というService Accountには、合わせてSecret
Role
を付与します。Secret
にはAPIアクセスの認証に利用するトークンの情報を、Role
にはtekton-pipelines
Namespaceに含まれるリソースに対して操作する権限を追加し、RoleBinding
を用いてService Accountと紐づけます。
# Service Account作成 $ kubectl apply -f pipeline-account.yml serviceaccount/pipeline-account created secret/kube-api-secret created role.rbac.authorization.k8s.io/pipeline-role created rolebinding.rbac.authorization.k8s.io/pipeline-role-binding created # 作成後の確認 $ kubectl describe sa pipeline-account Name: pipeline-account Namespace: default Labels: <none> Annotations: Image pull secrets: <none> Mountable secrets: basic-user-pass pipeline-account-token-sg475 Tokens: kube-api-secret pipeline-account-token-sg475 Events: <none>
Workspace用のPersistent Volume Claim作成
次に、Pipelineで利用するWorkspace
のためのストレージ領域を用意します。今回は、Alibaba Cloudで利用できるStorage Classのうちalicloud-disk-ssd
を利用します。Persistent Volume Claim作成時に、Storage Classとしてalicloud-disk-ssd
を指定することでPersistent Volumeが作成され、Kubernetesの提供する永続ストレージを利用することができます。
なおAlibaba Cloud DiskをPVCで利用する場合は、20Gi以上のサイズを指定する必要があります。
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: tekton-volume spec: accessModes: - ReadWriteOnce resources: requests: storage: 20Gi storageClassName: alicloud-disk-ssd
# 利用可能なStorage Class $ kubectl get sc NAME PROVISIONER AGE alicloud-disk-available diskplugin.csi.alibabacloud.com 3h28m alicloud-disk-efficiency diskplugin.csi.alibabacloud.com 3h28m alicloud-disk-essd diskplugin.csi.alibabacloud.com 3h28m alicloud-disk-ssd diskplugin.csi.alibabacloud.com 3h28m alicloud-disk-topology diskplugin.csi.alibabacloud.com 3h28m # 20Gi未満を指定した場合 $ kubectl apply -f tekton-pvc.yml $ kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE tekton-volume Pending alicloud-disk-ssd 5s $ kubectl describe pvc tekton-volume (中略) Message: disk size is not supported., PVC defined storage should equal/greater than 20Gi Normal ExternalProvisioning 0s (x3 over 19s) persistentvolume-controller waiting for a volume to be created, either by external provisioner "diskplugin.csi.alibabacloud.com" or manually created by system administrator # 20Gi以上を指定した場合 $ kubectl apply -f tekton-pvc.yml persistentvolumeclaim/tekton-volume created $ kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE tekton-volume Bound d-6we0n5028zulr8h5qwzo 20Gi RWO alicloud-disk-ssd 3s
※参考ドキュメント:
Task/TaskRunの利用方法
ここから実際のTask/Pipelineの利用方法を紹介します。
まずはTaskRun
を利用する方法について紹介します。ここでは3つのTaskのうち一つ目のTaskのみを利用します。
まずはTask
についてです。Task
はTektonのGitHub RepositoryでCatalogとして紹介されているこちらの定義ファイルを利用しました。
apiVersion: tekton.dev/v1beta1 kind: Task metadata: name: git-clone spec: workspaces: - name: output description: The git repo will be cloned onto the volume backing this workspace params: - name: url description: git url to clone type: string - name: revision description: git revision to checkout (branch, tag, sha, ref…) type: string default: master - name: refspec description: (optional) git refspec to fetch before checking out revision default: "" - name: submodules description: defines if the resource should initialize and fetch the submodules type: string default: "true" - name: depth description: performs a shallow clone where only the most recent commit(s) will be fetched type: string default: "1" - name: sslVerify description: defines if http.sslVerify should be set to true or false in the global git config type: string default: "true" - name: subdirectory description: subdirectory inside the "output" workspace to clone the git repo into type: string default: "" - name: deleteExisting description: clean out the contents of the repo's destination directory (if it already exists) before trying to clone the repo there type: string default: "false" - name: httpProxy description: git HTTP proxy server for non-SSL requests type: string default: "" - name: httpsProxy description: git HTTPS proxy server for SSL requests type: string default: "" - name: noProxy description: git no proxy - opt out of proxying HTTP/HTTPS requests type: string default: "" results: - name: commit description: The precise commit SHA that was fetched by this Task steps: - name: clone image: gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init:v0.12.1 script: | CHECKOUT_DIR="$(workspaces.output.path)/$(params.subdirectory)" cleandir() { # Delete any existing contents of the repo directory if it exists. # # We don't just "rm -rf $CHECKOUT_DIR" because $CHECKOUT_DIR might be "/" # or the root of a mounted volume. if [[ -d "$CHECKOUT_DIR" ]] ; then # Delete non-hidden files and directories rm -rf "$CHECKOUT_DIR"/* # Delete files and directories starting with . but excluding .. rm -rf "$CHECKOUT_DIR"/.[!.]* # Delete files and directories starting with .. plus any other character rm -rf "$CHECKOUT_DIR"/..?* fi } if [[ "$(params.deleteExisting)" == "true" ]] ; then cleandir fi test -z "$(params.httpProxy)" || export HTTP_PROXY=$(params.httpProxy) test -z "$(params.httpsProxy)" || export HTTPS_PROXY=$(params.httpsProxy) test -z "$(params.noProxy)" || export NO_PROXY=$(params.noProxy) /ko-app/git-init \ -url "$(params.url)" \ -revision "$(params.revision)" \ -refspec "$(params.refspec)" \ -path "$CHECKOUT_DIR" \ -sslVerify="$(params.sslVerify)" \ -submodules="$(params.submodules)" \ -depth "$(params.depth)" cd "$CHECKOUT_DIR" RESULT_SHA="$(git rev-parse HEAD | tr -d '\n')" EXIT_CODE="$?" if [ "$EXIT_CODE" != 0 ] then exit $EXIT_CODE fi # Make sure we don't add a trailing newline to the result! echo -n "$RESULT_SHA" > $(results.commit.path)
定義ファイルの各項目について簡単に紹介します。
spec.workspaces
: ここではoutput
というWorkspaceを指定しており、Cloneしたソースコードの置き場所として主に利用します。また実行後のSHAの値をファイルに出力し、Workspaceに保存しています。spec.results
:results
を指定すると、Task
の実行結果をファイルに出力することができます。ここではspec.steps
内で利用し、Taskの実行結果を$(results.commit.path)
に保存しています。spec.steps
: ここではscript
を設定してStepコンテナ内で実行するコマンドを指定しています。コンテナはTektonが用意したgit-init
というイメージを利用し、指定したGitHubリポジトリからのcloneと実行結果のSHAを保存する処理を実行します。
続いてTaskRun
についてです。TaskRunはTekton GitHub Repository中のこちらの定義ファイルを参考にしました。元の定義ファイルはapiVersion: v1alpha1
となっているので、apiVersion: v1beta1
に合わせて一部修正しております。
apiVersion: tekton.dev/v1beta1 kind: TaskRun metadata: name: git-clone-run spec: workspaces: - name: output emptyDir: {} params: - name: url value: https://github.com/FY0323/tekton-test.git taskRef: name: git-clone
spec.params
: ここではTask中で利用するパラメータを指定しています。今回はclone元のGitHub RepositoryのURLを指定しています。spec.taskRef
:taskRef
には、TaskRun中に利用するTaskの名称を指定します。
※参考ドキュメント:
上記定義ファイルを利用してTaskを実行します。
# Taskの作成 $ kubectl apply -f tk_task/task1.yml task.tekton.dev/git-clone created $ kubectl get tekton-pipelines NAME AGE task.tekton.dev/git-clone 17m # TaskRunの作成 $ kubectl apply -f taskrun1.yml taskrun.tekton.dev/git-clone-run created # 実行中 $ kubectl get tekton-pipelines NAME AGE task.tekton.dev/git-clone 18m NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME taskrun.tekton.dev/git-clone-run Unknown Running 4s # 完了後 $ kubectl get tekton-pipelines NAME AGE task.tekton.dev/git-clone 18m NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME taskrun.tekton.dev/git-clone-run True Succeeded 13s 6s $ kubectl get pods NAME READY STATUS RESTARTS AGE git-clone-run-pod-8827g 0/1 Completed 0 27s $ tkn taskrun logs git-clone-run --last -f [clone] + CHECKOUT_DIR=/workspace/output/ [clone] + '[[' false '==' true ]] [clone] + test -z [clone] + test -z [clone] + test -z [clone] + /ko-app/git-init -url https://github.com/FY0323/tekton-test.git -revision master -refspec -path /workspace/output/ '-sslVerify=true' '-submodules=true' -depth 1 [clone] {"level":"info","ts":1594704754.7358825,"caller":"git/git.go:136","msg":"Successfully cloned https://github.com/FY0323/tekton-test.git @ d33aa9b539e788c4f7b90ca8c4879664effd306c (grafted, HEAD, origin/master) in path /workspace/output/"} [clone] {"level":"info","ts":1594704754.7876565,"caller":"git/git.go:177","msg":"Successfully initialized and updated submodules in path /workspace/output/"} [clone] + cd /workspace/output/ [clone] + git rev-parse HEAD [clone] + tr -d '\n' [clone] + RESULT_SHA=d33aa9b539e788c4f7b90ca8c4879664effd306c [clone] + EXIT_CODE=0 [clone] + '[' 0 '!=' 0 ] [clone] + echo -n d33aa9b539e788c4f7b90ca8c4879664effd306c
※参考ドキュメント:
Pipeline/PipelineRunの利用方法
次にPipelineの利用方法についてです。
Pipelineで利用する3つのTaskは先ほどのtask1.yml
に加え、こちらとこちらの定義ファイルをそれぞれ利用しました。
task2.yml
apiVersion: tekton.dev/v1beta1 kind: Task metadata: name: kaniko spec: 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 default: "" - name: BUILDER_IMAGE description: The image on which builds will run default: gcr.io/kaniko-project/executor:latest workspaces: - name: source results: - name: IMAGE-DIGEST description: Digest of the image just built. steps: - name: build-and-push workingDir: $(workspaces.source.path) image: $(params.BUILDER_IMAGE) # specifying DOCKER_CONFIG is required to allow kaniko to detect docker credential # https://github.com/tektoncd/pipeline/pull/706 env: - name: DOCKER_CONFIG value: /tekton/home/.docker command: - /kaniko/executor - $(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) - --oci-layout-path=$(workspaces.source.path)/$(params.CONTEXT)/image-digest # 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-digest workingDir: $(workspaces.source.path) image: gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/imagedigestexporter:v0.11.1 # output of imagedigestexport [{"key":"digest","value":"sha256:eed29..660","resourceRef":{"name":"myrepo/myimage"}}] command: ["/ko-app/imagedigestexporter"] args: - -images=[{"name":"$(params.IMAGE)","type":"image","url":"$(params.IMAGE)","digest":"","OutputImageDir":"$(workspaces.source.path)/$(params.CONTEXT)/image-digest"}] - -terminationMessagePath=$(params.CONTEXT)/image-digested - name: digest-to-results workingDir: $(workspaces.source.path) image: stedolan/jq script: | cat $(params.CONTEXT)/image-digested | jq '.[0].value' -rj | tee /tekton/results/IMAGE-DIGEST
task3.yml
apiVersion: tekton.dev/v1beta1 kind: Task metadata: name: deploy-using-kubectl spec: workspaces: - name: git-source description: The git repo params: - name: pathToYamlFile description: The path to the yaml file to deploy within the git source - name: imageUrl description: Image name including repository - name: imageTag description: Image tag default: "latest" - name: imageDigest description: Digest of the image to be used. steps: - name: update-yaml image: alpine command: ["sed"] args: - "-i" - "-e" - "s;__IMAGE__;$(params.imageUrl):$(params.imageTag);g" - "-e" - "s;__DIGEST__;$(params.imageDigest);g" - "$(workspaces.git-source.path)/$(params.pathToYamlFile)" - name: run-kubectl image: lachlanevenson/k8s-kubectl command: ["kubectl"] args: - "apply" - "-f" - "$(workspaces.git-source.path)/$(params.pathToYamlFile)"
Pipeline
にはこちらのファイルを利用しています。
pipeline1.yml
apiVersion: tekton.dev/v1beta1 kind: Pipeline metadata: name: build-and-push-pipeline spec: workspaces: - name: git-source description: The git repo params: - name: gitUrl description: Git repository url - name: gitRevision description: Git revision to check out default: master - name: pathToContext description: The path to the build context, used by Kaniko - within the workspace default: src - name: imageUrl description: Image name including repository - name: imageTag description: Image tag default: "latest" - name: pathToYamlFile description: The path to the yaml file to deploy within the git source tasks: - name: clone-repo taskRef: name: git-clone workspaces: - name: output workspace: git-source params: - name: url value: "$(params.gitUrl)" - name: revision value: "$(params.gitRevision)" - name: subdirectory value: "." - name: deleteExisting value: "true" - name: source-to-image taskRef: name: kaniko runAfter: - clone-repo workspaces: - name: source workspace: git-source params: - name: CONTEXT value: $(params.pathToContext) - name: IMAGE value: $(params.imageUrl):$(params.imageTag) - name: deploy-to-cluster taskRef: name: deploy-using-kubectl runAfter: - source-to-image workspaces: - name: git-source workspace: git-source params: - name: pathToYamlFile value: $(params.pathToYamlFile) - name: imageUrl value: $(params.imageUrl) - name: imageTag value: $(params.imageTag) - name: imageDigest value: $(tasks.source-to-image.results.IMAGE-DIGEST)
spec.tasks
: Pipeline中のTaskにrunAfter
を設定することで、Task間の実行順を設定することができます。ここではclone-repo
source-to-image
deploy-to-cluster
という順で実行されるようrunAfter
を設定しています。
最後にPipelineRun
はこちらのファイルを利用しています。PipelineRun
ではPipelineで設定したパラメータやWorkspaceに利用するPersistent Volume Claimなどを設定します。
pipelinerun1.yml
apiVersion: tekton.dev/v1beta1 kind: PipelineRun metadata: name: build-and-push-pieline spec: pipelineRef: name: build-and-push-pipeline params: - name: gitUrl value: https://github.com/FY0323/tekton-test.git - name: pathToContext value: . - name: pathToYamlFile value: "test-app.yml" - name: imageUrl value: fy0323/tekton-test - name: imageTag value: "1" serviceAccountName: pipeline-account workspaces: - name: git-source persistentVolumeClaim: claimName: tekton-volume
spec.pipelineRef
: ここには実行するPipelineを指定します。spec.serviceAccountName
: ここではPipelineを実行する際に利用するService Account(ここではpipeline-account
)を指定します。spec.workspaces
: ここではPipeline中に利用するWorkspaceと、それに利用するKubernetesリソースを指定します。ここではPersistent Volume Claimを利用します。
※参考ドキュメント:
それでは用意した定義ファイルを用いて各リソースを作成します。
# Taskの作成 $ kubectl apply -f tk_task/ task.tekton.dev/kaniko created task.tekton.dev/deploy-using-kubectl created $ kubectl get tekton-pipelines NAME AGE task.tekton.dev/deploy-using-kubectl 7s task.tekton.dev/git-clone 18h task.tekton.dev/kaniko 18h # Pipelineの作成 $ kubectl apply -f pipeline1.yml pipeline.tekton.dev/build-and-push-pipeline created $ kubectl get tekton-pipelines NAME AGE pipeline.tekton.dev/build-and-push-pipeline 1m NAME AGE task.tekton.dev/deploy-using-kubectl 2m task.tekton.dev/git-clone 19h task.tekton.dev/kaniko 19h # PipelineRunの作成(Pipelineの実行) $ kubectl apply -f pipelinerun1.yml pipelinerun.tekton.dev/build-and-push-pieline created $ tkn pipelinerun logs --last -f [clone-repo : clone] + CHECKOUT_DIR=/workspace/output/. [clone-repo : clone] + '[[' true '==' true ]] [clone-repo : clone] + cleandir [clone-repo : clone] + '[[' -d /workspace/output/. ]] [clone-repo : clone] + rm -rf /workspace/output/./Dockerfile /workspace/output/./image-digest /workspace/output/./image-digested /workspace/output/./pipeline-account.yml /workspace/output/./pipeline1.yml /workspace/output/./pipelinerun1.yml /workspace/output/./sa_clusterrolebinding.yml /workspace/output/./src /workspace/output/./taskrun1.yml /workspace/output/./test-app.yml /workspace/output/./tk_resource /workspace/output/./tk_task [clone-repo : clone] + rm -rf /workspace/output/./.git [clone-repo : clone] + rm -rf '/workspace/output/./..?*' [clone-repo : clone] + test -z [clone-repo : clone] + test -z [clone-repo : clone] + test -z [clone-repo : clone] + /ko-app/git-init -url https://github.com/FY0323/tekton-test.git -revision master -refspec -path /workspace/output/. '-sslVerify=true' '-submodules=true' -depth 1 [clone-repo : clone] {"level":"info","ts":1594779966.749329,"caller":"git/git.go:136","msg":"Successfully cloned https://github.com/FY0323/tekton-test.git @ 3d34a47b3de078961ebf8c7b71143bf9ea17f2ec (grafted, HEAD, origin/master) in path /workspace/output/."} [clone-repo : clone] {"level":"info","ts":1594779966.8057873,"caller":"git/git.go:177","msg":"Successfully initialized and updated submodules in path /workspace/output/."} [clone-repo : clone] + cd /workspace/output/. [clone-repo : clone] + git rev-parse HEAD [clone-repo : clone] + tr -d '\n' [clone-repo : clone] + RESULT_SHA=3d34a47b3de078961ebf8c7b71143bf9ea17f2ec [clone-repo : clone] + EXIT_CODE=0 [clone-repo : clone] + '[' 0 '!=' 0 ] [clone-repo : clone] + echo -n 3d34a47b3de078961ebf8c7b71143bf9ea17f2ec [source-to-image : build-and-push] INFO[0002] Retrieving image manifest golang:latest [source-to-image : build-and-push] INFO[0004] Retrieving image manifest golang:latest [source-to-image : build-and-push] INFO[0008] Built cross stage deps: map[] [source-to-image : build-and-push] INFO[0008] Retrieving image manifest golang:latest [source-to-image : build-and-push] INFO[0010] Retrieving image manifest golang:latest [source-to-image : build-and-push] INFO[0014] Executing 0 build triggers [source-to-image : build-and-push] INFO[0014] Unpacking rootfs as cmd COPY src/main.go /go/src/work/ requires it. [source-to-image : build-and-push] INFO[0038] EXPOSE 8080 [source-to-image : build-and-push] INFO[0038] cmd: EXPOSE [source-to-image : build-and-push] INFO[0038] Adding exposed port: 8080/tcp [source-to-image : build-and-push] INFO[0038] COPY src/main.go /go/src/work/ [source-to-image : build-and-push] INFO[0038] Taking snapshot of files... [source-to-image : build-and-push] INFO[0038] WORKDIR /go/src/work [source-to-image : build-and-push] INFO[0038] cmd: workdir [source-to-image : build-and-push] INFO[0038] Changed working directory to /go/src/work [source-to-image : build-and-push] INFO[0038] No files changed in this command, skipping snapshotting. [source-to-image : build-and-push] INFO[0038] RUN go get -u github.com/gin-gonic/gin && go build main.go [source-to-image : build-and-push] INFO[0038] Taking snapshot of full filesystem... [source-to-image : build-and-push] INFO[0045] cmd: /bin/sh [source-to-image : build-and-push] INFO[0045] args: [-c go get -u github.com/gin-gonic/gin && go build main.go] [source-to-image : build-and-push] INFO[0045] Running: [/bin/sh -c go get -u github.com/gin-gonic/gin && go build main.go] [source-to-image : build-and-push] INFO[0104] Taking snapshot of full filesystem... [source-to-image : build-and-push] INFO[0109] CMD ["./main"] [source-to-image : digest-to-results] + cat ./image-digested [source-to-image : digest-to-results] + jq .[0].value -rj [source-to-image : digest-to-results] + tee /tekton/results/IMAGE-DIGEST [source-to-image : digest-to-results] sha256:b5248ad0c8ff878ad795c17fdad3b0a842cf38e3dc7df97f66012511af803c5a [deploy-to-cluster : run-kubectl] deployment.apps/test-app configured [deploy-to-cluster : run-kubectl] service/test-app created # 実行後の確認 $ kubectl get tekton-pipelines NAME AGE pipeline.tekton.dev/build-and-push-pipeline 37m NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME pipelinerun.tekton.dev/build-and-push-pieline True Succeeded 14m 10m NAME AGE task.tekton.dev/deploy-using-kubectl 39m task.tekton.dev/git-clone 19h task.tekton.dev/kaniko 19h NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME taskrun.tekton.dev/build-and-push-pieline-clone-repo-hwc7g True Succeeded 14m 13m taskrun.tekton.dev/build-and-push-pieline-deploy-to-cluster-n9wdw True Succeeded 11m 10m taskrun.tekton.dev/build-and-push-pieline-source-to-image-jmqg8 True Succeeded 13m 11m $ kubectl get pods NAME READY STATUS RESTARTS AGE build-and-push-pieline-clone-repo-hwc7g-pod-flpw6 0/1 Completed 0 6m7s build-and-push-pieline-deploy-to-cluster-n9wdw-pod-ttp7l 0/2 Completed 0 3m9s build-and-push-pieline-source-to-image-jmqg8-pod-zfrxg 0/3 Completed 0 5m49s test-app-6b47db66b5-cfgjz 1/1 Running 0 2m51s $ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 172.21.0.1 <none> 443/TCP 45h test-app NodePort 172.21.15.253 <none> 8080:30008/TCP 3m11s
なお、実行後のPersistent Volumeにアクセスすると、先ほどのPipelineで作成したファイルなどが確認できます。
# Persistent VolumeへアクセスするPodを作成 $ kubectl apply -f testpod.yml pod/mypod created # Podへログイン $ kubectl exec -it mypod /bin/bash kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl kubectl exec [POD] -- [COMMAND] instead. root@mypod:/# # Persistent Volumeの内容確認 root@mypod:/# pwd / root@mypod:/# ls -l /var/www/html total 48 -rw-r--r-- 1 root root 174 Jul 15 02:26 Dockerfile drwxr-xr-x 3 root root 4096 Jul 15 02:28 image-digest★ -rw-r--r-- 1 root root 146 Jul 15 02:28 image-digested★ -rw-r--r-- 1 root root 951 Jul 15 02:26 pipeline-account.yml -rw-r--r-- 1 root root 2099 Jul 15 02:26 pipeline1.yml -rw-r--r-- 1 root root 676 Jul 15 02:26 pipelinerun1.yml -rw-r--r-- 1 root root 283 Jul 15 02:26 sa_clusterrolebinding.yml drwxr-xr-x 2 root root 4096 Jul 15 02:26 src -rw-r--r-- 1 root root 464 Jul 15 02:26 taskrun1.yml -rw-r--r-- 1 root root 601 Jul 15 02:29 test-app.yml drwxr-xr-x 2 root root 4096 Jul 15 02:26 tk_resource drwxr-xr-x 2 root root 4096 Jul 15 02:26 tk_task root@mypod:/# cat /var/www/html/image-digested [{"key":"digest","value":"sha256:b5248ad0c8ff878ad795c17fdad3b0a842cf38e3dc7df97f66012511af803c5a","resourceRef":{"name":"fy0323/tekton-test:1"}}] root@mypod:/# ls -l /var/www/html/image-digest total 12 drwxr-xr-x 3 root root 4096 Jul 15 02:28 blobs -rwxr-xr-x 1 root root 259 Jul 15 02:28 index.json -rwxr-xr-x 1 root root 37 Jul 15 02:28 oci-layout root@mypod:/# cat /var/www/html/image-digest/oci-layout { "imageLayoutVersion": "1.0.0" } root@mypod:/# cat /var/www/html/image-digest/index.json { "schemaVersion": 2, "manifests": [ { "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "size": 1738, "digest": "sha256:b5248ad0c8ff878ad795c17fdad3b0a842cf38e3dc7df97f66012511af803c5a" } ] } root@mypod:/# ls -l /var/www/html/image-digest/blobs/sha256 total 401604 -rw-r--r-- 1 root root 51827493 Jul 15 02:28 5573c4b3094956931fd68c310ae92c9eb1a787f0c77ac2730be9d16cce172d5e -rw-r--r-- 1 root root 98813395 Jul 15 02:28 56269221df77fbedf580f9a2caacbcb7dc4e7ea2d33922cd95973f70323e13a6 -rw-r--r-- 1 root root 125 Jul 15 02:28 56c4bef2c0bbf3cff5165f41fc838bc6eb019e04a44afcb8e6e7d5ab1da0f0c9 -rw-r--r-- 1 root root 5836 Jul 15 02:28 8ea17da6b6f374412f284f9d00ec18ee7926f87456ae16f30f5c7e3cfb37de2b -rw-r--r-- 1 root root 123712482 Jul 15 02:28 90833167b3a65b4f9c43490478a2b69758cd005193ac60dbb81b20717ba4a5a0 -rw-r--r-- 1 root root 7811709 Jul 15 02:28 989e6b19a265d6b8b7934e7ddd7dc07f6e2fc945b3a28dda9b8aecb12cdb30e0 -rw-r--r-- 1 root root 9996168 Jul 15 02:28 af14b6c2f8785723bceb5964c5dec1f0489b7750e9d4ec671e49bfba15d80a39 -rw-r--r-- 1 root root 1738 Jul 15 02:28 b5248ad0c8ff878ad795c17fdad3b0a842cf38e3dc7df97f66012511af803c5a -rw-r--r-- 1 root root 365 Jul 15 02:28 bda1f29e2d9758bd7e97f0e8cd6855a27f1a5015ed64b5d9ef763f5ba88c762e -rw-r--r-- 1 root root 68650396 Jul 15 02:28 d4020e2aa747ca4d0ca2e87305f22f089fd57a696fd19bfa30182316d51b089a -rw-r--r-- 1 root root 50389504 Jul 15 02:28 e9afc4f90ab09248d75c8081b6dfba749a7f7efdac704ced7e0ceb506e02fa4a
なお上記mypod
で利用した定義ファイルはこちらになります。
apiVersion: v1 kind: Pod metadata: name: mypod spec: containers: - name: myfrontend image: nginx volumeMounts: - mountPath: "/var/www/html" name: mypd volumes: - name: mypd persistentVolumeClaim: claimName: tekton-volume