はじめに
皆さんはCUR、活用していますでしょうか。FinOps実践のための第一歩であり、とりあえず有効化されている方は多いんじゃないかと思います。AWSのコスト管理において、CUR(Cost and Usage Report) はコストの詳細を把握するための基本的なデータソースです。2023年末には後継として CUR 2.0 が登場し、現在は AWS BCM Data Exports というサービスを通じて管理・設定を行います。
Data Exportsには、マネジメントコンソールから操作できる機能に加えて、CLIを利用することで初めて使える機能 がいくつか存在します。その中でも特に活用できるのが、エクスポートするデータをSQLクエリで絞り込める機能 です。エクスポートするカラムを選択したり、WHERE句で特定の条件に合う行だけをエクスポートしたりすることが可能で、この機能を使いこなすことでコストや運用効率を改善できます。
本記事では、CURをカスタマイズするとどんな嬉しいことがあるのかを説明した後、Data ExportsでSQLクエリを設定するための具体的な手順をステップバイステップで紹介します。
CUR 2.0 / Data Exportsのおさらい
まず、そもそもCURとDataExportとは何なのかについておさらいしていきます。
CUR とは
CUR(Cost and Usage Report)は、AWSの利用コストと使用量の詳細データをS3にエクスポートする機能です。ラインアイテム単位(サービス・アカウント・リージョン・リソースごと)の詳細なコストデータを持ち、Athena/Glueと組み合わせてコスト分析に広く活用されています。
さらに、従来のCURの後継として CUR 2.0 の提供が開始されました。CUR 2.0はAWS BCM Data Exports(Billing and Cost Management Data Exports)というサービスから管理します。CUR 2.0ではParquetフォーマットへの対応や、新しいカラムの追加など、いくつかの改善が加えられています。
Data Exportsで扱えるテーブル
Data Exportsでは、複数のテーブルからデータをエクスポートできます。主なものは以下の通りです。
| テーブル名 | 説明 |
|---|---|
COST_AND_USAGE_REPORT |
CUR 2.0。コストと使用量の詳細データ(本記事の主役) |
COST_AND_USAGE_DASHBOARD |
CIDなど各種ダッシュボード向けのビュー |
FOCUS_1_2_AWS |
FinOps Open Cost & Usage Specification形式 |
CARBON_EMISSIONS |
カーボンエミッションデータ |
COST_OPTIMIZATION_RECOMMENDATIONS |
Cost Optimization Hub のレコメンデーション |
本記事では、COST_AND_USAGE_REPORTテーブル(CUR 2.0)を対象とします。
CURをカスタマイズすると嬉しいこと
では、本題です。CURをSQLでカスタマイズすることで、具体的にどのようなメリットがあるのでしょうか。
① S3ストレージコストの削減
CUR 2.0のCOST_AND_USAGE_REPORTテーブルには、100列以上のカラムが存在します。しかし、実際のコスト分析でこれら全列を使うことはほとんどなく、多くの場合は数十列もあれば十分なはずです。
デフォルト設定では全列がエクスポートされますが、SQLのSELECT句で必要なカラムのみを指定することで、エクスポートファイルのサイズを大幅に削減 できます。Parquetは列指向フォーマットなので、列を絞るほどファイルサイズへの効果は顕著です。
② クエリパフォーマンスの向上
S3に出力されたParquetファイルをAthenaでクエリするケースでは、ファイルのサイズが小さいほどスキャン量が減り、クエリが速くなり、Athenaのコストも下がります。特に毎日・毎時間更新されるCURを頻繁にクエリする場合、この効果は積み重なります。
③ 関心のあるデータへの絞り込み
WHERE句を使うことで、特定のサービス・アカウント・リージョンのデータだけをエクスポート することができます。例えば、以下のような絞り込みが可能です。
| ユースケース例 | WHERE句の例 |
|---|---|
| 特定サービスのみ対象 | WHERE line_item_product_code = 'AmazonEC2' |
| 税金(Tax)行を除外 | WHERE line_item_line_item_type != 'Tax' |
| 特定アカウントのみ | WHERE line_item_usage_account_id IN ('111111111111', '222222222222') |
| 特定リージョンのみ | WHERE product_region_code = 'ap-northeast-1' |
「EKS関連のコストだけを別のS3パスに出力したい」「開発環境アカウントのデータだけを分析チームに提供したい」といったニーズにも対応できます。
④ データ量の増加を抑制
AWS環境が大きくなるにつれて、CURのファイルサイズも増大します。SQLクエリによる列・行の絞り込みは、成長するAWS環境においてもデータ量の増加をコントロールする手段 となります。
マネジメントコンソールとCLIでできることの違い
実は、上で説明したSQLクエリの設定は、マネジメントコンソールからは行えません。
マネジメントコンソールのData Exports設定画面では、エクスポート名・S3の出力先・TIME_GRANULARITY(時間粒度)などの基本的な設定のみが可能で、SQLクエリを直接入力する画面は提供されていません。
CLIやAPIを利用した場合にのみ使える主な機能を下表にまとめます。
| 機能 | コンソール | CLI / API |
|---|---|---|
| エクスポートの基本設定(名前・S3出力先など) | ✅ | ✅ |
| TIME_GRANULARITY(HOURLY / DAILY / MONTHLY) | ✅ | ✅ |
| INCLUDE_RESOURCES(リソースIDの含有) | ✅ | ✅ |
| 出力カラムをSQLのSELECTで指定 | ❌ | ✅ |
| WHERE句による行フィルタリング | ❌ | ✅ |
| INCLUDE_SPLIT_COST_ALLOCATION_DATA | 一部 | ✅ |
| INCLUDE_IAM_PRINCIPAL_DATA | ❌ | ✅ |
なお、Data Exportsで利用可能なSQLはフルのSQLではなく、限定されたサブセットです。サポートされている構文は SELECT [columns] FROM [table] WHERE [conditions] の形式で、JOINや集計関数(SUM, COUNTなど)、GROUP BY、ORDER BYはサポートされていません。
手順
手順の全体像
| # | 内容 |
|---|---|
| 1 | S3バケットの準備(バケットポリシーの設定) |
| 2 | カスタムSQLを設定したData Exportの作成 |
| 3 | エクスポートの状態確認 |
| 4 | エクスポート結果の確認 |
なお、Data Exportsの作成は us-east-1(バージニア北部)リージョン で行う必要があります(グローバルサービスのため)。
1. S3バケットの準備
バケットの作成
エクスポート先となるS3バケットを用意します。既存のバケットを流用する場合は次のバケットポリシーの設定へ進んでください。
# 環境変数の設定
ACCOUNT_ID="123456789012" # ご自身のAWSアカウントID
BUCKET_NAME="cur-custom-${ACCOUNT_ID}"
REGION="ap-northeast-1" # バケットを作成するリージョン
# バケットの作成
aws s3api create-bucket \
--bucket ${BUCKET_NAME} \
--region ${REGION} \
--create-bucket-configuration LocationConstraint=${REGION}
バケットポリシーの設定
Data ExportsサービスがS3バケットにファイルを書き込めるよう、バケットポリシーを設定します。
以下の内容を bucket-policy.json として保存し、適用します。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "EnableAWSDataExportsToWriteToS3AndCheckPolicy",
"Effect": "Allow",
"Principal": {
"Service": "bcm-data-exports.amazonaws.com"
},
"Action": [
"s3:PutObject",
"s3:GetBucketPolicy"
],
"Resource": [
"arn:aws:s3:::YOUR-BUCKET-NAME",
"arn:aws:s3:::YOUR-BUCKET-NAME/*"
],
"Condition": {
"StringLike": {
"aws:SourceArn": "arn:aws:bcm-data-exports:us-east-1:YOUR-ACCOUNT-ID:export/*",
"aws:SourceAccount": "YOUR-ACCOUNT-ID"
}
}
}
]
}
# バケットポリシーの適用
aws s3api put-bucket-policy \
--bucket ${BUCKET_NAME} \
--policy file://bucket-policy.json
2. カスタムSQLを設定したData Exportの作成
エクスポート定義ファイルの作成
以下のJSONファイルを export-definition.json として作成します。このファイルがData Exportの設定の核心部分です。
今回のサンプルでは、「よく使う主要カラムだけを選択し、Tax(税金)行を除外する」というカスタマイズを行っています。
{
"Name": "custom-cur-export",
"Description": "カスタムSQL付きCUR 2.0エクスポート",
"DataQuery": {
"QueryStatement": "SELECT identity_line_item_id, identity_time_interval, bill_payer_account_id, bill_billing_period_start_date, bill_billing_period_end_date, line_item_usage_account_id, line_item_usage_account_name, line_item_line_item_type, line_item_usage_start_date, line_item_usage_end_date, line_item_product_code, line_item_usage_type, line_item_operation, line_item_availability_zone, line_item_usage_amount, line_item_unblended_cost, line_item_blended_cost, line_item_line_item_description, product_region_code, product_instance_type, product_product_family, pricing_term, pricing_unit, reservation_reservation_a_r_n, reservation_effective_cost, savings_plan_savings_plan_a_r_n, savings_plan_savings_plan_effective_cost, resource_tags, cost_category FROM COST_AND_USAGE_REPORT WHERE line_item_line_item_type != 'Tax'",
"TableConfigurations": {
"COST_AND_USAGE_REPORT": {
"TIME_GRANULARITY": "DAILY",
"INCLUDE_RESOURCES": "TRUE",
"INCLUDE_SPLIT_COST_ALLOCATION_DATA": "FALSE",
"INCLUDE_MANUAL_DISCOUNT_COMPATIBILITY": "FALSE"
}
}
},
"DestinationConfigurations": {
"S3Destination": {
"S3Bucket": "YOUR-BUCKET-NAME",
"S3Prefix": "cur-custom",
"S3Region": "ap-northeast-1",
"S3OutputConfigurations": {
"OutputType": "CUSTOM",
"Format": "PARQUET",
"Compression": "PARQUET",
"Overwrite": "OVERWRITE_REPORT"
}
}
},
"RefreshCadence": {
"Frequency": "SYNCHRONOUS"
}
}
主要な設定項目を説明します。
DataQuery.QueryStatement
ここにSQLクエリを記載します。SELECTで出力するカラムを指定し、FROMにテーブル名(COST_AND_USAGE_REPORT)を指定します。行を絞り込む場合はWHERE句を追記します。
利用可能なカラム一覧は、以下のCLIコマンドで確認できます。
aws bcm-data-exports get-table \
--table-name COST_AND_USAGE_REPORT \
--region us-east-1 \
--query 'Schema[*].Name' \
--output text
DataQuery.TableConfigurations
QueryStatementに加え、テーブル固有のオプションを設定します。COST_AND_USAGE_REPORTで利用できる主なオプションは以下の通りです。
| プロパティ名 | 説明 | 指定可能値 | デフォルト |
|---|---|---|---|
TIME_GRANULARITY |
データの時間粒度 |
HOURLY / DAILY / MONTHLY
|
HOURLY |
INCLUDE_RESOURCES |
リソースIDをレポートに含めるか |
TRUE / FALSE
|
FALSE |
INCLUDE_SPLIT_COST_ALLOCATION_DATA |
Split Cost Allocationデータを含めるか |
TRUE / FALSE
|
FALSE |
INCLUDE_MANUAL_DISCOUNT_COMPATIBILITY |
手動割引の互換データを含めるか |
TRUE / FALSE
|
FALSE |
INCLUDE_IAM_PRINCIPAL_DATA |
IAMプリンシパルのカラムを含めるか |
TRUE / FALSE
|
- |
S3OutputConfigurations
| 設定 | 今回の選択 | 説明 |
|---|---|---|
Format |
PARQUET |
出力フォーマット(PARQUET または TEXT_OR_CSV) |
Compression |
PARQUET |
圧縮形式(PARQUET形式の場合はPARQUET固定) |
OutputType |
CUSTOM |
SQLクエリを使う場合はCUSTOMを指定する |
Overwrite |
OVERWRITE_REPORT |
同一期間のデータを上書きするか |
OutputTypeは必ずCUSTOMを指定してください。 OutputTypeに指定できる値はCUSTOMのみです。
また、Overwriteフィールドについても注意が必要です。CREATE_NEW_REPORTを指定すると更新のたびに新規ファイルが追記されデータが重複するため、基本的にはOVERWRITE_REPORT(同一期間のデータを上書き)を推奨します。
create-exportコマンドの実行
aws bcm-data-exports create-export \
--export file://export-definition.json \
--region us-east-1
実行結果の例:
{
"ExportArn": "arn:aws:bcm-data-exports:us-east-1:123456789012:export/custom-cur-export-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
返却されたExportArnは次のステップで使用するので控えておきます。
3. エクスポートの状態確認
エクスポートの設定内容の確認
作成したエクスポートの設定が正しく反映されているか確認します。
EXPORT_ARN="arn:aws:bcm-data-exports:us-east-1:123456789012:export/custom-cur-export-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
aws bcm-data-exports get-export \
--export-arn ${EXPORT_ARN} \
--region us-east-1 \
--no-cli-pager
DataQuery.QueryStatementに設定したSQLが正しく反映されていることを確認します。
エクスポート実行履歴の確認
Data Exportsの実行(Execution)状況は以下で確認できます。
aws bcm-data-exports list-executions \
--export-arn ${EXPORT_ARN} \
--region us-east-1
ExecutionStatus.StatusCodeがSUCCESSになっていれば、S3へのエクスポートが完了しています。
ステータスコードの意味は以下の通りです。
| StatusCode | 説明 |
|---|---|
QUEUED |
キューに入っている |
IN_PROGRESS |
実行中 |
SUCCESS |
正常完了 |
FAILED |
失敗 |
4. エクスポート結果の確認
S3バケットに出力されたParquetファイルを確認します。
# エクスポートされたファイルの確認
aws s3 ls s3://${BUCKET_NAME}/cur-custom/ --recursive
出力されたParquetファイルは、Athenaから直接クエリすることもできます。S3のバケットとプレフィックスをAthena外部テーブルとして登録するか、AWS Glue Crawlerで自動的にスキーマを検出させることで、Athena上でSQLクエリを実行できます。
エクスポートされたファイルに指定したカラムのみが含まれていること、WHERE句の条件が反映されていることを確認しましょう。
補足:既存のエクスポートをSQLクエリ付きに更新したい場合
マネジメントコンソールで作成した既存のエクスポートにSQLクエリを追加したい場合は、update-exportコマンドを使います。
aws bcm-data-exports update-export \
--export-arn ${EXPORT_ARN} \
--export file://export-definition.json \
--region us-east-1
ただし、OutputTypeをCREATE_NEW_REPORTからCUSTOMに変更する更新はサポートされていません。その場合は、CUSTOMを指定した新規エクスポートを作り直す必要があります。
まとめ
本記事では、AWS BCM Data ExportsのCUR 2.0に対して、CLIを利用してSQLクエリを設定する方法を紹介しました。
マネジメントコンソールからは設定できないSQLクエリ機能を活用することで、以下のメリットが得られます。
- S3ストレージコストの削減: 不要なカラムを除外し、エクスポートファイルのサイズを削減
- Athenaクエリの高速化・コスト削減: スキャン量が減ることでクエリが速くなりコストも下がる
- 関心データへの絞り込み: WHERE句で特定サービス・アカウントのデータだけをエクスポート
CURをそのまま使うのも一つの選択ですが、扱うデータ量が増えてきたり、コスト分析の用途が明確になってきたりしたタイミングでカスタマイズを検討してみてはいかがでしょうか。