Google Cloud 予算アラートを Slack に連携する

Google Cloud 予算アラートを Slack に連携する

Google Cloud の予算アラートを Slack チャンネルに通知する方法を解説します。Cloud Run functions 第二世代を利用した構築方法で試します。
Clock Icon2025.05.09

はじめに

Google Cloud プロジェクトの利用金額は Cloud Billing の 請求先アカウント によって管理されます。Google Cloud からの請求は請求先アカウントに対して行われ、設定した支払い方法によってプロジェクト利用費を支払います。

請求先アカウントでは 予算アラート により、利用金額が設定したしきい値を超えるとメールなどに通知させることができます。本ブログでは予算アラートを Slack に通知する方法についてまとめました。

概要

予算アラートの Slack 通知は以下のように構成します。

slack-notification-overview
予算アラートの Slack 通知 アーキテクチャ

ポイントは以下です。

  • Cloud Billing 請求先アカウントの予算アラートを Cloud Pub/Sub にパブリッシュします。
  • Cloud Pub/Sub にパブリッシュされたアラートメッセージは Eventarc に転送され、Eventarc は CloudEvents形式で Cloud Run functions に配信されます。
  • Cloud Run functions では HTTP による CloudEvents の受信をトリガーとしてメッセージを処理し、Slack API を介して Slack アプリにメッセージを POST します。
  • Slack アプリにアクセスするための OAuth トークンは Secret Manager に格納し、Cloud Run functions から参照します。
  • Slack アプリは受信したメッセージを元に Slack チャンネルにメッセージを書き込みします。

構築手順の詳細は以下ドキュメントを参考にしています。

https://cloud.google.com/billing/docs/how-to/notify?hl=ja

なお、本検証では Cloud Run funcitons の 第二世代を利用した構成 としています。第二世代は Cloud Run に統合され、関数を Cloud Run サービスとしてデプロイできます。Cloud Run サービスの仕様を踏襲しており、Cloud Run サービスで利用可能な機能をサポートしています。一方、第一世代は Cloud Run に統合されていない関数実行サービスです。

第一世代は Cloud Pub/Sub などのトリガー機能を直接サポートするため Eventarc の構成が不要で設定はしやすいのですが、現在 Google Cloud では Cloud Run の豊富な機能が享受できる 第二世代を推奨 しています。今後も第一世代はサポート継続されるとドキュメントには明記されていますが、将来的な第二世代への移行の可能性なども考慮し本記事では第二世代の構成方法で試していきます。

Cloud Run functions の世代についての詳細は以下ドキュメントをご参照ください。

https://cloud.google.com/functions/docs/concepts/version-comparison?hl=ja

やってみた

本検証では、Cloud Billing は Cloud Console から、Google Cloud プロジェクトの構成は Cloud Shell から gcloud コマンドを利用して構築していきます。

Cloud Billing にアクセスするには Google Cloud プロジェクトへの権限付与とは別に、Cloud Billing 請求先アカウントへの権限付与が必要となります。リセラーパートナー経由で Google Cloud の支払いをしている場合、請求先アカウントはリセラーパートナーが管理しています。必要な権限付与をリセラーパートナーに依頼してください。

予算アラートを作成するユーザは、Cloud Billing 請求先アカウントに対し以下権限が付与されている必要があります。

  • billing.budgets.create
  • billing.budgets.get
  • billing.budgets.list

IAM ロールですと以下いずれかの事前定義ロールを保持していればよいです。

  • 請求先アカウント管理者(roles/billing.admin)
  • 請求先アカウントの費用管理者(roles/billing.costsManager)

今回は 請求先アカウント管理者 の権限を持つユーザで予算アラートを作成していきます。

また、Google Cloud プロジェクト上で Slack 通知のパイプラインを構成するため、Google Cloud プロジェクト上でも必要な権限を付与する必要があります。今回はオーナー権限で検証していきますが、事前定義ロールを付与する場合は以下をご参照ください。

