KnativeなCI/CDツールのTektonを使って、GKE上でイメージのビルド、GKEへのデプロイを行う
What is Tekton?
Tektonはk8sネイティブなCI/CDを構築するためのプロジェクトです。 CDF (Continuous Delivery Foundation)のメンバーでもあります。
Tektonの目指しているゴールとしてはクラウドプラットフォームやオンプレ環境などに依存しないCI/CD環境を構築することのようです。
READMEを参考にするとTektonは、
1. Cloud Native
・Kunernetes上で実行可能
・コンテナを実行ブロックの単位として扱える
2. Decoupled
・一つのパイプラインで複数のクラスタにデプロイできる
・パイプラインを構築するTaskは単独で実行可能
・複数のgitリポジトリを簡単に扱える
3. Typed
・リソースは入力によって変更可能
(例:例えばImageのビルドを簡単にkanikoにしたりbuildkitにしたりできる)
Tektonに関してもっとよく知りたい方にはCloud Next '19の以下のセッションが参考になると思います。
Tektonに登場する概念
Tektonに登場する概念ついて説明します。
Step
もっとも基本的な単位、kubernetesのPodと同じように定義できる
Task
複数のStepによって構成される。Stepごとに動作させるコンテナやリソースは指定できる。
Pipline
Taskを組み合わせたもの、実行する順番などを指定することもできる。また、複数あるTaskは同じノードで実行される訳ではない。
PiplineResources
Taskで利用されるインプットやアウトプットを指定するためのリソースとしてPiplineResources
があります。
例えば、
・TaskにインプットとしてGitHubのソースコードを利用
・TaskのアウトプットとしてDockerイメージを作り出す。また、作ったイメージはGCRやDockerHubなどのイメージレジストリーにPushされる
といった場合にそれぞれの入力や出力をPiplineResources
を利用して定義します。
TaskRun && PipelineRun
TaskやPipelineを実際に実行するためのリソースがTaskRun
とPiplineRun
です。
TaskRun
とPipelineRun
ではこれまでに定義したTask、Pipelineに対してPipelineで定義したリソースおよび、トリガーの情報、引数などを実際に渡します。
イメージとしてはTask、Pipelineでどのようなタイプのリソースを使ってどんな処理をするのかを定義しておいて、TaskRunとPipelineRunで実際のリソースを渡してあげるようなイメージです。
PipelineResourceとTask、TaskRunの関係を大雑把に示したものが以下の図になります。Pipelineは少し違いますが考え方は大体同じです。
それでは実際に、Tektonを使ってGoのコードのテスト、Dockerイメージのビルド&GCRへのプッシュ、GKEへのデプロイを行って行きます。
下準備
GKEクラスタの構築とTektonのデプロイ
Google Cloudのクイックスタートを参考にして、GKEクラスタを構築、およびkubectlコマンドでGKEクラスタにアクセスできるようにしてきます。
またこちらを参考にcluster-admin権限を取得、TektonPipelineリソースをGKEにデプロイします。TektonリソースはCRDsとしてデプロイされるのでコマンド一つでGKE上で使用できるようになります。
Kanikoのためのサービスアカウントの登録
今回はDockerイメージのビルドにKanikoを利用しているのでこちらのサイトを参考にしてGCRのeditor権限を持ったサービスアカウントを作成してSecretとして登録しておきます。今回は単にSecretとして利用していますが、プロダクションで利用する際にはKMSなどを使用した方がセキュリティ的に安全です。
ClusterRoleBindの設定
今回のデモではkubernetesクラスタにデプロイするので、クラスタのAdmin権限を持ったServiceAccountが必要になります。exampleのClusterRoleBindingリソースを参考にAdmin権限を付与したServiceAccountを登録します。今回の例ではdefaultに付与しています。GKEにうまくデプロイできない場合にはこのあたりをもう一度見直す必要があります。
流れ
今回のデモでは、
- Goアプリケーションのテスト
- Dockerイメージのビルド
- kubectlコマンドでGKEクラスタにそのままデプロイ
の順番で行います。
PipelineResourcesの登録
まずはPipelineで利用するPipelineResources
を登録します。
今回使用するリソースは、アプリケーションのGitHubリポジトリのURLと、イメージをPushするGCRを指定しています。以下のようなYamlファイルを用意してapplyします。
apiVersion: tekton.dev/v1alpha1 kind: PipelineResource metadata: name: tekton-sample-resource-image spec: type: image params: - name: url value: gcr.io/${YOUR_PROJECT}}/tekton-sample-image # modify to your GCR path. --- apiVersion: tekton.dev/v1alpha1 kind: PipelineResource metadata: name: tekton-sample-resource-git spec: type: git params: - name: revision value: master - name: url value: https://github.com/tommy-sho/tekton-sample.git # modify to your git repository
ここで重要なのは、spec.type
の部分です。Tektonでは各リソースの種類がtype
として指定されています。
kubectl get PipelineResource
でPipelineResource
が以下のように登録されていることが確認できます。
NAME AGE tekton-sample-resource-git 6m tekton-sample-resource-image 6m
Taskの登録
イメージのビルドとプッシュ
次にTask
を登録します。KanikoによるDockerイメージのビルドとGCRへのプッシュを行うタスクを以下のようなYamlファイルで定義します。
apiVersion: tekton.dev/v1alpha1 kind: Task metadata: name: build-image spec: inputs: resources: - name: app type: git params: - name: pathToDockerFile description: The path to the dockerfile to build default: /workspace/app/Dockerfile - name: pathToContext description: The build context used by Kaniko (https://github.com/GoogleContainerTools/kaniko#kaniko-build-contexts) default: /workspace/app/ # <- ここのpathはworkspace/resource名/のプレフィックスがつく outputs: resources: - name: builtImage type: image steps: - name: build-and-push image: gcr.io/kaniko-project/executor command: - /kaniko/executor args: - --dockerfile=${inputs.params.pathToDockerFile} - --destination=${outputs.resources.builtImage.url} - --context=${inputs.params.pathToContext} volumeMounts: - name: kaniko-secret mountPath: /secret env: - name: GOOGLE_APPLICATION_CREDENTIALS value: /secret/kaniko-secret.json volumes: - name: kaniko-secret secret: secretName: kaniko-secret
ここで注意する必要があるのはparmsで指定しているpathのプレフィックスです。現在(2019/5)では、type:git
のPipelineResource
は/workspace/リソース名
以下に配置されます。今回は、input.resource
にapp
という名前のリソースにしているので/workspace/app
以下にGitHubのコードが展開されます。
GKEへのデプロイ
GKEへのデプロイはkubectlが利用できるlachie83/k8s-kubectl
イメージを利用しました。Yamlファイルは以下のようになっています。
apiVersion: tekton.dev/v1alpha1 kind: Task metadata: name: deploy-kubectl spec: inputs: resources: - name: app type: git params: - name: path description: Path to the manifest to apply steps: - name: run-kubectl image: lachlanevenson/k8s-kubectl command: ['kubectl'] args: - 'apply' - '-f' - '${inputs.params.path}'
単純にkubetcl apply
を叩いているだけです。デプロイに使うマニフェストを渡しています。
Pipelineの設定
さて、PipelineResourceとTaskが定義できたのでいよいよPipelineを定義していきます。Pipelineを定義したYamlファイルは以下のようになっています。
apiVersion: tekton.dev/v1alpha1 kind: Pipeline metadata: name: sample-pipline spec: resources: - name: app type: git - name: image type: image tasks: - name: build-push-image taskRef: name: build-image params: - name: pathToDockerFile value: /workspace/app/Dockerfile - name: pathToContext value: /workspace/app resources: inputs: - name: app resource: app outputs: - name: builtImage resource: image - name: deploy runAfter: [build-push-image] taskRef: name: deploy-kubectl params: - name: path value: /workspace/app/manifest.yaml resources: inputs: - name: app resource: app
特に難しいことはありません。spec.resources
でPipelineの中で利用するPipelineResource
を定義しておいて、spec.tasks
で実行するTaskを並べています。
また、それぞれのTaskに対して、使用するparams
とresources
を定義します。
もしTaskに実行する順番(ex. line -> test -> build -> deployなど)がある場合には、runAfter
で依存関係を明示することができます。宣言しなかった場合にはそれぞれPodが立って並列で実行されます。
PipelineRunの定義
さてまだあります。PipelineRun
です。Pipeline
では最初にPipelineResource
を定義して、Taskの中でどのresource
を使用するかを定義していますが、またresource
のタイプのみしか明らかになっていないので、PipelineRun
で実際にどのPipelineResource
を使用するのかを宣言します。
PipelineRun
は以下のようにしました。
apiVersion: tekton.dev/v1alpha1 kind: PipelineRun metadata: name: pipeline-run spec: pipelineRef: name: sample-pipline trigger: type: manual resources: - name: app resourceRef: name: tekton-sample-resource-git - name: image resourceRef: name: tekton-sample-resource-image
使用するPipelineResource
とtrigger
が定義されています。最初の方で定義したPipelineResource
とPipeline
を結びつけています。
現在(2019/5)ではtrigger
はGitHubへのPushや、Slackなどを使ったチャットオペレーションには対応していない状態です。こちらのRoadmapによると2019年以内には対応する予定のようです。
実際にPipelineRunしてみる
これで準備ができたので、今までに定義したリソースをapplyした後で、PipelineRun
をapplyしてみます。
kubectl get tekton-pipelines
コマンドでTektonPipelineのリソースが確認できます。
$ > kubectl get tekton-pipelines NAME AGE pipeline.tekton.dev/sample-pipline 2m NAME STATUS STARTTIME COMPLETIONTIME pipelinerun.tekton.dev/pipeline-run True 2m 37s NAME AGE pipelineresource.tekton.dev/tekton-sample-resource-git 2m pipelineresource.tekton.dev/tekton-sample-resource-image 2m NAME AGE task.tekton.dev/build-image 2m task.tekton.dev/deploy-kubectl 2m NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME taskrun.tekton.dev/pipeline-run-build-push-image-6jbcm True 2m 43s taskrun.tekton.dev/pipeline-run-deploy-782tv True 43s 37s
一番下の部分がPipelineの中の各TaskでSUCCEEDED
が True
になっていることがわかります。
Taskが正しく完了しなかった場合にはSUCCEEDED
がFalse
になります。
実際にGKEにデプロイされたかどうかを確認します。
$ > kubectl get pods NAME READY STATUS RESTARTS AGE pipeline-run-build-push-image-dhjvh-pod-51492d 0/3 Completed 0 1m pipeline-run-deploy-wr7cm-pod-1a4ff9 0/3 Completed 0 26s sample-6d96898c4c-hk847 1/1 Running 0 20s sample-6d96898c4c-tr4hd 1/1 Running 0 20s
正しくデプロイされているようです。Taskで利用されたPodも確認できます。
まとめ
KnativeなCI/CDツールとして期待されているTektonを触って見ました。 今回は触っていませんが、DashBoradを作成してPipelineの実行状況の確認などもできるようです。 リポジトリをみるとTekton自体まだ開発が始まって間もないプロジェクトなのでまだ機能的には足りないと感じる部分はありますが、Kubernetes上でCI/CDパイプラインを構築できる点やカスタマイズ性の高さは触っていても非常に高いと感じたのでこれからの開発に要注目して行きたいとおもいます。 あとこのマスコットキャラも近未来感があって好きだったりします。
もし、うまく動かない、記事に変な点があったりする場合には気軽にコメントや連絡してください。