Google BigQuery

GCP: 今月のGCP課金額をslackに自動的に書き込む

こんにちは、エクスチュアの權泳東(権泳東/コン・ヨンドン)です。

今回はGCPの課金額をslackに自動的に書き込むプログラムを作って見ます。

GCPは低コストでクラウド環境を構築できるのでとても良いのですが、うっかりテスト用の設定を作ったまま放置してしまって課金されてたり、なんてことありませんか?
毎日slackに今月の課金額をポストしてくれると、少しはダメージが少なくなるかも知れません。

やることは以下のとおりです。
1. GCPのBillingレポートをBigQueryにエクスポートする
2. BigQueryのBilligテーブルにクエリを投げて、レポートを抽出する
3. slackのwebhookを使って、レポートをslackのチャネルにポストする

1. GCPのBillingレポートをBigQueryにエクスポートする

まずはBigQueryにBillingエクスポート用のデータセットを作ります。
わかりやすく billing という名前にでもしておきます。

次にGCPのBillingメニューの中の、Billing Exportを開きます。
そして、先ほど作ったbillingデータセットを指定して、Enable Billing Exportボタンをクリックします。

BigQuery billing export has been successfully enabled というメッセージが表示されればOKです。
次は、このテーブルに対してクエリを投げて見ます。

2. BigQueryのBilligテーブルにクエリを投げて、レポートを抽出する

Billing Export設定をしてから小一時間ほどすると、billingデータセットの中に2つのテーブルが出て来ます。

gcp_billing_export_* というテーブルと、 gcp_billing_export_v1_* というテーブルです。

v1の方が今月発表された新しいスキーマのテーブルなので、新しいv1テーブルでクエリを書いて見ます。
今月の現時点の、プロジェクト名と製品名ごとに0.01ドル以上の課金額を表示します。

#standardSQL
select format_timestamp('%Y/%m', usage_start_time, 'Asia/Tokyo') as mon,
project.name as project,
service.description as service, 
round(sum(cost * 100)) / 100 as cost
from billing.gcp_billing_export_v1_008EC3_C14E1F_66E889
where format_timestamp('%Y/%m', usage_start_time, 'Asia/Tokyo') = format_timestamp('%Y/%m', current_timestamp, 'Asia/Tokyo')
and cost >= 0.01
group by mon, project, service
order by project

結果はこうなります。

次は、このデータをSlackのチャネルに定期的に投稿するプログラムを書いて見ます。

3. slackのwebhookを使って、レポートをslackのチャネルにポストする

SlackのIncoming Webhookという機能を使って、メッセージをSlackの特定のチャネルにポストします。

Slackのアカウントを持っている前提で、以下のURLからWebhook URLを取得します。

Incoming Webhook Integration

メッセージを書き込みたいチャネルを選んで、緑色のAdd Incoming Webhook integrationボタンをクリックすればOKです。

Webhook URLが表示されるので、これをメモしておきます。

そして、Webhook URLにBillingテーブルに対するクエリ結果をポストすればSlackにメッセージが表示されます。
Node.jsで書いて見ます。

var Slack = require('slack-node');
require('date-utils');

//webhook url
webhookUri = "https://hooks.slack.com/services/foo/bar/baz"; 
slack = new Slack();
slack.setWebhook(webhookUri);

var BigQuery = require('@google-cloud/bigquery');
var projectId = 'myprojectid'; // gcp project id
var bigquery = BigQuery({
    projectId: projectId
});
var datasetName = 'billing';
var dataset = bigquery.dataset(datasetName);
var table = dataset.table('gcp_billing_export_v1_hoge'); //billing table

//query
var query = 'SELECT format_timestamp(\'%Y/%m\', usage_start_time, \'Asia/Tokyo\') as mon, ' +
'project.name as project, service.description as service, round(sum(cost * 100)) / 100 as cost ' +
'FROM billing.gcp_billing_export_v1_hoge ' +
'where format_timestamp(\'%Y/%m\', usage_start_time, \'Asia/Tokyo\') = format_timestamp(\'%Y/%m\', current_timestamp, \'Asia/Tokyo\') ' +
'AND cost >= 0.01 ' +
'GROUP BY mon, project, service order by project';

