はじめに

Google Cloud でCI/CDと言えばCloud Buildですが、ビルド結果がどうなったのか毎回コンソールの履歴画面に行って確認するのは手間です。
今回はCloud Buildの通知をslackに送信する方法を紹介します。
Cloud Buildの通知はMonitoringの様にネイティブで対応しておらず、Google公式の方法として「Cloud Build Notifier」と言うCloud Runの公式イメージを使って通知機能を実装します。
(いずれネイティブ対応されるかなと思ったのですが、5年ぐらいこのやり方が続いてます)

構成

用意するものとしては、以下の通り

 

(図は公式ガイドより抜粋)

  • 通知するpub/sub
  • 公式イメージをデプロイするCloud Run
  • 外付け設定のyamlとjsonを配置するCloud Storage
  • WebhookURLを保存するSecretManager
    • monitoringと違い、slackのbotトークンではない点に注意

公式ガイド

IAM

以下の二つのサービスアカウントを作成

  •  Pub/Subのサービスアカウント
    • サービストークン作成者
  •  Cloud Runのサービスアカウント
    • Secret Manager  シークレット アクセサー
    • ストレージ オブジェクト閲覧者

Secret Manager

シークレットを作成し、slackアプリのWebHookURLを値に設定。(slack周りの解説は省略)

Cloud Run

コンテナイメージに以下を指定

  • us-east1-docker.pkg.dev/gcb-release/cloud-build-notifiers/slack:latest
    • 中身はGoアプリで、処理としては以下の通り(実際のソースコードはこちら)
    1. Pub/Subのリクエストを受信
    2. 外付けのメッセージテンプレートファイルを読み込んでメッセージ生成
    3. SecretManagerのwebhookURLに送る(slackモードの場合)

環境変数に以下の2つを設定

  • CONFIG_PATH
    • CloudStorageのyamlファイルのパス
  • PROJECT_ID
    • プロジェクトID

Pub/Sub

トピック

Cloud Build通知はデフォルトで「"cloud-builds"」で送られるため、それ用のトピックを作成

公式ガイド

terraform


resource "google_pubsub_topic" "cloud_builds" {
  name    = "cloud-builds"
  project = var.project_id
}

サブスクリプション

上記トピックを受け取り、Cloud Runを起動するサブスクリプション

terraform


resource "google_pubsub_subscription" "cloud_builds_slack" {
  name    = "cb-slack-notifier"
  topic   = google_pubsub_topic.cloud_builds.name
  project = var.project_id

  push_config {
    push_endpoint = google_cloud_run_v2_service..uri

    oidc_token {
      service_account_email = 
    }
  }
  ack_deadline_seconds = 600
  message_retention_duration = "86400s"
  retry_policy {
    minimum_backoff = "10s"
    maximum_backoff = "600s"
  }
}

CloudStorage

以下の二つを作成し、CloudStorageにアップロードする

1. メッセージテンプレート(slack.json)

Slack Block Kit方式で書かれたメッセージテンプレート

公式サンプルファイル

サンプルだと情報が少な過ぎるのと、「{{.Params.buildStatus}}」がNoValueで読めなかったので以下の様に修正しました。
.Buildで使える変数はこちら、.Build.Substitutionsで使える変数はこちらを参考にしました。
(ビルド時間は無いので、StartTimeとFinishTimeで代用)


[
  {
    "type": "header",
    "text": {
      "type": "plain_text",
      "text": "{{.Build.Substitutions.TRIGGER_NAME}}: {{.Build.Status}}"
    }
  },
  {
    "type": "section",
    "fields": [
      {
        "type": "mrkdwn",
        "text": "*Project:*\n{{.Build.ProjectId}}"
      },
      {
        "type": "mrkdwn",
        "text": "*Branch:*\n{{.Build.Substitutions.BRANCH_NAME}}"
      },
      {
        "type": "mrkdwn",
        "text": "*Start Time (UTC):*\n{{.Build.StartTime.AsTime}}"
      },
      {
        "type": "mrkdwn",
        "text": "*Finish Time (UTC):*\n{{.Build.FinishTime.AsTime}}"
      }
    ]
  },
  {
    "type": "divider"
  },
  {
    "type": "section",
    "text": {
      "type": "mrkdwn",
      "text": "View Build Logs"
    },
    "accessory": {
      "type": "button",
      "text": {
        "type": "plain_text",
        "text": "Logs"
      },
      "value": "click_me_123",
      "url": "{{.Build.LogUrl}}",
      "action_id": "button-action"
    }
  }
]

2. 設定ファイル(slack.yaml)

公式サンプルファイル

失敗ステータスだけ欲しい場合など、このファイルでフィルター可能。
今回は全てのステータスで通知が飛ぶ様にします。また、読み込むシークレット名とテンプレートjsonのパスも設定します。

apiVersion: cloud-build-notifiers/v1
kind: SlackNotifier
metadata:
  name: slack-notifier
spec:
  notification:
    filter: build.status == Build.Status.SUCCESS || build.status == Build.Status.FAILURE || build.status == Build.Status.TIMEOUT || build.status == Build.Status.INTERNAL_ERROR || build.status == Build.Status.CANCELLED
    delivery:
      webhookUrl:
        secretRef: slack-webhook-url
    template:
      type: golang
      uri: gs://-cb-notifier/slack-message-template.json
  secrets:
  - name: slack-webhook-url
    value: projects//secrets/cb-slack-webhook/versions/latest

 

実際のslack通知

Cloud Buildがビルド完了するとこの様にメッセージテンプレートに基づいた通知が届きます。

さいごに

Cloud Build Notifierの良さは、自分の好きなチャネルに、必要な情報だけを届けられる柔軟性にあります。
今回紹介した設定をベースに、さらにフィルタリングや通知文面を自分好みにカスタマイズしてみてください。
最後までお読みいただき、ありがとうございました、