明日から本気だす

データベース好きなサポートエンジニアのメモです.

OCI の課金情報を Grafana で可視化してみた

この記事は、JPOUG Advent Calendar 2021 12日目の記事です。

11日目の記事は macchi_ora さんの「Oracle Clusterware用のREST APIを使ってみる」でした。

はじめに

JPOUG Advent Calendar 初参加 です。2020 年に Oracle 製品に関係する仕事から離れていたのですが、今年の夏より、某 SIerOracle 製品のサポート・エンジニアをしています。どうぞ宜しくお願いします。

私自身、8月頃から仕事とプライベートの両方で、本格的に Oracle Cloud Infrastructure をさわり始めているのですが、OCI にかぎらず、クラウド・サービスを利用する際は、課金状況が気になるものです。特に個人で(自腹で)何かしらのクラウド・サービスを利用している場合はなおさらです…。OCI では Web コンソールの [ガバナンスと管理] - [コスト管理] より、利用中の OCI サービスについての詳細なコスト分析が可能です。この標準のコスト分析の機能がとても使いやすく、特に不満があるわけではないのですが、せっかくなら OCI の勉強もかねて、独自の課金ダッシュボードみたいなものを作ってみたいと思いたち、REST API と Grafana を利用した OCI コスト情報の可視化にチャレンジしてみました。

環境

可視化環境については OCI 上の Compute インスタンスを利用しました。下図のように、パブリック・サブネット上の VM に Grafana 7.5 をインストールし、また、プライベート・サブネットの VM には InfluxDB 1.8.10 をインストールしています。

プライベート・サブネットの VM から REST API を定期的に叩き、取得した OCI のコスト情報を一度 InfluxDB に格納し、それを Grafana から参照するような仕組みにしました。

1-13

Grafana を採用した理由としては、同環境に OCI Grafana Metrics プラグインを導入し OCI Monitoring のリソースデータをあわせて取得すれば、コスト情報と OCI のリソース情報を Grafana 上の同一ダッシュボードで閲覧できてカッコいいのでは 便利なのでは…と安易に考えたからです。

github.com

なお、OCI Grafana Metrics プラグインについては、昨年の JPOUG Advent Calendar の ryota_hnk さんの記事 Oracle Cloud監視入門 の中でも紹介されており、私も参考にさせていただきました。

Account Metering REST API

OCI で利用しているサービスの課金状況取得については、以下の Account Metering REST API の Resource Usage Cost を利用しました。

docs.oracle.com

API 実行時の認証

ドキュメントにも記載がありますが Account Metering REST API を実行するには、以下の何れかのロールが必要となります。

また、REST API 実行時に IDCS-GUID(idcs-XXXXXXXX)と Accout ID(cacct-XXXXXXXX)が必要となります。私は、OCI Web コンソールから、以下の方法で確認しました。

IDCS-GUID と Account ID の確認方法

OCI Web コンソールにログイン後、画面右上の [プロファイル]‐[サービス・ユーザー・コンソール] をクリックします。

1-4

表示された MY Oracle サービスの URL より cacct-XXXXXXXX からはじまる Accout ID を確認します。

1-5

同ページのサービス一覧より「Oracle Identity Cloud Service」 の [管理コンソール] をクリックします。

1-6

表示された Identity Cloud Service の URL より idcs‐XXXXXXXX からはじまる IDCS-GUID を確認します。

1-7

API を試してみる

上記の認証情報がそろったら、試しに curl コマンドで OCI のコスト情報が取得できるか確認します。

$ curl -X GET -u hogehoge@sample.com:YourPassword -H "X-ID-TENANT-NAME:<idcs-XXXXXXXX" "https://itra.oraclecloud.com/metering/api/v1/usagecost/<cacct-XXXXXXXX>?startTime=2021-11-30T00:00:00.000&endTime=2021-11-30T23:59:59.999000.000&usageType=DAILY"

なお、上記コマンド例では usageType=DAILY で日毎のコスト情報を取得していますが、usageType=MONTHLY(月毎)や usageType=HOURLY(時間毎)の指定も可能です。

また、下記コマンド例のように /metering/api/v1/usagecost/{accountId}/tagged を利用するとで、タグ指定によるコスト情報の取得も可能です。下記コマンド例の場合は ORCL:OCIService=Compute タグで絞り込みコスト情報を取得しています。

$ curl -X GET -u hogehoge@sample.com:YourPassword -H "X-ID-TENANT-NAME:<idcs-XXXXXXXX" "https://itra.oraclecloud.com/metering/api/v1/usagecost/<cacct-XXXXXXXX>tagged?startTime=2021-11-30T00:00:00.000&endTime=2021-11-30T23:59:59.999000.000&usageType=DAILYtags=ORCL%3AOCIService%3DCompute"

