💰

AWSのコスト可視化の取り組み

2025/01/20に公開

はじめに

こんにちは、レンティオでエンジニアをしている坂元です。

レンティオではAWSコスト最適化の一環として、
Cost and Usage Report(CUR)とAthena、Redash、Slackを使ったAWSコストの可視化を行ったのでその取り組みについて紹介します。

Cost and Usage Report(CUR)とは

https://docs.aws.amazon.com/ja_jp/cur/latest/userguide/what-is-cur.html

AWSのコストや使用状況の詳細なレポートをS3に出力してくれるサービスです。レポートは、Athena、Redshift、QuickSightとの統合などAWSの他サービスと連携して分析や可視化を行うことができます。

構成

レンティオではBIツールとしてRedashを使っており、RedashからAthenaのクエリを実行する環境が整っていました。
また、Redash Slack botでSlackからダッシュボードのキャプチャを確認できるようになっているので、Redashでダッシュボードを作成し毎日Slackにキャプチャを投稿する構成にしました。

構築

以下の手順で構築しました。
この手順はRedashやRedash Slack botの構築が済んでいることが前提の手順です。

  1. CURのレポート出力の設定
  2. Athenaのテーブル作成
  3. Redashでダッシュボード作成
  4. Slackにリマインダーを設定

1. CURのレポート出力の設定

AWSマネジメントコンソールの 請求とコスト管理 > データエクスポート から作成します。

今回は、

  • CUR 2.0
  • Parquet形式

を選択して作成しました。

2. Athenaのテーブル作成

レポートに出力されるカラムとデータ型はAWSのドキュメントに記載されていますので、ここを参考にテーブルを作成します。
https://docs.aws.amazon.com/ja_jp/cur/latest/userguide/table-dictionary-cur2.html