本検証で必要な事前定義ロール
  • Service Usage 管理者(roles/serviceusage.serviceUsageAdmin)
  • Pub/Sub 管理者(roles/pubsub.admin)
  • Secret Manager 管理者(roles/secretmanager.admin)
  • サービス アカウント管理者(roles/iam.serviceAccountAdmin)
  • Project IAM 管理者(roles/resourcemanager.projectIamAdmin)
  • Cloud Run 管理者(roles/run.admin)
  • Artifact Registry 管理者(roles/artifactregistry.admin)
  • ストレージ管理者(roles/storage.admin)
  • サービス アカウント ユーザー(roles/iam.serviceAccountUser)
  • Cloud Build 編集者(roles/cloudbuild.builds.editor)
  • Eventarc 管理者(roles/eventarc.admin)
  • ログ表示アクセス者(roles/logging.viewAccessor)
  • Project IAM 管理者(roles/resourcemanager.projectIamAdmin)

Google Cloud プロジェクト API 有効化

Google Cloud で対象のプロジェクトを選択し、Cloud Shell から実行していきます。以下コマンドを実行し、Google Cloud 上でリソースを構成するうえで必要な API を有効化します。

gcloud services enable artifactregistry.googleapis.com \
cloudbuild.googleapis.com \
eventarc.googleapis.com \
run.googleapis.com \
pubsub.googleapis.com \
secretmanager.googleapis.com

Pub/Sub トピックとサブスクリプションの作成

slack-notification-pubsub
Pub/Sub トピックとサブスクリプションの作成

予算アラートをパブリッシュする宛先となる Cloud Pub/Sub トピックと、Eventarc にメッセージを連携するための Cloud Pub/Sub サブスクリプションを作成していきます。

まずは環境変数 PROJECT_ID にプロジェクト ID を指定します。

export PROJECT_ID=$(gcloud config get-value project)

Pub/Sub トピックを作成します。

gcloud pubsub topics create budget-alerts-topic

予算アラートが Pub/Sub トピックにパブリッシュされたことをトリガーとし、Eventarc が Cloud Run functions にイベント配信を行います。Eventarc にメッセージを連携するための Pub/Sub サブスクリプションを作成していきます。

gcloud pubsub subscriptions create budget-alerts-sub \
--topic budget-alerts-topic

予算アラートを作成

slack-notification-budget-alerts
予算アラートを作成

Cloud Billing 請求先アカウントから Pub/Sub トピックに通知をパブリッシュするよう請求先アカウントの予算アラートを作成します。

Cloud Console 上部の検索窓に「請求先アカウント」と入力し、[請求先アカウント] をクリックします。

sba-1

以下の画面が表示されたら [請求先アカウントを管理] をクリックします。

sba-2

[組織を選択] から予算アラートの対象とするプロジェクトが所属する組織を選択します。[マイプロジェクト] タブを選択し、予算アラートの対象とするプロジェクトにリンクされた請求先アカウント名をクリックします。

sba-3

メニューから [予算とアラート] を選択し、[+予算を作成] を選択します。

budget-alerts-1

予算アラートの名前、期間、スコープ(予算対象とするリソースの選択)などを設定し、[次へ] をクリックします。

budget-alerts-2

budget-alerts-3

予算タイプ、目標金額を設定し、[次へ] をクリックします。

budget-alerts-4

予算アラートを発生させるしきい値を設定します。

budget-alerts-5

[Pub/Sub トピックをこの予算に接続する] にチェックをいれ、さきほど作成した Cloud Pub/Sub トピックを選択します(Cloud Pub/Sub トピックを作成したプロジェクトを選択し、Pub/Sub トピックを選択)。[終了] をクリックします。

budget-alerts-6

Slack アプリの作成とワークスペースへのインストール

slack-notification-slackapi
Slack アプリの作成とインストール

Google Cloud から通知されたメッセージを Slack チャンネルに投稿するための Slack アプリを作成していきます。 https://api.slack.com/apps にアクセスします。

[Create New App] をクリックします。

slack-api1

[From a manifest] をクリックします。

slack-api

[select a workspace] から Slack アプリをインストールするワークスペースを選択し [Next] をクリックします。

slack-api3

必要に応じて、作成するアプリのマニフェストを修正し、[Next] をクリックします。今回は display_informationname のみ更新しています。

slack-api4

[Create] をクリックします。

slack-api5

Slack アプリが作成されたら、左側の項目から [OAuth & Permissions] を選択し、[Scopes] -> [Bot Token Scopes] の [Add an OAuth Scope] をクリックします。

slack-scopes-1

[chat:write] と [chat:write.public] を選択します。

slack-scopes-2

