OpenShift Pipelines のエラー通知を Slack に送信してみた

OpenShift Pipelines (Tekton) は、OpenShift 上で CI/CD パイプラインを構築するためのツールです。開発者としては、パイプラインのエラーや失敗が発生したら困るので、リアルタイムで知りたいと思います。そこで、Tekton Catalog を参考にしてエラー通知を Slack に送信する仕組みを構築してみました。

前提条件

  • OpenShift 4 クラスターが構築済みであること
  • OpenShift Pipelines Operator がインストールされていること
  • oc CLI がインストール済みであること
  • OpenShift Pipelines でパイプラインが構築済みであること
  • Slack に通知を送信するワークスペース、チャンネルが用意されていること

CloudEvents について

CloudEvents は、イベントデータの標準化を目的とした CNCF のプロジェクトです。OpenShift Pipelines は CloudEvents をサポートしており、パイプラインの状態をイベントとして発行することができます。OpenShift Pipelines の CloudEvents は下記リンクのようになっています。

CloudEvents の HTTP ヘッダーの一部は下記のような形で送られてきます。PipelineRun が Failed の時、”Ce-Type” が “dev.tekton.event.pipelinerun.failed.v1” で送られてくる仕様なので、これを利用したいと思います。

"Ce-Id": "77f78ae7-ff6d-4e39-9d05-b9a0b7850527",
"Ce-Source": "/apis/tekton.dev/v1beta1/namespaces/default/taskruns/curl-run-6gplk",
"Ce-Specversion": "1.0",
"Ce-Subject": "curl-run-6gplk",
"Ce-Time": "2021-01-29T14:47:58.157819Z",
"Ce-Type": "dev.tekton.event.taskrun.unknown.v1",

構築の概要

図のような構成を構築したいと思います。まず初めに、Pipeline Run からの CloudEvents を EventListener で受け取ります。次に EventListener は Pipeline Run が失敗したことを検知して TriggerBinding と TriggerTemplate から Slack に Webhook を送信するような Task Run を動作させます。Slack の指定したチャンネルに Pipeline Run 失敗通知が届けば動作完了です。

Slack App の準備

Slackに通知を送信するためには、Slack App を作成し、適切な権限を付与する必要があります。Slack App の Webhook URL を取得し、OpenShift Pipelines で使用できるようにします。

Slack App のページから Create New App をクリックします。

From scratch をクリックします。

App Name を入力して、Slack App を追加するワークスペースを選択します。 

Basic Information が表示されれば完了です。次に Incoming Webhooks をクリックします。

デフォルトでは Activate Incoming Webhooks は Off になっているので、クリックして On にします。

On になれば完了です。Add New Webhook to Workspace をクリックします。

Slack App が Slack ワークスペースに投稿する権限を与えます。投稿することのできる投稿先チャンネルを指定して許可します。

Webhook URL が作成されれば完了です。この Webhook URL は後に使うので Copy して保存しておきます。

 

Slack の通知を送信したいチャンネルに作成した Slack App が追加されていることを確認します。

Task の作成

Tekton Catalog を参考に Slack にメッセージを送信するための Task を作成します。この Task はシンプルなもので、Slack の Webhook URL に対して POST リクエストを送信します。

Secret を作成します。stringData の url に前項で保存した Slack API の Webhook URLを入力します。

webhook-url.yaml

kind: Secret
apiVersion: v1
metadata:
  name: webhook-secret
stringData:
  # 下記に前項で保存した Slack API の Webhook URLを入力
  url: https://hooks.slack.com/services/

下記コマンドを実行してWebhook URLを持つシークレットを作成します。

$ oc apply -f  webhook-url.yaml
secret/webhook-secret created

下記コマンドを実行してシークレットが作成されていることを確認します。

$ oc get secret

NAME                       TYPE                      DATA   AGE
︙
webhook-secret             Opaque                    1      17s

Task の編集をします。params の中の bot-name の default を作成した Slack App の名前に置き換えます。

send-to-webhook-slack.yaml

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: send-to-webhook-slack
  labels:
    app.kubernetes.io/version: "0.1"
  annotations:
    tekton.dev/pipelines.minVersion: "0.12.1"
    tekton.dev/categories: Messaging
    tekton.dev/tags: messaging
    tekton.dev/displayName: "Send message to Slack Channel"
    tekton.dev/platforms: "linux/amd64,linux/s390x,linux/ppc64le"
