エムオーテックス株式会社が運営するテックブログです。

続・AWS Cost and Usage Reportを活用したコスト分析・最適化の取り組み

はじめに

こんにちは、SREチームの植松です。

以前に公開した記事では、AWS Cost and Usage Report(CUR)に蓄積されたコストデータをAthenaやQuickSightを用いて分析・可視化する取り組みについてご紹介しました。

tech.motex.co.jp

今回の記事では、前回からのアップデートとして、SREチームで実際に使用している分析・可視化の具体例や、 LANSCOPE エンドポイントマネージャークラウド版のAWSコスト最適化事例について紹介したいと思います。

AWS Cost and Usage Reportの導入方法については、前回のブログAWS公式資料で紹介されているので 本記事では割愛させていただきます。

事例1:アカウント・サービス横断でコストを分析する

AWSアカウントやサービス全体のコストを俯瞰して分析したい時は、以下のクエリを活用しています。

SELECT
    line_item_usage_account_id,
    line_item_product_code,
    year || LPAD(month, 2, '0') as year_month,
    SUM(line_item_unblended_cost) AS cost
FROM
    ${table_name}
GROUP BY
    line_item_usage_account_id,
    line_item_product_code,
    year || LPAD(month, 2, '0')
ORDER BY
    line_item_product_code asc,
    cost desc

クエリを実行すると以下の画像のようにコストが発生したアカウントID、AWSサービス名、年月、コストが表示されます。 このままだと見づらいのでQuickSightもしくはExcelに取り込んでピボットテーブルを作成します。

アカウント・サービス横断のコスト集計結果(抜粋)

ぼかしが多くて恐縮ですが、以下の画像のようにピボットテーブルの行をline_item_product_code、account 列をyear_monthとすることで、サービスごとに各AWSアカウントのコストを時系列で表示できるようになります。

QuickSightの条件付き書式で、
- 前月より1000$以上コスト増 -> 赤色
- 前月より 500$以上コスト増 -> 黄色
- 額に関わらず前月から1.5倍-> 青色
色付けしており、見るべきポイントを絞るようにしています。 色分けのルールは他社事例を参考にしています。

AWSのコストモニタリングの知見をシェアしたい - Uzabase for Engineers

アカウント・サービス横断コストをQuickSightに取り込み

エムオーテックスでは開発、ステージング、本番環境・・・などの用途別にAWSアカウントを分離しています。 開発用のAWSアカウントは複数あり、開発アカウント間での大きなコストの違いは通常ありません。 ただ、ダッシュボードを眺めていたところ、ある月のDynamoDBコストについて開発用のAWSアカウント間で 大きな差があることに気づきました。 コストは以下のようになっていました。

開発用AWSアカウント ある月のDynamoDBコスト($)
A 1,178.79
B 192.57
C 116.27

Cost Explorerでリソース名でグルーピングしたコストを見てみると、「その他」のコストが大部分を占めており、特定のリソースが大きく伸びているというわけではないことがわかります。

ある開発アカウントのDynamoDBのデイリーコスト

では、どのリソースにコストがかかっているのでしょうか?という疑問もCURのクエリで解決します。 以下はあるアカウントのDynamoDBのリソースID、API操作、使用状況の明細別のコストを抽出するクエリです。 line_item_product_codeやyear、monthを変えることで他のサービスや年月にも応用できます。

SELECT
    line_item_usage_account_id,
    line_item_product_code,
    line_item_resource_id,
    line_item_operation,
    line_item_usage_type,
    SUM(line_item_unblended_cost) AS cost
FROM
    ${table_name}
WHERE
    line_item_usage_account_id = ${account_id}
AND year = '2023'
AND month = '10'
AND line_item_product_code = 'AmazonDynamoDB'
GROUP BY
    line_item_usage_account_id,
    line_item_product_code,
    line_item_resource_id,
    line_item_operation,
    line_item_usage_type

ピボットテーブルにしてみた結果、多数のテーブルでCommittedThroughputのコストがかかっていることがわかりました。 DynamoDBのプロビジョンドキャパシティを設定している場合にこのコストがかかります。 流量が少ない開発アカウントは原則オンデマンドキャパシティとしているため、 CommittedThroughputコストがかかっている状況は想定外となります。 スクリプトで各テーブルをオンデマンドキャパシティへと変更することで、月あたり1000$ほどのコスト最適化ができました。

このように、アカウント・サービス横断でコストを俯瞰することで、環境間のコストの違いや異常に気づきやすくなります。

ピボットテーブル