左側の項目から [Install App] を選択し、[Install to xxxx] (xxxx はワークスペース名) をクリックします。その後、確認のダイアログで [許可] をクリックします。

install-app

Slack API の [Install App] -> [OAuth Tokens] の [Bot User OAuth Token] をコピーします。これは Cloud Run functions から Slack アプリにアクセスするために利用する OAuthトークンとなります。

bot-user-oauth-token

ワークスペースにインストールした Slack アプリはワークスペース上の [App] -> [+アプリを追加する] から追加できます。

slack

Secret Manager に OAuth トークンを保存

slack-notification-secret-manager
Secret Manager に OAuth トークンを保存

前述の手順でコピーした OAuth トークンは Cloud Run functions から Slack アプリにアクセスするために利用します。トークンを Cloud Run functions にハードコードすることは漏洩のリスクが大きいため推奨しません。そのため、シークレット管理のためのマネージドサービスである Secret Manager にトークンを保存します。Secret Manager に保存したシークレットは Cloud Run functions でマウントして参照することが可能です。

以下コマンドを実行し、Secret Manager にシークレットを作成します。

gcloud secrets create slack_bot_user_oauth_token \
--replication-policy="automatic"

先ほどコピーした Slack 用の OAuth トークンを以下コマンドに入力して実行します。

echo -n "<Slack Bot User OAuth Token>" | \
gcloud secrets versions add slack_bot_user_oauth_token --data-file=-

Cloud Run functions 用サービスアカウントの作成

Cloud Run functions が Secret Manager にアクセスするために、Secret Manager へのアクセス権限を付与したサービスアカウントを作成し Cloud Run functions をデプロイする際にアタッチする必要があります。
ここでは事前にサービスアカウントの作成と権限付与を行います。以下コマンドを実行します。

gcloud iam service-accounts create slack-notification
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member=serviceAccount:slack-notification@$PROJECT_ID.iam.gserviceaccount.com \
--role=roles/secretmanager.secretAccessor \
--condition=None

Cloud Run functions のデプロイ

slack-notification-cloud-run-functions
Cloud Run functions のデプロイ

予算アラートが Cloud Pub/Sub にパブリッシュされたことをきっかけとし、Slack アプリに通知を行う Cloud Run functions をデプロイしていきます。

まずは Cloud Shell のユーザ環境に、Cloud Run functions にデプロイする Python のソースコードを準備していきます。最初にディレクトリを作成します。

mkdir slack-notification
cd slack-notification

slack-notification ディレクトリ配下に requirements.txt を作成します。

requirements.txt
requests
functions_framework
google-cloud-secret-manager

slack-notification ディレクトリ配下に main.py を作成します。

main.py
import base64
import json
import requests
import functions_framework
from google.cloud import secretmanager
import os

@functions_framework.cloud_event
def send_slack_chat_notification(cloud_event):

    project_id = os.environ.get("PROJECT_ID") # Cloud Run functions デプロイ時に環境変数を定義
    secret_id = "slack_bot_user_oauth_token"  # シークレット ID
    version_id = "latest"      # 最新バージョンを使用

    # Secret Managerからトークンを取得
    client = secretmanager.SecretManagerServiceClient()
    name = f"projects/{project_id}/secrets/{secret_id}/versions/{version_id}"
    response = client.access_secret_version(request={"name": name})
    token = response.payload.data.decode("UTF-8")

    # CloudEvent 形式のデータから必要な情報を取得
    event_data = cloud_event.data

    billing_account_id = event_data['message']['attributes']['billingAccountId']

    pubsub_message = base64.b64decode(event_data['message']['data']).decode('utf-8')
    message_json = json.loads(pubsub_message)

    text = (
        f"請求先アカウントID: {billing_account_id}\n"
        f"予算アラート名: {message_json.get('budgetDisplayName', '')}\n"
        f"費用: {message_json.get('costAmount', '')}\n"
        f"予算: {message_json.get('budgetAmount', '')}\n"
        f"アラート閾値: {message_json.get('alertThresholdExceeded', '') * 100}%\n"
        f"通貨: {message_json.get('currencyCode', '')}"
    )

    # Slack API への POST
    requests.post("https://slack.com/api/chat.postMessage", data={
        "token": token,
        "channel": "#google-cloud-budget-alerts", # Slack チャンネル名
        "text": text
    })
