OpenShift Pipelines のご紹介(Tekton Triggers編)

こんにちは、Red Hatでソリューションアーキテクトをしている石川です。

前回の記事ではクラウドネイティブなCI/CDを実現するOpenShift Pipelinesの機能のうち、パイプライン構築のためのコンポーネントであるTekton Pipelinesに焦点を挙げ、その概要をご紹介させて頂きました。 今回の記事では、Tekton Triggersという仕組みを使い、構築したパイプラインを開発の一連の流れの中で自動的に実行する方法をご紹介したいと思います。 Tekton TriggersはTekton Pipelinesと同様に、OpenShift Pipelines Operatorをインストールすることですぐに使い始めることが出来ます。

f:id:jpishikawa:20210730134821p:plain

ユースケース

Tekton Triggersについての説明に入る前にユースケースについておさらいしたいと思います。 前回の記事では、開発者が自身の作業用ブランチからメインブランチに対しプルリクエストを行い、それが承認されたタイミング(merge commitが作成されたタイミング)で、アプリケーションの静的診断、コンテナイメージの作成、レジストリへのプッシュなどを自動的に実施するというパイプラインを考えました。

Tekton Pipelinesでは実行する個々のアクションをTask、それらの実行順を定義するものとしてPipelineというカスタムリソースを作成しました。 これに対しTekton Triggersは開発者のGitへのアクションを契機に、定義されたPipelineを、文字通りトリガーし実行する役割を担っています。

f:id:jpishikawa:20210730135435p:plain

上の図を違う角度から見てみましょう。 GitHubやGitlabなど、一般的なGitリポジトリでは開発者のアクションに応じてWebhookを設定することが出来ます。このWebhookを受け取り、定義されたPipelineを実行するためのPipelineRunリソースを作成するのがTekton Triggersとなります。

f:id:jpishikawa:20210730135456p:plain

Tekton Triggersで登場する概念

Tekton Triggersでは以下の3つカスタムリソースを使用することで上記の動きを実現しています。

  • TriggerTemplate: Webhookを受け取った際に作成するリソース(PipelineRun、TaskRun、等)の定義

  • TriggerBinding: 受け取ったWebhookのヘッダーやボディ部からPipelineRunに渡すパラメータの定義

  • EventListner: Webhookの受け口(Service+Pod)の作成と、使用するTriggerTemplate/TriggerBindingの指定

それぞれの関係性を表すと以下のような図になります。

f:id:jpishikawa:20210730135855p:plain

GitリポジトリからのWebhookはEventListnerが作成したServiceを経由して、同じく作成されたPodが受け取ります。このPodはTriggerBindingの中で定義された通りに受信したWebhookのJSONを加工し必要なパラメータを抜き出すと共に、TriggerTemplateで指定されたリソース(PipelineRun)を作成します。

カスタムリソースの作成

ここからは実際に使用されるカスタムリソースを作成していきたいと思います。 まず初めにTriggerBindingでWebhookから受け取る情報を定義します。

TriggerBinding

apiVersion: triggers.tekton.dev/v1alpha1
kind: TriggerBinding
metadata:
  name: test-trigger-binding
spec:
  params:
    - name: gitrevision
      value: $(body.head_commit.id)
    - name: gitrepositoryurl
      value: $(body.repository.url)

TriggerBindingでは上記のように、spec.params以下でWebhookから受け取った情報のうち、何をこの後のステップで利用していくかを定義します。 Webhookからどのような形でデータが受け渡されるかについては、使用するGitリポジトリによって異なりますが、例えばGitHubの場合こちらのページからイベント種類ごとの内容を確認することが出来るので参考にしてみて下さい。

TriggerTemplate

apiVersion: triggers.tekton.dev/v1alpha1
kind: TriggerTemplate
metadata:
  name: test-pipeline-template
spec:
  params:
    - name: gitrevision
      description: The git revision
      default: master
    - name: gitrepositoryurl
      description: The git repository url
  resourcetemplates:
    - apiVersion: tekton.dev/v1beta1
      kind: PipelineRun
      metadata:
        generateName: test-run-
      spec:
        pipelineRef:
          name: test-pipeline
        params:
          - name: url
            value: $(tt.params.gitrepositoryurl)
          - name: SONAR_HOST_URL
            value: 'http://sonarqube:9000'
          - name: SONAR_PROJECT_KEY
            value: 'test-project'
          - name: IMAGE
            value: 'jpishikawa/mkdocs-test'
        workspaces:
          - name: shared-workspace
            volumeClaimTemplate:
              spec:
                accessModes:
                  - ReadWriteOnce
                resources:
                  requests:
                    storage: 1Gi
          - name: sonar-settings
            emptyDir: {}

続いてTriggerTemplateです。 spec.paramsにはTriggerBindingで定義したパラメータの情報を記載し、spec.resourcetemplates以下でWebhookを受けた際に作成するリソースの情報を記載します。上記の例ではkind: PipelineRunのリソースを作成するようにしていますが、単一のTaskだけを動かしたい場合などはTaskRunを設定することも可能となります。

EventListner

apiVersion: triggers.tekton.dev/v1alpha1
kind: EventListener
metadata:
  name: test-event-listener