spec:
  description: >-
    These tasks post a simple message to a slack channel.


    This task uses Incoming Webhooks of slack to send the message.


  params:
  - name: webhook-secret
    type: string
    description: secret name of the slack app webhook URL (key is url)
  - name: message
    type: string
    description: plain text message
    default: 'Webhook-test'
  - name: bot-name
    type: string
    description: plain text message
  # 下記を作成した Slack App の名前に変更
    default: 'sample app'
  - name: icon-emoji
    type: string
    description: plain text message
    default: ':ロボット:'
  steps:
  - name: post
    image: docker.io/curlimages/curl:7.70.0@sha256:031df77a11e5edded840bc761a845eab6e3c2edee22669fb8ad6d59484b6a1c4 #tag: 7.70.0
    script: |
      #!/usr/bin/env sh
      MESSAGE=$(echo "${MESSAGE}" | sed -e 's/\"/\\\\"/g')
      BOTNAME=$(echo "${BOTNAME}" | sed -e 's/\"/\\\\"/g')
      JSON="{\"text\": \"${MESSAGE}\", \"username\": \"${BOTNAME}\", \"icon_emoji\": \"${EMOJI}\"}"
      curl -X POST -H 'Content-Type: application/json' --data "${JSON}" "${URL}"
    env:
    - name: URL
      valueFrom:
        secretKeyRef:
          name: $(params.webhook-secret)
          key: url
    - name: MESSAGE
      value: $(params.message)
    - name: BOTNAME
      value: $(params.bot-name)
    - name: EMOJI
      value: $(params.icon-emoji)

下記コマンドを実行してタスクを作成します。

$ oc create -f  send-to-webhook-slack.yaml
task.tekton.dev/send-to-webhook-slack created

下記コマンドを実行してタスクが作成されていることを確認します。

$ tkn task ls
NAME                    DESCRIPTION              AGE
send-to-webhook-slack   These tasks post a ...   43 seconds ago

TriggerBinding の作成

TriggerBinding は、イベントデータを Task に渡すための設定です。パイプラインのエラー情報を Task に渡すための TriggerBinding を作成します。

下記コマンドを実行して TriggerBinding を作成します。

$ oc apply -f trigger_binding.yaml
triggerbinding.triggers.tekton.dev/failure-trigger-binding created

trigger_binding.yaml

apiVersion: triggers.tekton.dev/v1beta1
kind: TriggerBinding
metadata:
  name: failure-trigger-binding
spec:
  params:
  - name: message
    value: "Failed pipeline $(body.pipelineRun.metadata.name)"

下記コマンドを実行して TriggerBinding が作成されていることを確認します。

$ tkn triggerbinding ls
NAME                      AGE
failure-trigger-binding   3 minutes ago

TriggerTemplate の作成

TriggerTemplate は、イベントに応じて実行するリソースのテンプレートです。Task を実行するための TriggerTemplate を作成します。

下記コマンドを実行して TriggerTemplare を作成します。

$ oc apply -f trigger_template.yaml
triggertemplate.triggers.tekton.dev/failure-trigger-template created

trigger_template.yaml

apiVersion: triggers.tekton.dev/v1beta1
kind: TriggerTemplate
metadata:
  name: failure-trigger-template
spec:
  params:
    - name: message
    - name: bot-name
  resourcetemplates:
  - apiVersion: tekton.dev/v1beta1
    kind: TaskRun
    metadata:
      generateName: failure-handler
    spec:
      params:
      - name: webhook-secret
        value: webhook-secret
      - name: message
        value: $(tt.params.message)
      - name: bot-name
        value: $(tt.params.bot-name)
      taskRef:
        name: send-to-webhook-slack

下記コマンドを実行して TriggerTemplate が作成されていることを確認します。

$ tkn triggertemplate  ls
NAME                       AGE
failure-trigger-template   4 minutes ago

EventListener の作成

OpenShift Pipelines で発生した CloudEvents を受け取るための EventListener を作成します。この EventListener は、interceptors の cel を利用して CloudEvents の内容を判別し、 HTTP ヘッダーの ”Ce-Type” が “dev.tekton.event.pipelinerun.failed.v1”  の場合に Slack にメッセージを送信する Task を実行します。

下記コマンドを実行して EventListener を作成します。

$ oc apply -f failure-event-listener.yaml
eventlistener.triggers.tekton.dev/failure-event-listener created

failure-event-listener.yaml

apiVersion: triggers.tekton.dev/v1beta1
kind: EventListener
metadata:
  name: failure-event-listener