main.py のポイント
  • このあと Cloud Run functions デプロイ時に環境変数 PROJECT_ID を設定します。プロジェクト ID は Cloud Run の環境変数から読み込みます。
  • このあと Cloud Run functions デプロイ時に Secret Manager のシークレットをマウントします。マウントしたシークレットをクライアントライブラリで呼び出します。
  • Eventarc から CloudEvent 形式で渡されたデータから必要な情報を抽出してテキスト化しています。請求先アカウント ID は CloudEvent データの data.message.attributes 内に Key-Value ペアで記述されています。予算アラートの詳細は CloudEvent データの data.message.data に base64 でエンコードされた UTF-8 文字列で記述されているため、base64 でデコードして JSON オブジェクトを取得します。フォーマットの詳細はこちらのドキュメントをご参照ください。
  • Slack API に渡す data"channel": は Slack チャンネル名を指定します。ここでは "#google-cloud-budget-alerts" としました。

gcloud run deploy コマンドで Cloud Run functions に関数をデプロイします。gcloud functions deploy --gen2 でのデプロイも可能ですが、Cloud Run に統合された Cloud Console での管理において不整合が発生する部分がありましたので gcloud run deploy を利用しています。

先ほどの説明でも記載しましたが、環境変数として PROJECT_IDとシークレットのマウント設定をしています。また、シークレットにアクセスするための権限を付与したサービスアカウントを指定しています。

gcloud run deploy slack-notification \
--source . \
--function send_slack_chat_notification \
--base-image python312 \
--region asia-northeast1 \
--no-allow-unauthenticated \
--set-env-vars PROJECT_ID=$PROJECT_ID \
--set-secrets 'mnt/secrets=slack_bot_user_oauth_token:latest' \
--service-account=slack-notification@$PROJECT_ID.iam.gserviceaccount.com

Eventarc 用サービスアカウントの作成

Eventarc を設定する前に、まずは Eventarc に紐づけるサービスアカウントの作成します。

gcloud iam service-accounts create budget-alerts-pubsub-trigger

イベントプロバイダ(この場合 Cloud Pub/Sub)からイベントを受信できるよう Eventarc イベント レシーバー(roles/eventarc.eventReceiver) の IAM ロールを付与します。

gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:budget-alerts-pubsub-trigger@$PROJECT_ID.iam.gserviceaccount.com" \
--role=roles/eventarc.eventReceiver \
--condition=None

また、Cloud Run functions を起動できるよう Cloud Run 起動元(roles/run.invoker) の IAM ロールを付与します。

gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:budget-alerts-pubsub-trigger@$PROJECT_ID.iam.gserviceaccount.com" \
--role=roles/run.invoker \
--condition=None

Eventarc トリガーの作成

slack-notification-eventarc
Eventarc トリガーの作成

SCC からのメッセージが Cloud Pub/Sub にパブリッシュされたことをトリガーとし、Cloud Run functions にメッセージを配信する Eventarc トリガーを作成します。--destination-run-service で作成した Cloud Run functions を選択、--event-filters は Cloud Pub/Sub のイベントを指定、--transport-topic で Pub/Sub トピックを指定します。先ほど作成したサービスアカウントも紐づけます。

gcloud eventarc triggers create budget-alerts-pubsub-trigger \
--location=asia-northeast1 \
--destination-run-service=slack-notification \
--destination-run-region=asia-northeast1 \
--event-filters="type=google.cloud.pubsub.topic.v1.messagePublished" \
--service-account="budget-alerts-pubsub-trigger@$PROJECT_ID.iam.gserviceaccount.com" \
--transport-topic=projects/$PROJECT_ID/topics/budget-alerts-topic

テスト

予算アラートが Slack チャンネルに連携されるか試してみます。
予算を超えた予算アラートを再作成することで数分後に通知されます。以下の通知が Slack チャンネルに連携されました。

slack-notification.png (579×241)

おわりに

本記事では、Google Cloud の予算アラートを Slack に通知する仕組みを構築しました。Cloud Pub/Sub、Eventarc、Cloud Run functions、Secret Manager といったマネージドサービスを組み合わせることで通知パイプラインを実現できます。

予算超過のリアルタイム検知による厳格なコスト管理をされるケースにおいて、導入の参考にしていただければと思います。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.