var options = {
    query: query,
    useLegacySql: false
};

var bqresult = "";
var project_name = "";
var cnt = 0;
var subtotal = 0;
var total = 0;

//query結果をproject毎にまとめる
bigquery.query(options, function(err, rows) {
    if (!err) { 
        for (var i=0; i<rows.length; i++) {
            var row = rows[i];
            if (cnt == 0) {
                project_name = row["project"];
                cnt = 1;
            }
            //projectとprojectの間には罫線を入れる
            if (project_name != row["project"]) {
                bqresult += "----------\n";
                bqresult += "小計 $" + (Math.floor(subtotal * Math.pow(10, 2)) / Math.pow(10, 2)) + "\n\n";
                subtotal = 0;
            }
            bqresult += row["project"] + "\t" + row["service"] + "\t$" + row["cost"] + "\n";
            subtotal += row["cost"];
            total += row["cost"];
            project_name = row["project"];
        }
        bqresult += "----------\n";
        bqresult += "小計 $" + (Math.floor(subtotal * Math.pow(10, 2)) / Math.pow(10, 2)) + "\n\n";
        bqresult += "合計 $" + (Math.floor(total * Math.pow(10, 2)) / Math.pow(10, 2)) + "\n"; 
        
        var dt = new Date();
        var fd = dt.toFormat("YYYY-MM-DD");
        //slackに投稿        
        slack.webhook({
          channel: "#alert",
          username: "ctobot",
          icon_emoji: ":dollar:",
          text: "今月のGCPコスト (as of " + fd + ")\n" + bqresult
        }, function(err, response) {
          console.log(response);
        });
    }
});

実行すると、Slackのチャネルにメッセージが表示されます。

モザイクだらけでアレなのと、通貨設定がドルなのですが、そこはスルーしてください。

注意点として、GCPの課金エクスポートは、エクスポート設定をしたタイミングからの課金額しか反映されません。
月の途中でエクスポート設定をしても月初の金額は分からないので気をつけてください。

あとは、このプログラムをGCE上のLinuxインスタンスで毎日cronすれば良いですね。

以上、今回はGCP課金額をslackに自動投稿する方法についてでした。
弊社ではGCPを利用した分析基盤の構築支援などを行なっております。
お問い合わせはこちらからどうぞ。

ブログへの記事リクエストはこちらまで

ピックアップ記事

  1. 最速で理解したい人のためのIT用語集

関連記事

  1. Google BigQuery

    Big QueryでWindow関数を用いて、累積和を計算する

    こんにちは。エクスチュアでインターンをさせて頂いている中野です。…

  2. Google Analytics

    Google Analytics4 を知る。

    Google Analyticsを知る。 -エクスチュアのツール紹介-…

  3. Adobe Analytics

    Adobe AEP SDKでReactNativeアプリを計測する (iOS編)

    こんにちは、エクスチュアの權泳東(コン・ヨンドン)です。今回は…

  4. Google BigQuery

    Treasure DataからBigQueryにデータを移動させる方法

    こんにちは、エクスチュアの酒井です。今回は、TreasureDat…

  5. Adobe Analytics

    Adobe Analytics: BigQueryでパーティシペーション指標を集計するSQL

    こんにちは、エクスチュアの權泳東(権泳東/コン・ヨンドン)です。…

  6. GA 360 Suite

    GoogleDataStudio:複数のデータソースにフィルターを適用する

    こんにちは。エクスチュアの渡部です。今回はDataStudio(デ…

最近の記事

  1. Mouseflowのセッションリプレイのしくみ
  2. LangChainって何?: 次世代AIアプリケーション構築…
  3. 1st Party Cookieと3rd Party Coo…
  4. Amplitudeで何が分かる?
  5. SnowflakeのHybrid Tableのマニュアルを読…
  1. Mouseflow

    Mouseflowのフィルター機能が変わりました!
  2. Mouseflow

    Mouseflowで問題解決① -事例ベースで機能紹介-
  3. Adobe Analytics

    AdobeAnalytics: GTMのdataLayerをAdobeAnaly…
  4. IT用語集

    サマリー(Summary)って何?
  5. ブログ

    ベイズとR
PAGE TOP