GCP予算アラートをSlackに通知する


目的

GCPで予算を使いすぎた(使いすぎそうになった)ときにアラートをSlackに飛ばしたい。
そのための管理コストも最小限にしたい。

通知の流れ

  1. 予算アラート ->
  2. GCP pub/sub ->
  3. GCP functions ->
  4. Slack Incoming webhook ->
  5. Slack channel

になります。こんな設定、エンジニア以外出来ないだろと行った感じですね。エンジニアがやっても面倒でした。Googleどうにかしろ。

それぞれの設定

1. 予算アラートの作成

予算が超過した場合、または、超過しそうな場合にアラートを発生させ、アカウントユーザーに対してメールを送信できます。
しかし、予算通知の場合は、トリガーの設定は反映されません。
常に1日のうちに何度も通知が実行されます。
そのため、トリガー相当の機能は通知プログラム内で実装する必要があります。
(ただし、トリガーを設定しておくと条件を満たした場合のみ後述の特定のフィールドが含まれるようになります。)

予算アラートの作成は、お支払い > 予算とアラート から行います。

予算の額だけ設定しておいてください。「指定額」と「先月の使用額」を選べます。

2. GCP pub/subのトピック作成

Pub/sub consoleから、予算アラートを受け取るTopicを作成してください。
作成はトピックIDだけを入力して後はデフォルトで大丈夫です。

作成が完了したら、先程の予算アラート設定の最下部で、先ほど作成したトピックを登録します。

3. Slack incoming webhookの作成

Slackの説明に従って、通知を出したいチャンネルにIncoming webhookを設定してWebhook URLを生成してください。こちらのURLをこの後に使います。

また、Webhookには新旧のWebhookが存在します。Payloadの仕様が違います。本投稿は新しいWebhookで作成しています。

4. GCP functionsの関数作成

Pub/subからイベントを受け取り、Slackに通知を行う処理をFunctionsに作成します。

名前は任意、トリガーをCloud Pub/Subに変更し、先程作成したトピックを指定してください。ソースコードは、下載のコードで、Incoming webhookのURL部分だけを置換して、設定してください。実行する関数は”alertBudget"を指定してください。

index.js
const { IncomingWebhook } = require("@slack/client");

/**
 * Background Cloud Function to be triggered by Pub/Sub.
 *
 * @param {object} event The Cloud Functions event.
 * @param {object} context
 */
exports.alertBudget = async (event, context) => {
  const pubsubMessage = event.data;
  const decoded = pubsubMessage.data ? Buffer.from(pubsubMessage.data, 'base64').toString() : '{}';
  const data = JSON.parse( decoded );

  // 予算をオーバーした場合はSlackに通知を投げる
  if (data.costAmount > data.budgetAmount) {
    const body = {
      "text": "GCP予算超過があります\n" + 
      `*現在の消化予算*:${data.costAmount} ${data.currencyCode}`
    };
    // こちらのURLを、各自のIncoming webhookのURLに置き換えてください。
    const webhook = new IncomingWebhook('https://hooks.slack.com/services/XXXXX');
    await webhook.send(body);
  } else {
    console.log(`${data.costAmount}/${data.budgetAmount}`);
  }

  return 'Slack notification sent successfully';

};
package.json
{
    "name": "alert-budget-http",
    "version": "0.0.1",
    "dependencies": {
        "@slack/client": "^3.15.0" 
    }
}

5. 通知のテスト

このようなJsonがPub/Subからは送られてきます。
dataに指定されている文字列は、下記のような内容がBase64でエンコードされた文字列が入っています。

RequestBodyFromPub/Sub
{
  "data": "ew0KICAgICJidWRnZXREaXNwbGF5TmFtZSI6ICJuYW1lLW9mLWJ1ZGdldCIsDQogICAgImFsZXJ0VGhyZXNob2xkRXhjZWVkZWQiOiAxLjAsDQogICAgImZvcmVjYXN0VGhyZXNob2xkRXhjZWVkZWQiOiAxLjAsDQogICAgImNvc3RBbW91bnQiOiAxMDAuMDEsDQogICAgImNvc3RJbnRlcnZhbFN0YXJ0IjogIjIwMTktMDEtMDFUMDA6MDA6MDBaIiwNCiAgICAiYnVkZ2V0QW1vdW50IjogMTAwLjAwLA0KICAgICJidWRnZXRBbW91bnRUeXBlIjogIlNQRUNJRklFRF9BTU9VTlQiLA0KICAgICJjdXJyZW5jeUNvZGUiOiAiVVNEIg0KfQ=="
}
RawDataBody
{
    "budgetDisplayName": "name-of-budget",
    "alertThresholdExceeded": 1.0,
    "forecastThresholdExceeded": 1.0,
    "costAmount": 100.01,
    "costIntervalStart": "2019-01-01T00:00:00Z",
    "budgetAmount": 100.00,
    "budgetAmountType": "SPECIFIED_AMOUNT",
    "currencyCode": "USD"
}

alertThresholdExceededはトリガーに設定している実値の閾値を超えている場合のみ含まれます。(閾値を超えていない場合は、このフィールド含まれません。)
forecastThresholdExceededはトリガーに設定している推定値を超えている場合のみに含まれます。

通知のテストを行いたい場合は、ファンクションのテストで、RequestBodyFromPub/Subの内容をコピペして「関数をテストする」を実行してください。

正しく動いていれば、Slackに次のような通知が飛びます。(文面は自分用に多少いじっています。)

以上になりますが、長かった。。。
しかし、これ一度予算上限を超えてしまうと何度も通知されちゃうな。トリガー相当の実装は骨が折れすぎる。。。

その他みつけた実現手段

  • 旧Webhookを使ったものが比較的多くGoogle検索で引っかかります。基本的には同じなので、今回のFunctionsの実装は、旧Webhookでも動作するものになっています。
  • Googleの公式では、Slackへの通知に、Slackボットユーザーを利用しています。

参考