AWS の請求金額をモニタリングする
AWS の請求金額をモニタリングする:
前回の記事、AWS EC2 インスタンスが何台起動しているかをモニタリングする では AWS で起動しているインスタンスの数を定期的にカウントできるようにしました。
そうしたところ、やっぱり実際の請求金額も見たいというご要望がありましたので、今回はサービスごとの請求金額を次のような積み上げグラフとしてモニタリングできるようにします。
以下設定で新しくlambda 関数を作成します。
※ 詳細な手順は、前回記事 AWS Lambda 関数の実装 を参考にしてください。
このコードで lambda_function を置き換えます。
lambda_function (クリックして表示してください)
この項目は設定には影響しませんので、興味があれば読んでみてください。
今回作成したコードは以下の流れになっています。
まず、請求が発生しているサービスを取得します。
集計期間は月初めから今日までとします。
このとき、サービスの区分は請求書と同じにしたいので、'USAGE_TYPE_GROUP' を指定します。
問い合わせの結果を見てみます。
今回はデータ転送、EC2、 S3 で集計を分けたいので、それぞれのサービス名をリストアップします。
まず、サービス名に
残りのうち、
それ以外のサービスは最後にまとめて数えるため、ここではリストにいれません。
※この分類は請求書と照らし合わせながら筆者が独断で決めました。AWSの仕様変更によっては今後変わる可能性が大いにあります。
次にサービスを指定して請求金額を取得します。
サービスを指定する場合は
このように返ってきます。
最後に取得した金額をメトリクスに登録します。
作成した AWS Lambda 関数を定期的に呼び出すようにします。
ここで登録したメトリクスは CloudWatchメトリクス → COST_MONITOR → service と移動し、すべてにチェックを入れるとグラフ表示することができます。
積み上げ表示にしたいので右上のメニューから「折れ線グラフ」を「スタックエリア」に変更します。
作成したグラフと実際の請求書を見比べてみます。
まず、グラフにカーソルを当てて詳細を確認します。
次に実際の請求書を確認します。
テーブルで金額を表してみます。ほぼ一致していることが確認できます。
(*) 196.31(総額) - (17.19+80.39+8.05)
CloudWatch ダッシュボードを作成して、前回作成したインスタンス起動数のグラフと並べてみます。
当然ながら、インスタンス起動数と連動して Elastic Compute Cloud の金額も上昇しているように見えます。
以上です。
前回の記事、AWS EC2 インスタンスが何台起動しているかをモニタリングする では AWS で起動しているインスタンスの数を定期的にカウントできるようにしました。
そうしたところ、やっぱり実際の請求金額も見たいというご要望がありましたので、今回はサービスごとの請求金額を次のような積み上げグラフとしてモニタリングできるようにします。
- AWS への請求金額を定期的に集計します。
- 集計は請求金額の大半を占める EC2 と S3、油断したときに発生するデータ転送量、それ以外で分けるようにします。
1. AWS Lambda 関数を作成する
1.1 設定
以下設定で新しくlambda 関数を作成します。※ 詳細な手順は、前回記事 AWS Lambda 関数の実装 を参考にしてください。
- 名前:monitoring_billing
- ランタイム:Python 2.7
- ロール:「カスタムロールの作成」を選択して以下の権限を与えてください
- "ce:GetDimensionValues"
- "ce:GetCostAndUsage"
- "cloudwatch:PutMetricData"
1.2 コードの編集
このコードで lambda_function を置き換えます。lambda_function (クリックして表示してください)
lambda_function
# -*- coding: utf-8 -*- """ Created on Tue Sep 11 13:22:20 2018 @author: Okada """ import json import boto3 import datetime def get_amount(responce): return float(responce["ResultsByTime"][0]["Total"]["UnblendedCost"]["Amount"]) def get_services(start_day, end_day): responce = boto3.client('ce').get_dimension_values( TimePeriod = {"Start": start_day, "End": end_day}, Dimension = 'USAGE_TYPE_GROUP', Context = 'COST_AND_USAGE' ) data_transfer = [] ec2 = [] s3 = [] for item in responce["DimensionValues"]: service = item["Value"].split(":") if "Data Transfer - " in service[1]: data_transfer.append(item["Value"]) elif service[0].lower() == "ec2": ec2.append(item["Value"]) elif service[0].lower() == "s3": s3.append(item["Value"]) return [data_transfer, ec2, s3] def get_cost_and_usage (start_day, end_day, services=[]): if len(services) == 0: return (boto3.client('ce').get_cost_and_usage( TimePeriod = {"Start": start_day, "End": end_day}, Granularity = "MONTHLY", Metrics = ["UnblendedCost"] )) return (boto3.client('ce').get_cost_and_usage( TimePeriod = {"Start": start_day, "End": end_day}, Granularity = "MONTHLY", Metrics = ["UnblendedCost"], Filter = {"Dimensions": {"Key": "USAGE_TYPE_GROUP", "Values": services}} )) def put_metric_data(service, value): return (boto3.client("cloudwatch").put_metric_data( Namespace="COST_MONITOR", MetricData=[{ "MetricName": "service-billing", "Dimensions": [{"Name":"service", "Value": service}], "Value": value, "Unit": "None" }] )) def lambda_handler(event, context): now = datetime.datetime.utcnow() start_dt = now - datetime.timedelta(days = 1) start_day = "%d-%02d-%02d" % (start_dt.year, start_dt.month, 1) end_day = now.strftime("%Y-%m-%d") [data_transfer, ec2, s3] = get_services(start_day, end_day) status = {"Start": start_day, "End": end_day, "Unit": "USD", "Values": {}} target = {"Data Transfer": data_transfer, "Elastic Compute Cloud": ec2, "Simple Storage Service": s3} known_amount = 0.0 for label in target: responce = get_cost_and_usage (start_day, end_day, target[label]) amount = get_amount(responce) put_metric_data(label, amount) status["Values"][label] = amount known_amount += amount responce = get_cost_and_usage (start_day, end_day) amount = get_amount(responce) other_amount = amount - known_amount put_metric_data("other", other_amount) status["Values"]["other"] = other_amount status["Values"]["total"] = sum(status["Values"].values()) status["Total"] = amount return { "statusCode": 200, "body": json.dumps(status) }
1.3 コードの解説
この項目は設定には影響しませんので、興味があれば読んでみてください。今回作成したコードは以下の流れになっています。
- 1. 請求の集計期間を月初めから今日までと定義する
- 2. ec2, s3, データ転送の金額を個別に集計する
- 2.1. サービスの請求金額を取得
- 2.2. サービスの請求金額をメトリクスに登録
- 2.3. メトリクス登録済み請求金額を覚えておく
- 3. aws 全体の請求金額を取得
- 4. (サービスの請求金額 - 登録済み請求金額) を Other としてメトリクスに登録
まず、請求が発生しているサービスを取得します。
集計期間は月初めから今日までとします。
このとき、サービスの区分は請求書と同じにしたいので、'USAGE_TYPE_GROUP' を指定します。
get_services関数
import boto3 start_day = "2018-10-01" end_day = "2018-10-12" responce_service = boto3.client('ce').get_dimension_values( TimePeriod = {"Start": start_day, "End": end_day}, Dimension = 'USAGE_TYPE_GROUP', Context = 'COST_AND_USAGE' )
u'Value':
の項目がサービス名です。>>> import pprint >>> pprint.pprint(responce_service) {u'DimensionValues': [{u'Attributes': {u'unit': u'GB'}, u'Value': u'EC2: Data Transfer - CloudFront (Out)'}, {u'Attributes': {u'unit': u'GB'}, u'Value': u'EC2: Data Transfer - Inter AZ'}, {u'Attributes': {u'unit': u'GB'}, u'Value': u'EC2: Data Transfer - Internet (In)'}, ... {u'Attributes': {u'unit': u'GB'}, u'Value': u'S3: Data Transfer - Region to Region (In)'}, {u'Attributes': {u'unit': u'GB'}, u'Value': u'S3: Data Transfer - Region to Region (Out)'}, {u'Attributes': {u'unit': u'GB-Month'}, u'Value': u'S3: Storage - Standard'}], 'ResponseMetadata': {...}, u'ReturnSize': 19, u'TotalSize': 19}
まず、サービス名に
Data Transfer -
と入っているものをデータ転送とします。残りのうち、
S3:
で始まるものを S3、EC2:
で始まるものを EC2 として分けます。それ以外のサービスは最後にまとめて数えるため、ここではリストにいれません。
※この分類は請求書と照らし合わせながら筆者が独断で決めました。AWSの仕様変更によっては今後変わる可能性が大いにあります。
get_services関数
data_transfer = [] ec2 = [] s3 = [] for item in responce["DimensionValues"]: service = item["Value"].split(":") if "Data Transfer - " in service[1]: data_transfer.append(item["Value"]) elif service[0].lower() == "ec2": ec2.append(item["Value"]) elif service[0].lower() == "s3": s3.append(item["Value"])
Granularity = "MONTHLY"
オプションで月ごとの集計になります。"DAILY"
とすれば日ごとの集計になります。サービスを指定する場合は
Filer
オプションに前の関数で作成したサービスのリストを渡します。 Filer
オプションを指定しない場合は全請求金額が取得できます。get_cost_and_usage関数
services = ['S3: API Requests - Standard', 'S3: Data Transfer - Region to Region (In)', 'S3: Data Transfer - Region to Region (Out)', 'S3: Storage - Standard'] responce_cost = boto3.client('ce').get_cost_and_usage( TimePeriod = {"Start": start_day, "End": end_day}, Granularity = "MONTHLY", Metrics = ["UnblendedCost"], Filter = {"Dimensions": {"Key": "USAGE_TYPE_GROUP", "Values": services}} )
>>> pprint.pprint(responce_cost) {'ResponseMetadata': {...}, u'ResultsByTime': [{u'Estimated': True, u'Groups': [], u'TimePeriod': {u'End': u'2018-10-12', u'Start': u'2018-10-01'}, u'Total': {u'UnblendedCost': {u'Amount': u'175.0786809984', u'Unit': u'USD'}}}]}
put_metric_data関数
service = "Simple Storage Service" value = float(responce_cost["ResultsByTime"][0]["Total"]["UnblendedCost"]["Amount"]) boto3.client("cloudwatch").put_metric_data( Namespace="COST_MONITOR", MetricData=[{ "MetricName": "service-billing", "Dimensions": [{"Name":"service", "Value": service}], "Value": value, "Unit": "None" }] ))
2. モニタリング設定
作成した AWS Lambda 関数を定期的に呼び出すようにします。
2.1 CloudWatch ルール
- イベントソース:スケジュール 6 時間 ごと
- ターゲット:Lambda 関数 monitoring_billing (今回作成したlambda関数)
ここで登録したメトリクスは CloudWatchメトリクス → COST_MONITOR → service と移動し、すべてにチェックを入れるとグラフ表示することができます。
積み上げ表示にしたいので右上のメニューから「折れ線グラフ」を「スタックエリア」に変更します。
3. まとめ
3.1 グラフを確認する
作成したグラフと実際の請求書を見比べてみます。まず、グラフにカーソルを当てて詳細を確認します。
次に実際の請求書を確認します。
テーブルで金額を表してみます。ほぼ一致していることが確認できます。
項目 | グラフ | 請求書 |
---|---|---|
Data Transfer | 17.2 | 17.19 |
Elastic Compute Cloud | 80.4 | 80.39 |
Simple Storage Service | 8.05 | 8.05 |
Other | 90.7 | 90.68(*) |
3.2 インスタンス起動数と並べて表示する
CloudWatch ダッシュボードを作成して、前回作成したインスタンス起動数のグラフと並べてみます。当然ながら、インスタンス起動数と連動して Elastic Compute Cloud の金額も上昇しているように見えます。
以上です。
コメント
コメントを投稿