OCI の課金情報を Grafana で可視化してみた
この記事は、JPOUG Advent Calendar 2021 12日目の記事です。
11日目の記事は macchi_ora さんの「Oracle Clusterware用のREST APIを使ってみる」でした。
はじめに
JPOUG Advent Calendar 初参加 です。2020 年に Oracle 製品に関係する仕事から離れていたのですが、今年の夏より、某 SIer で Oracle 製品のサポート・エンジニアをしています。どうぞ宜しくお願いします。
私自身、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 から参照するような仕組みにしました。
Grafana を採用した理由としては、同環境に OCI Grafana Metrics プラグインを導入し OCI Monitoring のリソースデータをあわせて取得すれば、コスト情報と OCI のリソース情報を Grafana 上の同一ダッシュボードで閲覧できてカッコいいのでは 便利なのでは…と安易に考えたからです。
なお、OCI Grafana Metrics プラグインについては、昨年の JPOUG Advent Calendar の ryota_hnk さんの記事 Oracle Cloud監視入門 の中でも紹介されており、私も参考にさせていただきました。
Account Metering REST API
OCI で利用しているサービスの課金状況取得については、以下の Account Metering REST API の Resource Usage Cost を利用しました。
API 実行時の認証
ドキュメントにも記載がありますが Account Metering REST API を実行するには、以下の何れかのロールが必要となります。
また、REST API 実行時に IDCS-GUID(idcs-XXXXXXXX)と Accout ID(cacct-XXXXXXXX)が必要となります。私は、OCI Web コンソールから、以下の方法で確認しました。
IDCS-GUID と Account ID の確認方法
OCI Web コンソールにログイン後、画面右上の [プロファイル]‐[サービス・ユーザー・コンソール] をクリックします。
表示された MY Oracle サービスの URL より cacct-XXXXXXXX
からはじまる Accout ID を確認します。
同ページのサービス一覧より「Oracle Identity Cloud Service」 の [管理コンソール] をクリックします。
表示された Identity Cloud Service の URL より idcs‐XXXXXXXX
からはじまる IDCS-GUID を確認します。
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)の一覧をどのように確認するのかわからず途方にくれていたところ、下記の技術ブログに行き着き助けられました。
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 に格納しました。
- タグによる絞り込みはせず取得した日毎のコスト情報
- タグによる絞り込みはせず取得した時間毎のコスト情報
- タグによる絞り込み(tagged)で取得した日毎のコスト情報
- タグによる絞り込み(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_service
が ALL
となっているレコードは、REST API でタグ指定せず取得したコスト情報となります。なお、日毎と時間毎のデータで格納先 InfluxDB のデータベースを分けています。全体を通してももちろんそうですが、この辺の InfluxDB の使い方については、私の勉強不足でして、もっとスマートな方法があるのかもしれません…。あくまでご参考までの情報ということで、おおめに見て頂ければと思います。
Grafana の設定
あとは、Grafana の [Configuration] - [Data Sources] - [Add data source] より InfluxDB をデータソースとして追加し、ダッシュボードとグラフを作成するだけです。
グラフの追加
試しに、前述の「1.タグによる絞り込みはせず取得した日毎のコスト情報」のデータを使って、積立棒グラフを作成してみます。
Grafana の [Edit Panel] 画面の Query タブにて、下図のように Query を指定します。
[Panel]-[Display]-[Bars] を ON に変更します。
無事 resource
(resourceName)で GROUP BY
した棒グラフが表示されました!!
ダッシュボード作成
同様の手順で、前述の 2. から 4. のデータについてもグラフを作成したものが下図です。左側の 2 つが日毎のグラフ、右側の 2 つが時間毎のグラフです。また、上段のグラフが resource
(resourceName)で GROUP BY
し、下段のグラフが oci_service
(ORCL:OCIService=XXXXX)で GROUP BY
したものを表示しています。
もちろん、OCI Grafana Metrics プラグインで取得したリソースグラフと並べて表示することもできました!! 下記は試しに OCI Monitoring から取得した Compute インスタンスの CPU 使用率を並べてみたものです。
まとめ
Account Metering REST API から取得した OCI コスト情報を Grafana でカッコよく(?)可視化をすることができました。最初に申し上げましたとおり、OCI Web コンソール標準のコスト分析がとても使いやすいですので、特別な要件がない限り、わざわざ作り込んで、コスト情報を Grafana で可視化する必要はないと思うのですが、個人的な取り組みとしては、いろいろと発見があり楽しかったです。 特にいくつかの OCI 機能に触れたり、普段使わない OSS を試す中で、自身の勉強不足が身にしみました。他の方の技術ブログに助けれました…。来年はもう少し幅広い技術をキャッチアップしていこうと思います。 最後までお読みいただきありがとうございます。
Oracle VM Manager の MySQL 接続時に Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' が発生
TL;DR
- Oracle VM Manager の MySQL に mysql クライアントで接続する際に Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' が発生した
- Oracle VM Manager の MySQL は my.cnf を /u01/app/oracle/mysql/data に配置し、起動時に --defaults-file で指定していた
- mysql コマンドクライアントの -S オプションで sock ファイルの PATH を指定し接続できた
環境
事象
Oracle VM Manager の内部では Weblogic Server と MySQL がインストールされています。Oracle VM Manager 上の MySQL について確認したいと思い mysql
コマンドで接続しようとしたところ、以下のメッセージで接続ができませんでした。
$ mysql -u root -p Enter password: ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (2)
/var/lib/mysql 配下を確認すると mysql.sock
がないので、ネットの情報などから touch コマンドで sock ファイルを手動作成しましたが事象は変わらず。
調べたこと
OracleVM の MySQL は起動時に /u01/app/oracle/mysql/data 配下の my.cnf ファイルを --defaults-file
オプションで指定しているようでした。
$ systemctl status ovmm_mysql.service ● ovmm_mysql.service - LSB: start and stop MySQL Loaded: loaded (/etc/rc.d/init.d/ovmm_mysql; bad; vendor preset: disabled) Active: active (running) since 火 2021-10-05 08:42:59 JST; 11h ago Docs: man:systemd-sysv-generator(8) Process: 1039 ExecStart=/etc/rc.d/init.d/ovmm_mysql start (code=exited, status=0/SUCCESS) CGroup: /system.slice/ovmm_mysql.service ├─1077 /bin/sh /usr/bin/mysqld_safe --defaults-file=/u01/app/oracle/mysql/data/my.cnf --user=oracle --datadir=/u01/app/oracle/mysql/data --pid-file=/u01/app/o... └─1612 /usr/sbin/mysqld --defaults-file=/u01/app/oracle/mysql/data/my.cnf --basedir=/usr --datadir=/u01/app/oracle/mysql/data --plugin-dir=/usr/lib64/mysql/pl...
上記パス(/u01/app/oracle/mysql/data)の my.cnf で [client] グループに sock は指定されていたのですが、my.cnf は /u01/app/oracle/mysql/data のみしか存在しなかったため mysql コマンドクライアント起動時に、Build 時のデフォルトである /var/lib/mysql/mysql.sock が参照されてしまっていた(?)模様…
$ cat /u01/app/oracle/mysql/data/my.cnf | grep -A 2 "\[client\]" [client] socket=/u01/app/oracle/mysql/data/mysqld.sock port=49500 $ mysql --help | grep my.cnf order of preference, my.cnf, $MYSQL_TCP_PORT, /etc/my.cnf /etc/mysql/my.cnf /usr/etc/my.cnf ~/.my.cnf
対策
以下のいずれかで無事 Oracle VM Manager 上の MySQL に接続できました。
- mysql コマンドクライアントで接続時に -S オプションで sock ファイルを指定
$ mysql -S /u01/app/oracle/mysql/data/mysqld.sock -u root -p
- mysql コマンドクライアントで接続時に --defaults-file オプションで my.cnf を指定
mysql --defaults-file=/u01/app/oracle/mysql/data/my.cnf -u root -p