事例2:EBS・RDSのスナップショット一覧を抽出する

リリース作業時のバックアップでEBSやRDSの手動スナップショットを取ることがありますが、 不要になったタイミングで削除できていないケースが散見されました。

以下のクエリを実行することでEBS、RDSのスナップショット一覧と1年間のコスト見積もりを集計することができます。

SELECT
    line_item_usage_account_id,
    product_region AS region,
    line_item_usage_type,
    line_item_resource_id AS resource_id,
    (SUM(pricing_public_on_demand_cost) / 31) * 365 AS annual_public_cost
FROM
    ${table_name}
WHERE
    (
        (
            line_item_product_code = 'AmazonEC2'
        AND line_item_line_item_type = 'Usage'
        AND line_item_usage_type LIKE '%Snapshot%'
        )
    OR  line_item_usage_type = 'APN1-Aurora:BackupUsage'
    )
AND line_item_usage_start_date >= current_date - interval '31' day
AND line_item_usage_start_date < current_date
GROUP BY
    line_item_usage_account_id,
    product_region,
    line_item_usage_type,
    line_item_resource_id

以下が抽出結果の抜粋になります。エムオーテックスはAWS Backupを利用してRDS等の自動バックアップを取得しているため、 ここで抽出されたリソースは不要なものになります。 本当に削除しても問題ないかの精査は必要ですが、合計2300$程度の最適化が見込めました。

EBS、RDSのスナップショット抽出結果(抜粋)

事例3:使用タイプの最大値と最小値に大きな差があるリソースを抽出する

CURは各AWSリソースの使用タイプ(明細)をline_item_usage_typeというカラムに格納しています。 例えば、S3の「APN1-TimedStorage-ByteHrs」は、東京リージョンに保存しているデータに対する料金を意味します。

各リソース単位で使用タイプの最大値と最小値を比較し、大きな差があるリソースについては 最適化の余地があるのではないかと考えました。

例えば、ストレージ料金が大多数を占めるS3バケットやDynamoDBテーブルがあれば、 低頻度ストレージに置き換えができますし、使用量のスパイクやバグなどの理由で一部の使用タイプが跳ね上がっているリソースが見つかるかもしれません。

あるAWSリソースの使用タイプ 月間コスト($)
APN1-Requests-Tier1 1
APN1-Requests-Tier2 2
APN1-TimedStorage-ByteHrs 1000
APN1-USE1-AWS-Out-Bytes 3
APN1-USE2-AWS-Out-Bytes 4
APN1-USW2-AWS-Out-Bytes 5

上の表はある1つのAWSリソースのコストの内訳です。最大となる使用タイプはAPN1-TimedStorage-ByteHrs の1000$、 最小はAPN1-Requests-Tier1 の1$となります。最大-最小は999$となり大きな値であるといえます。 このような状態となっているAWSリソースを抽出するために、 最大-最小が一定額以上 または、数十~数百倍となるリソース名を抽出するクエリが以下になります。

WITH min_max_cost AS(
    select
        line_item_usage_account_id,
        line_item_product_code,
        line_item_resource_id,
        min(cost) as min_cost,
        max(cost) as max_cost
    from
        (
            select
                line_item_usage_account_id,
                line_item_product_code,
                line_item_resource_id,
                line_item_usage_type,
                SUM(line_item_unblended_cost) AS cost
            from
                ${table_name}
            where
                year = '2023'
            and month = '11'
            GROUP BY
                line_item_usage_account_id,
                line_item_product_code,
                line_item_resource_id,
                line_item_usage_type
            order by
                cost desc
        ) sub
    group by
        line_item_usage_account_id,
        line_item_product_code,
        line_item_resource_id
)
SELECT
    line_item_resource_id
FROM
    min_max_cost
WHERE
    (max_cost - min_cost) >= 500
OR  (
        max_cost >= min_cost * 100
    and max_cost > 500
    )

抽出されたリソースに関する使用タイプの例が以下になります。 総コストにおけるAPN1-TimedStorage-ByteHrsの割合が大多数を占めており、 アクセス頻度が少ないリソース(S3)が抽出できました。

低頻度ストレージに置き換えられそうなS3

こちらのリソース(S3)については想定外にリクエストコストがかかっており、 処理を見直すことで月1000$以上のコスト最適化ができました。

想定外にリクエストコストがあったリソース

おわりに

前回の記事から引き続き、CURとAthenaを利用したコスト分析・最適化の取り組みをご紹介しました。

AWSコストのモニタリングなどに関わっている方の参考になれば幸いです。