KnativeなCI/CDツールのTektonを使って、GKE上でイメージのビルド、GKEへのデプロイを行う

What is Tekton?

Tektonk8sネイティブなCI/CDを構築するためのプロジェクトです。 CDF (Continuous Delivery Foundation)のメンバーでもあります。

f:id:tomiokasyogo:20190506004157p:plain

Tektonの目指しているゴールとしてはクラウドプラットフォームやオンプレ環境などに依存しないCI/CD環境を構築することのようです。

READMEを参考にするとTektonは、

1. Cloud Native

・Kunernetes上で実行可能

・コンテナを実行ブロックの単位として扱える

2. Decoupled

・一つのパイプラインで複数のクラスタにデプロイできる

・パイプラインを構築するTaskは単独で実行可能

・複数のgitリポジトリを簡単に扱える

3. Typed

・リソースは入力によって変更可能

(例:例えばImageのビルドを簡単にkanikoにしたりbuildkitにしたりできる)

Tektonに関してもっとよく知りたい方にはCloud Next '19の以下のセッションが参考になると思います。

www.youtube.com

Tektonに登場する概念

Tektonに登場する概念ついて説明します。

Step

もっとも基本的な単位、kubernetesのPodと同じように定義できる

Task

複数のStepによって構成される。Stepごとに動作させるコンテナやリソースは指定できる。

Pipline

Taskを組み合わせたもの、実行する順番などを指定することもできる。また、複数あるTaskは同じノードで実行される訳ではない。

f:id:tomiokasyogo:20190505192759p:plain f:id:tomiokasyogo:20190505192947p:plain

PiplineResources

Taskで利用されるインプットやアウトプットを指定するためのリソースとしてPiplineResourcesがあります。 例えば、

・TaskにインプットとしてGitHubソースコードを利用

・TaskのアウトプットとしてDockerイメージを作り出す。また、作ったイメージはGCRやDockerHubなどのイメージレジストリーにPushされる

といった場合にそれぞれの入力や出力をPiplineResourcesを利用して定義します。

TaskRun && PipelineRun

TaskやPipelineを実際に実行するためのリソースがTaskRunPiplineRunです。 TaskRunPipelineRunではこれまでに定義したTask、Pipelineに対してPipelineで定義したリソースおよび、トリガーの情報、引数などを実際に渡します。 イメージとしてはTask、Pipelineでどのようなタイプのリソースを使ってどんな処理をするのかを定義しておいて、TaskRunとPipelineRunで実際のリソースを渡してあげるようなイメージです。

PipelineResourceとTask、TaskRunの関係を大雑把に示したものが以下の図になります。Pipelineは少し違いますが考え方は大体同じです。

f:id:tomiokasyogo:20190505195412p:plain

それでは実際に、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にうまくデプロイできない場合にはこのあたりをもう一度見直す必要があります。

流れ

今回のデモでは、

  1. Goアプリケーションのテスト
  2. Dockerイメージのビルド
  3. 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 PipelineResourcePipelineResourceが以下のように登録されていることが確認できます。

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:gitPipelineResource/workspace/リソース名以下に配置されます。今回は、input.resourceappという名前のリソースにしているので/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に対して、使用するparamsresourcesを定義します。

もし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

使用するPipelineResourcetriggerが定義されています。最初の方で定義したPipelineResourcePipelineを結びつけています。

現在(2019/5)ではtriggerGitHubへの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でSUCCEEDEDTrueになっていることがわかります。 Taskが正しく完了しなかった場合にはSUCCEEDEDFalseになります。 実際に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パイプラインを構築できる点やカスタマイズ性の高さは触っていても非常に高いと感じたのでこれからの開発に要注目して行きたいとおもいます。 あとこのマスコットキャラも近未来感があって好きだったりします。

もし、うまく動かない、記事に変な点があったりする場合には気軽にコメントや連絡してください。

参考サイト、記事

github.com

github.com

github.com

www.youtube.com