OCI Web コンソールのコスト分析では、Compute や Database といったサービス毎のコスト情報が確認できるかと思います。実は、同様に REST API でサービス毎のコスト情報を取得する際に、指定するタグ(ORCL:OCIService=XXXXX)の一覧をどのように確認するのかわからず途方にくれていたところ、下記の技術ブログに行き着き助けられました。

cloudii.jp

2年前の記事で、既に Account Metering REST API を試されていて「凄い…」の一言です。

OCI コスト情報の確認

以下は usageType=DAILY を指定して REST API を実行した際の JSON レスポンスの抜粋となりますが、items の中に subscriptionId が採番された複数のコスト情報が確認できます。加えて costs という項目が存在し、この中の computedAmount が利用料金になるようです。

    "items": [
    {
        "subscriptionId": "XXXXXX",
        "subscriptionType": "PRODUCTION",
        "serviceName": "ATP",
        "resourceName": "ATP_PAAS_OCPU_HOUR",
        "currency": "JPY",
        "gsiProductId": "XXXXX",
        "startTimeUtc": "2021-11-01T00:00:00.000",
        "endTimeUtc": "2021-11-02T00:00:00.000",
        "serviceEntitlementId": "XXXXXXX",
        "costs": [
        {
            "computedQuantity": 24,
            "computedAmount": 3709,
            "unitPrice": 161.292,
            "overagesFlag": "N"
        }
        ]
    },

そのため、JSON レスポンスの items 内の subscriptionId が採番された全データから、serviceName , resourceName が同一のコスト情報についての computedAmount の値を集計し、以下のような InfluxDB 格納用のデータ形式に変換することにしました。

    influx_data = {
        "measurement" : 'myusagecost',
        "tags": {
            'oci_service' : tag_name, <--タグ指定をした場合は該当タグ名
            'service' : serviceName,
            'resource' : resourceName,
        },
        "time": startTimeUtc,
        "fields":  {
            computedAmountAll <--computedAmoun を集計した値
        }
    }

その際、REST API でタグの絞り込みを実行した場合は oci_service に絞り込んだタグの名を付与しています。また、日毎と時間毎のそれぞれでデータを取得し、以下 4 種類のデータを InfluxDB に格納しました。

  1. タグによる絞り込みはせず取得した日毎のコスト情報
  2. タグによる絞り込みはせず取得した時間毎のコスト情報
  3. タグによる絞り込み(tagged)で取得した日毎のコスト情報
  4. タグによる絞り込み(tagged)で取得した時間毎のコスト情報

REST API で取得したコスト情報から computedAmount を集計し 4種類のデータを作成するスクリプトについては、 オラクル社のブログ記事 で紹介されている Python のコードをもとに作成しています。作成したスクリプト全文の紹介は割愛させていただきますが、下記は usageType=DAILY を指定して実行した REST API の取得データから serviceName , resourceName 毎に computedAmount の値を集計する箇所のサンプルコードとなります。

import collections
import requests
import datetime
import sys

def get_charges(meteringid, start_time, end_time, usage_type='DAILY'):

    username=meteringid['username']
    password=meteringid['password']
    idcs_guid=meteringid['idcs_guid']
    domain=meteringid['domain']

    compartmentbill=collections.defaultdict(lambda:collections.defaultdict(dict))

    url_params = {
        'startTime': start_time.isoformat() + '.000',
        'endTime': end_time.isoformat() + '.000',
        'usageType': usage_type,
        'computeTypeEnabled': 'Y'
    }

    resp = requests.get(
        'https://itra.oraclecloud.com/metering/api/v1/usagecost/' + domain,
        auth=(username, password),
        headers={'X-ID-TENANT-NAME': idcs_guid, 'Accept-Encoding': '*'},
        params=url_params
    )

    if resp.status_code != 200:
        print('Error in GET: {}'.format(resp.status_code), file=sys.stderr)
        raise Exception
    
    for item in resp.json()['items']:
        itemcost=0
        service=item['serviceName']
        resource=item['resourceName']
        time_utc = item['startTimeUtc']
        for cost in item['costs']:
            itemcost += cost['computedAmount']
        try:
            compartmentbill[time_utc][service][resource]+=itemcost
        except KeyError:
            compartmentbill[time_utc][service][resource]=itemcost
    
    return compartmentbill
    
def main():
    meteringid={
        'username': 'hogehoge@sample.com',
        'password': 'YourPassword',
        'idcs_guid': 'idcs-XXXXXXXX',
        'domain': 'cacct-XXXXXXXX'
    }
    start_time=datetime.datetime(2021, 11, 1, 0, 0)
    end_time=datetime.datetime(2021, 11, 3, 0, 0)
    ret_val=get_charges(meteringid, start_time, end_time)
    print(ret_val)

if __name__ == "__main__":
    main()

OCI コスト情報の可視化

InfluxDB に格納されたデータ

最終的に InfluxDB には、スクリプトで集計した OCI のコスト情報が下記のようなレコードで格納されているかたちです。

日毎のデータ

> select * from usagecost where service = 'ATP';
name: usagecost
time                 cost oci_service resource             service
----                 ---- ----------- --------             -------
2021-11-01T00:00:00Z 3709 ALL         ATP_PAAS_OCPU_HOUR   ATP
2021-11-01T00:00:00Z 439  Database    ATP_PAAS_XD_TB_MONTH ATP
2021-11-01T00:00:00Z 439  ALL         ATP_PAAS_XD_TB_MONTH ATP
2021-11-01T00:00:00Z 3709 Database    ATP_PAAS_OCPU_HOUR   ATP
2021-11-02T00:00:00Z 439  ALL         ATP_PAAS_XD_TB_MONTH ATP
2021-11-02T00:00:00Z 3548 Database    ATP_PAAS_OCPU_HOUR   ATP
2021-11-02T00:00:00Z 3548 ALL         ATP_PAAS_OCPU_HOUR   ATP
2021-11-02T00:00:00Z 439  Database    ATP_PAAS_XD_TB_MONTH ATP

時間毎のデータ

> select * from usagecost where service = 'ATP';
name: usagecost
time                 cost oci_service resource             service
----                 ---- ----------- --------             -------
2021-11-01T00:00:00Z 161  ALL         ATP_PAAS_OCPU_HOUR   ATP
2021-11-01T00:00:00Z 19   Database    ATP_PAAS_XD_TB_MONTH ATP
2021-11-01T00:00:00Z 19   ALL         ATP_PAAS_XD_TB_MONTH ATP
2021-11-01T00:00:00Z 161  Database    ATP_PAAS_OCPU_HOUR   ATP
2021-11-01T01:00:00Z 19   ALL         ATP_PAAS_XD_TB_MONTH ATP
2021-11-01T01:00:00Z 161  Database    ATP_PAAS_OCPU_HOUR   ATP
2021-11-01T01:00:00Z 161  ALL         ATP_PAAS_OCPU_HOUR   ATP
2021-11-01T01:00:00Z 19   Database    ATP_PAAS_XD_TB_MONTH ATP

oci_serviceALL となっているレコードは、REST API でタグ指定せず取得したコスト情報となります。なお、日毎と時間毎のデータで格納先 InfluxDB のデータベースを分けています。全体を通してももちろんそうですが、この辺の InfluxDB の使い方については、私の勉強不足でして、もっとスマートな方法があるのかもしれません…。あくまでご参考までの情報ということで、おおめに見て頂ければと思います。

Grafana の設定

あとは、Grafana の [Configuration] - [Data Sources] - [Add data source] より InfluxDB をデータソースとして追加し、ダッシュボードとグラフを作成するだけです。

グラフの追加

試しに、前述の「1.タグによる絞り込みはせず取得した日毎のコスト情報」のデータを使って、積立棒グラフを作成してみます。

Grafana の [Edit Panel] 画面の Query タブにて、下図のように Query を指定します。

1-8

[Panel]-[Display]-[Bars] を ON に変更します。

1-10

無事 resource (resourceName)で GROUP BY した棒グラフが表示されました!!

1-11

ダッシュボード作成

同様の手順で、前述の 2. から 4. のデータについてもグラフを作成したものが下図です。左側の 2 つが日毎のグラフ、右側の 2 つが時間毎のグラフです。また、上段のグラフが resource (resourceName)で GROUP BY し、下段のグラフが oci_service (ORCL:OCIService=XXXXX)で GROUP BY したものを表示しています。

1-2

もちろん、OCI Grafana Metrics プラグインで取得したリソースグラフと並べて表示することもできました!! 下記は試しに OCI Monitoring から取得した Compute インスタンスの CPU 使用率を並べてみたものです。

1-12

まとめ

Account Metering REST API から取得した OCI コスト情報を Grafana でカッコよく(?)可視化をすることができました。最初に申し上げましたとおり、OCI Web コンソール標準のコスト分析がとても使いやすいですので、特別な要件がない限り、わざわざ作り込んで、コスト情報を Grafana で可視化する必要はないと思うのですが、個人的な取り組みとしては、いろいろと発見があり楽しかったです。 特にいくつかの OCI 機能に触れたり、普段使わない OSS を試す中で、自身の勉強不足が身にしみました。他の方の技術ブログに助けれました…。来年はもう少し幅広い技術をキャッチアップしていこうと思います。 最後までお読みいただきありがとうございます。