spec:
  serviceAccountName: pipeline
  triggers:
    - name: failure-trigger
      interceptors:
        - ref:
            name: cel
          params:
          - name: "filter"
            value: "header.match('ce-type', 'dev.tekton.event.pipelinerun.failed.v1')"
      bindings:
      - ref: failure-trigger-binding
      template:
        ref: failure-trigger-template
  resources:
       kubernetesResource:
         spec:
           template:
             spec:
               serviceAccountName: pipeline
               containers:
               - resources:
                   requests:
                     cpu: "250m"
                     memory: "64Mi"
                   limits:
                     cpu: "500m"
                     memory: "128Mi"

下記コマンドを実行して EventListener が作成されていることを確認します。

$ tkn eventlistener ls
NAME                     AGE            URL                                                                   AVAILABLE
failure-event-listener   1 minute ago   http://el-failure-event-listener.ci-pipeline.svc.cluster.local:8080   True

下記コマンドを実行して EventListener をルートとして公開します。

$ oc expose svc el-failure-event-listener
route.route.openshift.io/el-failure-event-listener exposed

下記コマンドを実行して EventListener がルートとして公開されていることを確認します。

$ oc get route
NAME                        HOST/PORT                                               PATH                        SERVICES                      PORT            TERMINATION   WILDCARD
el-failure-event-listener   el-failure-event-listener-ci-pipeline.apps.xxx          el-failure-event-listener   http-listener                 None

下記コマンドを実行して EventListener の webhook-url を確認します。この webhook-url は次項の CloudEvents の設定で使用します。

$ echo "URL: $(oc  get route el-failure-event-listener --template='http://{{.spec.host}}')"
URL: http://el-failure-event-listener-ci-pipeline.apps.xxx

CloudEvents の設定

cloud_events_config.yaml の data.sink パラメータに前項で取得した EventListener の webhook-url を入力します。

cloud_events_config.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: config-events
  namespace: openshift-pipelines
  labels:
    app.kubernetes.io/instance: default
    app.kubernetes.io/part-of: openshift-pipelines
data:
  formats: tektonv1
  # ここに前項で取得した EventListener の webhook-url を入力
  sink: http://el-failure-event-listener-ci-pipeline.apps.xxx

下記コマンドを実行して CloudEvents 設定をします。

$ oc apply -f config-events.yaml
configmap/config-events configured

下記コマンドを実行して CloudEvents 設定が反映されていることを確認します。

$ oc describe configmaps config-events -n openshift-pipelines
Name: config-events
Namespace: openshift-pipelines
︙
formats:
----
tektonv1
sink:
----
http://el-failure-event-listener-ci-pipeline.apps.xxx

動作確認

最後に、実際にパイプラインを実行して、エラーが発生した場合に Slack に通知が届くことを確認します。

以前の記事のようにアプリケーションリポジトリに空のコミットを push します。

$ git commit -m "empty-commit" --allow-empty
$ git push

このままだと成功してしまうのでメッセージは送信されません。

パイプラインを少し編集してパイプラインが失敗するようにしてみます。

パイプラインの定義が記述されている pipeline.yaml 内の fetch-repository タスクの params を下記のように編集してこのタスクが失敗するようにします。
pipeline.yaml の詳細は以前の記事を参照してください。

tasks:
  - name: fetch-repository
    taskRef:
      name: git-clone
      kind: ClusterTask
    workspaces:
    - name: output
      workspace: shared-workspace
    - name: ssh-directory
      workspace: ssh-creds
    params:
    - name: url
      # URL を適当な文字列に変更
      value: "test"
    - name: subdirectory

下記コマンドを実行して編集を反映させます。

$ oc apply -f pipeline.yaml 

空のコミットをもう一度pushします。

$ git commit -m "empty-commit" --allow-empty
$ git push

少し経った後、OpenShift コンソール上でパイプラインが失敗したことを確認します。

webhookを許可したSlackのチャンネルを確認してメッセージが送信されていれば完了です。

 

まとめ

OpenShift Pipelines にエラー通知をSlackに送信する仕組みを Tekton Catalog を参考にして構築してみました。OpenShift Pipelines では OpenShift コンソール上の GUI で通知機能を設定するような実装はありませんが、CloudEvents と EventListener を組み合わせて実装することが出来ました。次回は CD 部分である OpenShift GitOps のエラー通知機能を実装してみたいと思います。

参考文献

https://github.com/tektoncd/catalog/blob/main/task/send-to-webhook-slack/0.1/README.md

https://tekton.dev/docs/pipelines/events/

https://tekton.dev/docs/pipelines/additional-configs/#configuring-cloudevents-notifications

https://tech-lab.sios.jp/archives/44104

ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

0人がこの投稿は役に立ったと言っています。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です