spec:
  serviceAccountName: sample-sa
  triggers:
    - bindings:
        - ref: test-trigger-binding
      template:
        ref: test-pipeline-template
      interceptors:
        - github:
            secretRef:
              secretName: github-webhook
              secretKey: webhook-secret
            eventTypes:
              - push
        - cel:
            filter: "body.ref == \"refs/heads/master\""

最後にEventListnerです。 これまで作成したTriggerBinding、TriggerTemplateを参照するよう、spec.triggers以下に記述します。 spec.triggers[].interceptorsという項目では、Webhookに設定された値を検証し、条件を満たさない場合PipelineRunを作成しないというフィルタリングの設定を行っています。上記の例ではWebhookのシークレットを使った送信元リポジトリの認証を行い、トリガー実行をmasterブランチへのpushイベントに対してのみ制限しています。

EventListnerではServiceAccountの設定が必須となっています。前述の通り、EventListnerはTriggerTemplateで指定したリソースを作成することになるため、以下のように必要な権限を持ったService Accountを作成します。

apiVersion: v1
kind: ServiceAccount
metadata:
  name: sample-sa
secrets:
  - name: github-webhook
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: sample-role
rules:
- apiGroups:
  - triggers.tekton.dev
  resources:
  - eventlisteners
  - triggers
  - triggerbindings
  - triggertemplates
  verbs:
  - get
  - list
  - watch
# PipelineRunを作成可能な権限を付与
- apiGroups:
  - tekton.dev
  resources:
  - pipelineruns
  verbs:
  - create
- apiGroups:
  - ""
  resources:
  - configmaps
  verbs:
  - get
  - list
  - watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: sample-role-binding
subjects:
  - kind: ServiceAccount
    name: sample-sa
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: sample-role

ここまででTrigerBinding、TriggerTemplate、EventListnerのマニフェストを作成出来ました。

実際にこれらを実行してみると、Webhookを受けるためのEventListnerのService、Podの作成を確認することが出来ます。

❯ oc get svc
NAME                     TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
el-test-event-listener   ClusterIP   172.30.244.201   <none>        8080/TCP   4m48s

❯ oc get pod        
NAME                                               READY   STATUS      RESTARTS   AGE
el-test-event-listener-58dd7f786c-ql5dz            1/1     Running     0          4m33s

一点注意するポイントとして、Serviceはtype: ClusterIPとして作成されるため、デフォルトではクラスター外部からの通信を受け付けることが出来ません。GitHubなど外部からのWebhookを受け付ける場合にはoc exposeなどのコマンドを実行しエンドポイントを作成してあげましょう。

❯ oc get route                             
NAME                     HOST/PORT                                                               PATH   SERVICES                 PORT            TERMINATION   WILDCARD
el-test-event-listener   el-test-event-listener-cicd.apps.mycluster.193b.sandbox80.opentlc.com          el-test-event-listener   http-listener                 None

これでEventListnerがGitリポジトリのWebhookを受け付ける準備が完了しました。

Webhookの設定

次にGitリポジトリ側でWebhookの設定をしましょう。 今回はGitHubで設定を行いたいと思います。 Webhookを設定したいGitHubのリポジトリにアクセスし、"Settings"→"Webhooks"から新規のWebhookを設定します。

f:id:jpishikawa:20210730141737p:plain Payload URLには先ほど作成したWebhookのエンドポイントのURLを設定、Content typeをapplication/json 、Secretには認証に使用するシークレットキーを入力します。 今回はpushイベントを対象としWebhookを作成しますが、より詳細な設定を行うことも可能です。

Pipelineの実行

ここまででGitのpushイベントに応じてPipelineを実行する準備が整いました。 最後に実際に新規のcommitを作成し、正しくPipelineが動くか確認してみましょう。

リポジトリ上で新規ファイルを作成するなどの操作を行い新規のcommitを作成すると、それに応じて先ほど設定したWebhookが作成されます。 作成されたWebhookの履歴は設定画面から確認出来ます。 f:id:jpishikawa:20210730141803p:plain

これまでの設定が正しく行われていればTekton Triggersにより新たなPipelineRunが作成されます。 OpenShiftのコンソールを見ると新たなPipelineRunが作成され、Pipelineが実行されていることを確認することが出来ます。 f:id:jpishikawa:20210730141821p:plain

f:id:jpishikawa:20210730141835p:plain

これでGitのアクションからPipeline実行までの一連の動きについて確認することが出来ました。

まとめ

前回から2回に分けてOpenShift Pipelinesについての内容をご紹介しました。

昨今では開発を高速に、かつセキュアにしていくためにはCI/CDによる自動化の仕組みが不可欠となっています。Tektonには複数のカスタムリソースが存在するため、初めは取っ付きづらさを感じるかもしれませんが、一つ一つの役割を理解すると、シンプルで拡張性を持ったパイプラインを構築することが出来るということが理解出来たのではないでしょうか。 皆さんも是非OpenShift Pipelinesを使ったCI/CDパイプラインの構築にチャレンジしてみて下さい。

* 各記事は著者の見解によるものでありその所属組織を代表する公式なものではありません。その内容については非公式見解を含みます。