今日から始めるKubernetes-nativeなGitOps【CI編 ~webhook設定~】

この記事は [WESEEK Advent Calendar](https://qiita.com/advent-calendar/2020/weseek) 2020 21日目の記事です。

こんにちは！ [WESEEK, Inc.](https://weseek.co.jp/) で長期インターンをさせていただいております [yamagai] (https://twitter.com/yamagai_0919)と申します。

前回は [今日から始めるKubernetes-nativeなGitOps【CI編 ~pipeline構築~】](https://qiita.com/yamagai/items/4344875ef479a664f9ff)を書きましたが、
今回は [Tekton Triggers](https://github.com/tektoncd/triggers) を使った Webhook の設定について書きます。


前回に引き続き、CI/CDを導入する対象のプロジェクトはフロントエンドが Next.jsでバックエンドが Rails のアプリです。
フロントエンドとバックエンドは一つのリポジトリにまとめています。


# 参考リポジトリ
こちらも、前回に引き続き以下のリポジトリを使います。

- CIを使用するプロジェクトのリポジトリ
    - https://github.com/bamboooo-dev/alcatraz
    - /api 以下がバックエンドで、/ui 以下がフロントエンド

- 今回使用する CI の設定を書いたマニフェストのリポジトリ
    - https://github.com/bamboooo-dev/alcatraz-tekton

- アプリのマニフェストリポジトリ
    - https://github.com/bamboooo-dev/alcatraz-manifests

# Tekton Triggers を用意する

### インストール
- 公式ドキュメント
    - https://github.com/tektoncd/triggers/blob/v0.10.1/docs/install.md#installing-tekton-triggers-1

**前提**

- [Tekton Pipeline](https://github.com/tektoncd/pipeline)の導入が済んでいる

インストールは以下のコマンド

```jsx
kubectl apply --filename https://storage.googleapis.com/tekton-releases/triggers/latest/release.yaml
```

入っているようですね

```jsx
(⎈ |jukai:tekton-pipelines)❯ kubectl get pod
NAME                                                     READY   STATUS      RESTARTS   AGE
image-pipelinerun-api-image-build-push-ppvzp-pod-cvd4z   0/4     Completed   0          46h
image-pipelinerun-ui-image-build-push-crj7d-pod-ckwhb    0/4     Completed   0          46h
tekton-dashboard-6bdbc955c6-crfpt                        1/1     Running     0          2d22h
tekton-pipelines-controller-6486ffc547-7xnwl             1/1     Running     0          2d22h
tekton-pipelines-webhook-77d7486ddf-hscxk                1/1     Running     0          2d22h
tekton-triggers-controller-dc77dfb98-mwkj2               1/1     Running     0          2d
tekton-triggers-webhook-856b74b94d-qd8mj                 1/1     Running     0          2d
```

# Tekton Triggers の概念を理解する

Tekton Pipeline にも`PipelineRun`などの複数のカスタムリソースがありましたが、Tekton Triggers にもいくつかカスタムリソースがあります。
使い始める前にそれらの概念を説明します。

### TriggerTemplate
trigger された時に実行する PipelineRun と pipeline で使用する params を指定するカスタムリソース

###  TriggerBinding
Trigger に渡す params (TriggerTemplate 内の spec.params で設定した params の値) を記述するカスタムリソース
namespace の設定は必須

###  EventListener
TriggerTemplate と TriggerBinding を結びつけるカスタムリソース
これが作成されると、リクエストを受け付ける Service が作成される仕組み
作成された Service が Github Push events などを受け付けて、TrigggerTemplate に基づき Pipeline を実行する


![TriggerFlow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640444/1e3e9571-1d3c-c27a-9a16-893254e24a03.png)
(引用元: https://github.com/tektoncd/triggers/tree/master/docs)


# Webhook を設定する

### trigger側の設定
以下の三つのカスタムリソースはリポジトリの[ここ](https://github.com/bamboooo-dev/alcatraz-tekton/blob/main/tekton/triggers/triggers.yaml)で定義しています。

TriggerTemplate と TriggerBinding については特に注意点はありませんが、EventListener についてはオプションがあるので利用しましょう。

こちらの[ドキュメント](https://github.com/tektoncd/triggers/blob/v0.10.2/docs/eventlisteners.md#resources)にある通り、

```jsx
spec:
  resources:
    kubernetesResource:
      serviceType: NodePort
```
このようにすることで EventListener により作成される Service の Type が指定できます。

今回は`NodePort`として作成しましょう。

上記のマニフェストを適用すると以下のように Service が作成されます。

```jsx
(⎈ |jukai:tekton-ci)❯ kubectl get svc
NAME                          TYPE       CLUSTER-IP    EXTERNAL-IP   PORT(S)          AGE
el-alcatraz-tekton-listener   NodePort   10.43.84.50   <none>        8080:32640/TCP   134m
```

32640番ポートが開いたみたいですね。

### github webhook の設定
次は Github 側の設定をしましょう。

リポジトリの [Settings] → [Webhooks] から Add webhook を選択しましょう。

![スクリーンショット 2020-12-21 21.48.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640444/eda34c14-f663-128f-9c46-aabcdd7aa1a5.png)


それでは、今回は
Payload URL → `http://(ノードのIPアドレス):(ポート番号)`
Content Type → `application/json`
として webhook は push イベントごとにしておきましょう。

![スクリーンショット 2020-12-21 21.53.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640444/9284dabf-ab68-f49b-8066-e71558dbc45d.png)

これで webhook の設定は完了です！

# 動作確認
それでは、ブランチの push により `PipelineRun` が実行されるかどうかを見ていきましょう。

まずは、Tekton Dashboard をブラウザから見られるようにしましょう。

```jsx
(⎈ |jukai:tekton-pipelines)❯ kubectl port-forward svc/tekton-dashboard 9097:9097 -n tekton-pipelines
```

次に push です。

```jsx
~/bamboooo/alcatraz tekton-test*
(⎈ |jukai:tekton-ci)❯ git add .

~/bamboooo/alcatraz tekton-test*
(⎈ |jukai:tekton-ci)❯ git commit -m "tekton test"
[tekton-test bbbb189] tekton test
 1 file changed, 1 insertion(+), 1 deletion(-)

~/bamboooo/alcatraz tekton-test
(⎈ |jukai:tekton-ci)❯ git push origin tekton-test
Enumerating objects: 15, done.
Counting objects: 100% (15/15), done.
Delta compression using up to 4 threads
Compressing objects: 100% (6/6), done.
Writing objects: 100% (8/8), 640 bytes | 320.00 KiB/s, done.
Total 8 (delta 4), reused 0 (delta 0)
remote: Resolving deltas: 100% (4/4), completed with 4 local objects.
remote:
remote: Create a pull request for 'tekton-test' on GitHub by visiting:
remote:      https://github.com/bamboooo-dev/alcatraz/pull/new/tekton-test
remote:
To github.com:bamboooo-dev/alcatraz.git
 * [new branch]      tekton-test -> tekton-test
```

ダッシュボードを見てみると...
![スクリーンショット 2020-12-21 21.59.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640444/70522c53-88eb-acae-c86e-f086ec84a99e.png)

`PipelineRun`で設定した name の後ろにハッシュがくっ付いた名前の PipelineRun が実行されていますね！
成功です。

### webhook イベントのフィルタリング
ただ、これだとどのブランチが push されても image の build & push が実行されてしまいます。
そこで main ブランチの push時のみに実行されるように設定していきましょう。

Tekton には[`Interceptor`](https://github.com/tektoncd/triggers/blob/v0.10.2/docs/eventlisteners.md#interceptors)という、EventListener がイベント受信時にリクエストに対してフィルターを行う機能が存在しているのでこれを使っていきましょう！

今回は[CEL Interceptor](https://github.com/tektoncd/triggers/blob/v0.10.2/docs/eventlisteners.md#cel-interceptors)を使用していきます。

```jsx
triggers:
    - bindings:
      - ref: alcatraz-tekton-pipelinebinding
      template:
        ref: alcatraz-tekton-triggertemplate
      interceptors:
        - cel:
            filter: body.ref == "refs/heads/main"
```
上記のように EventListener のマニフェストの `spec.triggers` 以下に `interceptors` を追加しました。
これにより main ブランチの時のみ PipelineRun が実行されるはずです。見てみましょう。

先ほどの tekton-test ブランチを main ブランチにマージする pull request を作成しました。
これがマージされたら PipelineRun が実行されるはずです。
![スクリーンショット 2020-12-21 22.28.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640444/b8e9cf7a-c8c0-74fe-8a55-beb0b97eb846.png)
`interceptor`ではなく`inceptor`になっているのと、github actions が落ちているのは気にしないでください。

マージしました。
![スクリーンショット 2020-12-21 22.31.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640444/f3d1cf96-0400-bf61-4c2c-86e26c33038e.png)

ダッシュボードを見てみると...![スクリーンショット 2020-12-21 22.33.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640444/bbc78810-3ccd-ddb9-3a57-cccbcc636d58.png)

おおっ、PipelineRun が走っていますね、成功です。
ただ何やらエラーになっていますね...

これはまた別の記事で紹介しようと思います。

ちなみに、よりセキュアにするなら GitHub Interceptors を使って Secret に設定してあるのと同じアクセストークンを持つリクエストの場合だけ実行するように設定することも可能です。


# 最後に

今回は、Tekton Triggers と Webhook の設定をしてブランチのpush時に image の build と push が実行されるようにしていきました。

実はこれだと、フロントとバックエンドが一つのリポジトリにまとまっているために main ブランチの push 時にフロントのイメージもバックエンドのイメージも毎回 push されてしまうという状態です。

フロントとバックエンドのリリースサイクルを揃えざるをえないことになってしまうのはよろしくないので余裕があれば次回の記事で改修してみようと思います。

また次回の記事では、Pipeline にマニフェストリポジトリにイメージタグを更新する pull request を送る task を追加し、それがマージされ次第CDされるようにしていきましょう。





※この記事は [WESEEK Tips wiki](https://tips.weseek.co.jp/) に 2020/12/21 に投稿された記事の転載です。
Tips wiki では、IT企業の技術的な情報やプロジェクトの情報を公開可能な範囲で公開しています。