0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Google Cloudの予算アラートをSlackに通知する

Posted at

ノーコードだとメールしか通知設定ができない

Cloud Monitoringの通知にWebhookやSlackがあるので、Slackに通知するのはポチポチするだけでできるだろう…と思ったが、出来ない。
未来は出来るようになっているかもしれないが、今(2025/06/28)はまだできない。

仕方がないのでPub/SubからCloud Run FunctionsをコールしてもらってSlackに通知してもらうようにする。

アラートの設定にPub/Subトピックスを追加する

Google Cloudにログインをしてから

  1. 「課金」→「予算とアラート」を選択します。
  2. 「予算の作成」を開き、予算の名前、対象のプロジェクト、期間、金額などを設定します。
  3. 次に、アラートのしきい値を設定します。例えば、「実際の費用の50%」「90%」「100%」などに設定します。
  4. 「通知の管理」セクションをみるとわかるのですが、メールしか出来ないです。「Pub/Subトピックをこの予算に接続する」 にチェックを入れます。

image (2).png

そして、新しいPub/Subトピックを作成するか、既存のトピックを選択します。このトピックに予算アラートのメッセージが送信されます。

Slack Incoming Webhooksの設定をする

ここは割愛します。

Cloud Run Functionsの作成をする

  1. Google Cloud Consoleから、「pub/sub」を選択します。

  2. トリガータイプ: 「Cloud Pub/Sub」を選択します。

  3. Pub/Subトピック: ステップ1で作成した予算アラートのPub/Subトピックを選択します。(例えばbuget_alertという場合)
    image (3).png

  4. 上部にある「Cloud Run 関数トリガー」をクリックします。
    image (4).png

  5. ランタイム: 「Python 3.x」のいずれかを選択するのですが…3.1.13でやったら何故かうまくいかず。  3.1.12でやったらうまくいきました。

  6. エントリポイント: 関数名を入力します (例: process_budget_alert)。

  7. コード: 以下のサンプルコードを参考に、main.pyrequirements.txt を設定します。

main.py

import base64
import json
import os
import requests
# from datetime import datetime # 必要であればコメント解除

def process_budget_alert(event, context):
    """
    Pub/Sub からの予算アラートメッセージを処理し、Slack に通知します。

    Args:
         event (dict): The Pub/Sub message.
         context (google.cloud.functions.Context): The Cloud Functions event metadata.
    """
    try:
        if 'data' in event:
            message_data = base64.b64decode(event['data']).decode('utf-8')
            budget_alert = json.loads(message_data)
        else:
            raise ValueError("No data found in Pub/Sub event.")

        print(f"Received budget alert: {budget_alert}")

        slack_webhook_url = os.environ.get('SLACK_WEBHOOK_URL')
        if not slack_webhook_url:
            raise ValueError("SLACK_WEBHOOK_URL environment variable is not set.")

        budget_display_name = budget_alert.get('budgetDisplayName', '不明な予算')
        cost_amount = budget_alert.get('costAmount', 0.0)
        cost_interval_start = budget_alert.get('costIntervalStart', '')
        alert_threshold_exceeded = budget_alert.get('alertThresholdExceeded', 0.0)
        currency = budget_alert.get('currencyCode', 'JPY')

        formatted_date = ""
        if cost_interval_start:
            try:
                from datetime import datetime # ここでインポート
                dt_object = datetime.fromisoformat(cost_interval_start.replace('Z', '+00:00'))
                formatted_date = dt_object.strftime("%Y年%m月%d日")
            except ValueError:
                formatted_date = cost_interval_start

        slack_message_text = f"*Google Cloud 予算アラート: {budget_display_name}*"
        slack_message_text += f"\n- *使用金額*: {currency} {cost_amount:,.2f}"
        slack_message_text += f"\n- *期間開始日*: {formatted_date}"
        slack_message_text += f"\n- *アラートしきい値*: {alert_threshold_exceeded * 100:.0f}% を超過しました。"

        slack_payload = {
            "blocks": [
                {
                    "type": "section",
                    "text": {
                        "type": "mrkdwn",
                        "text": slack_message_text
                    }
                },
                {
                    "type": "context",
                    "elements": [
                        {
                            "type": "mrkdwn",
                            "text": "詳細はGoogle Cloud Consoleをご確認ください。"
                        }
                    ]
                }
            ]
        }

        response = requests.post(
            slack_webhook_url, data=json.dumps(slack_payload),
            headers={'Content-Type': 'application/json'}
        )
        response.raise_for_status()

        print("Slack notification sent successfully.")

    except Exception as e:
        print(f"Error processing budget alert: {e}")

requirement.txt

functions-framework==3.*
requests
  1. ランタイム環境変数

    SLACK_WEBHOOK_URLを忘れずに設定する。

テストしてみる

Cloud Run Functionsのテストからペイロードを送ることも出来るのですが、せっかくなので、Pub/Subからやってみます。

  1. Pub/Subのトピックから、「メッセージ」→「メッセージをパブリッシュする」を選択します。
    image (5).png

  2. 「メッセージの本文」を以下のようにしてメッセージを送ります。

    {
      "budgetDisplayName": "あなたのプロジェクトの予算名 (例: 月額10000円予算)",
      "costAmount": 5500.75,
      "costIntervalStart": "2025-06-01T00:00:00Z",
      "costIntervalEnd": "2025-07-01T00:00:00Z",
      "alertThresholdExceeded": 0.5,
      "forecastAmount": 9000.00,
      "forecastThresholdExceeded": 0.9,
      "currencyCode": "JPY",
      "gcpProjectId": "your-gcp-project-id"
    }
    

動かしてみて…

Pub/Subの通知はしきい値に達してから来ると思ってたのですが、そうではありませんでした。

一日に何度も通知がきます。
そして、それは仕様で、ちゃんと公式サイトに記載されています。

なので、しきい値を超えると何度もSlackに通知します。

そうなったらPub/Subへの通知を辞めるか、プログラムで一日1回だけSlackに送るように変更するしか無いですね。

問題点

問題点1

理由がまだよくわかっていないのですが、ここに書いてある手順のとおりにPub/Sub→トリガーの設定から作らないと出来ない。

Cloud Run Functionsから作ると、何故かトリガータイプがHTTP(S)になってしまう。

問題点2

Pub/Subの通知が一日に何度も来る。30分に一回の割合で来るので、個人で行っているGoogle Cloud だと、無料枠を超えてしまうかもしれない。

などなどと考えると、メール通知でいいのかなぁ…と思ってしまいますね。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?