テーブル作成
CREATE EXTERNAL TABLE `cur` (
	`bill_bill_type` string,
	`bill_billing_entity` string,
	`bill_billing_period_end_date` timestamp,
	`bill_billing_period_start_date` timestamp,
	`bill_invoice_id` string,
	`bill_invoicing_entity` string,
	`bill_payer_account_id` string,
	`bill_payer_account_name` string,
	`cost_category` map < string, string >,
	`discount` map < string, string >,
	`discount_bundled_discount` double,
	`discount_total_discount` double,
	`identity_line_item_id` string,
	`identity_time_interval` string,
	`line_item_availability_zone` string,
	`line_item_blended_cost` double,
	`line_item_blended_rate` string,
	`line_item_currency_code` string,
	`line_item_legal_entity` string,
	`line_item_line_item_description` string,
	`line_item_line_item_type` string,
	`line_item_net_unblended_cost` double,
	`line_item_net_unblended_rate` string,
	`line_item_normalization_factor` double,
	`line_item_normalized_usage_amount` double,
	`line_item_operation` string,
	`line_item_product_code` string,
	`line_item_resource_id` string,
	`line_item_tax_type` string,
	`line_item_unblended_cost` double,
	`line_item_unblended_rate` string,
	`line_item_usage_account_id` string,
	`line_item_usage_account_name` string,
	`line_item_usage_amount` double,
	`line_item_usage_end_date` timestamp,
	`line_item_usage_start_date` timestamp,
	`line_item_usage_type` string,
	`pricing_currency` string,
	`pricing_lease_contract_length` string,
	`pricing_offering_class` string,
	`pricing_public_on_demand_cost` double,
	`pricing_public_on_demand_rate` string,
	`pricing_purchase_option` string,
	`pricing_rate_code` string,
	`pricing_rate_id` string,
	`pricing_term` string,
	`pricing_unit` string,
	`product` map < string, string >,
	`product_comment` string,
	`product_fee_code` string,
	`product_fee_description` string,
	`product_from_location` string,
	`product_from_location_type` string,
	`product_from_region_code` string,
	`product_instance_family` string,
	`product_instance_type` string,
	`product_instancesku` string,
	`product_location` string,
	`product_location_type` string,
	`product_operation` string,
	`product_pricing_unit` string,
	`product_product_family` string,
	`product_region_code` string,
	`product_servicecode` string,
	`product_sku` string,
	`product_to_location` string,
	`product_to_location_type` string,
	`product_to_region_code` string,
	`product_usagetype` string,
	`reservation_amortized_upfront_cost_for_usage` double,
	`reservation_amortized_upfront_fee_for_billing_period` double,
	`reservation_availability_zone` string,
	`reservation_effective_cost` double,
	`reservation_end_time` string,
	`reservation_modification_status` string,
	`reservation_net_amortized_upfront_cost_for_usage` double,
	`reservation_net_amortized_upfront_fee_for_billing_period` double,
	`reservation_net_effective_cost` double,
	`reservation_net_recurring_fee_for_usage` double,
	`reservation_net_unused_amortized_upfront_fee_for_billing_period` double,
	`reservation_net_unused_recurring_fee` double,
	`reservation_net_upfront_value` double,
	`reservation_normalized_units_per_reservation` string,
	`reservation_number_of_reservations` string,
	`reservation_recurring_fee_for_usage` double,
	`reservation_reservation_a_r_n` string,
	`reservation_start_time` string,
	`reservation_subscription_id` string,
	`reservation_total_reserved_normalized_units` string,
	`reservation_total_reserved_units` string,
	`reservation_units_per_reservation` string,
	`reservation_unused_amortized_upfront_fee_for_billing_period` double,
	`reservation_unused_normalized_unit_quantity` double,
	`reservation_unused_quantity` double,
	`reservation_unused_recurring_fee` double,
	`reservation_upfront_value` double,
	`resource_tags` map < string, string >,
	`savings_plan_amortized_upfront_commitment_for_billing_period` double,
	`savings_plan_end_time` string,
	`savings_plan_instance_type_family` string,
	`savings_plan_net_amortized_upfront_commitment_for_billing_period` string,
	`savings_plan_net_recurring_commitment_for_billing_period` string,
	`savings_plan_net_savings_plan_effective_cost` double,
	`savings_plan_offering_type` string,
	`savings_plan_payment_option` string,
	`savings_plan_purchase_term` string,
	`savings_plan_recurring_commitment_for_billing_period` double,
	`savings_plan_region` string,
	`savings_plan_savings_plan_a_r_n` string,
	`savings_plan_savings_plan_effective_cost` double,
	`savings_plan_savings_plan_rate` double,
	`savings_plan_start_time` string,
	`savings_plan_total_commitment_to_date` double,
	`savings_plan_used_commitment` double,
	`split_line_item_actual_usage` double,
	`split_line_item_net_split_cost` double,
	`split_line_item_net_unused_cost` double,
	`split_line_item_parent_resource_id` string,
	`split_line_item_public_on_demand_split_cost` double,
	`split_line_item_public_on_demand_unused_cost` double,
	`split_line_item_reserved_usage` double,
	`split_line_item_split_cost` double,
	`split_line_item_split_usage` double,
	`split_line_item_split_usage_ratio` string,
	`split_line_item_unused_cost` double
)
PARTITIONED BY (`billing_period` string)
ROW FORMAT SERDE 'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe'
WITH SERDEPROPERTIES ('serialization.format' = '1')
STORED AS PARQUET
LOCATION 's3://xxx/data'
TBLPROPERTIES (
	'projection.enabled' = 'true',
	'projection.billing_period.format' = 'yyyy-MM',
	'projection.billing_period.interval' = '1',
	'projection.billing_period.interval.unit' = 'MONTHS',
	'projection.billing_period.range' = '2024-01,NOW',
	'projection.billing_period.type' = 'date',
	'storage.location.template' = 's3://xxx/data/BILLING_PERIOD=${billing_period}/'
)

3. Redashでダッシュボード作成

RedashからAthenaのクエリが実行できることを確認したら、クエリやダッシュボードを作成します。

表示項目は模索中ですが、レンティオでは下記の項目を表示しています。

  • 先月との比較
  • 日毎の使用タイプごとの料金
  • 3ヵ月分のサービスごとの料金

4. Slackにリマインダーを設定

ダッシュボードのURLを投稿するリマインダーを設定します。

Redash Slack botについてはこちら
https://zenn.dev/rentio/articles/redash-bot-serverless

Redashで各クエリのRefresh Schedule をリマインダーよりも前の時間に設定しておくことで、毎日最新のキャプチャがSlackに投稿されます。

https://redash.io/help/user-guide/querying/scheduling-a-query/

おわりに

以上、コスト可視化の取り組みについての紹介でした。

この記事が参考になれば幸いです。

採用情報

レンティオでは絶賛、エンジニアを募集しています!
https://recruit.rentio.co.jp/engineer

Discussion