目的
GCPで予算を使いすぎた(使いすぎそうになった)ときにアラートをSlackに飛ばしたい。
そのための管理コストも最小限にしたい。
通知の流れ
- 予算アラート ->
- GCP pub/sub ->
- GCP functions ->
- Slack Incoming webhook ->
- 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"を指定してください。
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';
};
{
"name": "alert-budget-http",
"version": "0.0.1",
"dependencies": {
"@slack/client": "^3.15.0"
}
}
5. 通知のテスト
このようなJsonがPub/Subからは送られてきます。
dataに指定されている文字列は、下記のような内容がBase64でエンコードされた文字列が入っています。
{
"data": "ew0KICAgICJidWRnZXREaXNwbGF5TmFtZSI6ICJuYW1lLW9mLWJ1ZGdldCIsDQogICAgImFsZXJ0VGhyZXNob2xkRXhjZWVkZWQiOiAxLjAsDQogICAgImZvcmVjYXN0VGhyZXNob2xkRXhjZWVkZWQiOiAxLjAsDQogICAgImNvc3RBbW91bnQiOiAxMDAuMDEsDQogICAgImNvc3RJbnRlcnZhbFN0YXJ0IjogIjIwMTktMDEtMDFUMDA6MDA6MDBaIiwNCiAgICAiYnVkZ2V0QW1vdW50IjogMTAwLjAwLA0KICAgICJidWRnZXRBbW91bnRUeXBlIjogIlNQRUNJRklFRF9BTU9VTlQiLA0KICAgICJjdXJyZW5jeUNvZGUiOiAiVVNEIg0KfQ=="
}
{
"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ボットユーザーを利用しています。
参考
- 中野智文のブログ
- 見た目をいじりたい場合 -> Payloadドキュメント