SP/RI のコスト配分が学べる「Handling Savings Plan (SP) and Reserved Instance (RI) Chargeback, Showback, and Cost Allocation」ワークショップをやってみた
コスト管理を行う上でのテーマの一つに「コスト配分」があります。
例えば 5つの AWS アカウントが所属する組織で AWS サポート費用が $100 だとした場合に、費用負担元が異なる場合はその費用を誰が負担するのかは課題となります。均等に 5分割($20)とするのか、サポート利用頻度に応じて分割して再配分するのかといった対応が必要になります。
もう少し身近な例だと職場の人たちで、食事に行った時の割り勘に近いイメージかなと思います。
- 懇親会の費用として負担
- 参加人数で割り算して負担
- 大量に食べる人やお酒を飲む人は多めの負担
- 偉い人が多めに負担
食事会であれば、そこまでシビアに管理する必要はないかもしれませんが、ビジネスの世界では予算があるため、適切に管理されることが求められます。
そのためコスト配分にはいくつかのモデルが存在します。AWS のコスト配分モデルの紹介としては下記の記事がとても参考になります。
記事の中では、8つのモデルが紹介されています。
- アカウント単位 (Account based)
- タグ単位 (Tag based)
- AWSコストカテゴリ単位 (AWS Cost Categories)
- 割引前の比率 (Non-Discounted Proportional)
- 使用量の単位 (Consumption based)
- テレメトリ単位 (Telemetry based)
- 固定配分 (Fixed Allocation)
- 均等配分 (Equal Allocation)
企業におけるルールと8つのモデルからコスト配分を実践していくわけですが、その中でも一際悩ましいのが SP/RI の配分です。SP/RI には複数の支払いオプションや AWS Organizations を利用した割引共有の仕組みがあり、通常の費用に加えて検討が必要な特徴があります。
前置きが長くなりましたが、、そんな SP/RI でのコスト配分の実践に向けた一助になるワークショップが提供されているため、今回やってみました。
ワークショップをやってみた
まとめ
- 組織で集中的に SP/RI を購入している担当者の方は必見なワークショップ
- レガシー CUR と CUR 2.0 で、EC2 以外にも RDS, ElastiCache, OpenSearch Service, Redshift, MemoryDB 向けのサンプルクエリが紹介
- 使用量の単位 (Consumption based), 割引前の比率 (Non-Discounted Proportional) ,均等配分 (Equal Allocation) のコスト配分モデルが学べる
SP/RI におけるコスト配分の課題
ワークショップの冒頭でも言及されていますが SP/RI のコスト配分には2つの大きな留意点があります。
Savings Plans (SPs) and Reserved Instances (RIs) Application and Billing
- 支払いオプション(全額前払い、一部前払い、前払いなし)
- 前払いは、購入月に一括で請求されます
- 非前払いは、契約期間での毎月定額で請求されます
この請求タイミングと金額への考慮が必要です。特に前払いの場合は単月で大きな支出となるため、期間内で償却する形を取りたいというケースが多いかと思います。
- 組織割引共有
- 割引共有は購入アカウントで利用されなかった割引が組織の他のアカウントに適用され、無駄なく活用する機能です
- SP/RI の購入戦略には、適用対象リソースがない管理アカウントや専用メンバーアカウント(サイドカーアカウント)で SP/RI を購入することで、組織全体に効率良く適用する戦略があります
この場合、購入と割引利用のアカウントが異なるため、購入アカウントに請求される前払い及び定期の費用について、利用アカウント所有者との費用負担の取り決めが必要となります。ここが SP/RI のコスト配分の部分になります。
冒頭の食事会の例であれば、このようなアプローチが想定されます。
- 購入した管理アカウントの所有者が費用を負担する
- 全てのアカウント数で割った費用を各アカウント所有者で負担する
- 割引メリットの利用状況に応じて負担する
まさに「言うは易し、行うは難し」で、理屈は何となくわかったが何もない状態からにコスト配分の方法を確立するには、骨が折れる課題です。
環境
ワークショップ内の前提要件から重要な設定を抜粋します。
- CUR(レガシーまたは 2.0)が出力済み
- 組織割引共有が有効
また、今回利用する環境の状況を整理しておきます。
AWS アカウント ID | SP ID | 支払いオプション | 期間 | 前払い料金 | 定期料金/時間 | EC2 稼働 |
---|---|---|---|---|---|---|
xxxx-xxxx-xx35 | xxxxxxxfb | No Upfront | 1年(25/05/01 -) | $0.00 | $0.00340 | なし |
xxxx-xxxx-xx30 | xxxxxxxdf | No Upfront | 1年(25/05/01 -) | $0.00 | $0.00340 | 1台 |
xxxx-xxxx-xx37 | xxxxxxxd7 | All Upfront | 1年(25/07/25 -) | $35.04 ($0.0040 / 時間) |
$0.00 | 3台 |
この環境下では、AWS アカウントID(xx35)が購入した SP は AWS アカウントID(xx37)が利用することになるため、この定期料金($0.0034 /時間)をコスト配分します。
※ 定期料金は月の時間(7月なら31日で744時間)に左右されます
- 6月(30日):730(h) * $0.0034 = $2.482 / month
- 7月(31日):744(h) * $0.0034 = $2.529 / month
Cost Explorer における 非ブレンドコスト(Unblended cost)と償却コスト(Amortized cost)
この後のワークショップの出力結果で重要となる Unblended cost と Amortized cost を整理しておきます。
Unblended cost は現金主義ベース、Amortized cost は発生主義ベースの表示となります。詳細は下記の記事をご確認ください。
私は初めて知ったのですが、ワークショップ内で Cost Explorer フィルターの詳細オプションを利用することで割引を購入したアカウントから利用したアカウントに切り替えて償却コストとして表示することが可能です。
(機械翻訳)
この方法は、コストエクスプローラーおよびコストと使用状況レポート(CUR)の「償却」コストビューと一致します。償却とは、初期費用をコミットメント期間全体に均等に配分する発生主義の会計手法です。償却ビューでは、特定の期間にリンクされたアカウント間でSP/RIコミットメントがどのように適用/消費されたかを確認できます。例えば、1年間のSP/RIを1,200ドル前払いで購入した場合、償却ビューではその期間*の毎月100ドルが表示されます。コミットメントが毎月消費されるにつれて、その100ドルがどこに(どのリンクされたアカウントに)適用されたかを確認できます。
- 非ブレンドコスト(Unblended cost)
実際に請求に即した購入アカウントごとに前払い金額と定期金額が表示されています。
- 償却コスト(Amortized cost)
そのまま、詳細オプションを Amortized costs へ変更します。ちょっと期待したものと違うので、項目を追加します。
良い結果となりました。8月において AWS アカウントID(xx37)は二つ分の SP 費用を利用していることがわかります。
- (xx37)で 利用した $5.51
- (xx35)で 購入した $2.529
- (xx37)で 購入した $2.976
この先のワークショップでは、非ブレンドコスト(Unblended cost)と償却コスト(Amortized cost)をベースにコスト配分を行います。
Allocation Based on Consumption(消費に基づく配分)
(機械翻訳)
この方法では、リンクアカウント1が組織の総支出の50%を占めているものの、SP/RIフリートに含まれないサービスを使用しているためにSP/RI節約効果全体の10%を消費している場合、リンクアカウント1はSP/RI節約効果全体の10%を受け取ります。この方法は高い精度を実現しますが、使用パターンの変化により共有SPまたはRIの消費量が月ごとに変動する場合、混乱を招く可能性があります
つまり割引のメリットを利用(消費)した分に応じて、配分を行う方法となります。
早速、サンプルクエリを利用して Athena からクエリを実行します。今回は「RIなしのレガシーCUR」向けのクエリを利用します。
読解のために、少し追加します。
- line_item_line_item_type を追加
- 列の順番を変更
- 関係のないサービス等を除外条件に追加
- EC2 もオンデマンド費用を除外条件に追加
SELECT
-- Dimensions Start: These are the dimensions considered during the reallocation: billing_period, usage_account_id, product_code, purchase_option, savings_plan_a_r_n
-- You can add more dimensions here, and should be reflected throughout the query. You will need to increment the GROUP BY for each dimension you add.
date_trunc('month', bill_billing_period_start_date) AS billing_period,
line_item_product_code,
product_product_name, --This column most closely matches with Cost Explorer Service titles
CASE
WHEN (savings_plan_savings_plan_a_r_n <> '') THEN 'SavingsPlan'
WHEN (line_item_usage_type LIKE '%Spot%') THEN 'Spot'
ELSE 'OnDemand' END AS purchase_option,
line_item_usage_account_id,
savings_plan_savings_plan_a_r_n,
line_item_line_item_type,
SUM(line_item_unblended_cost) AS unblended_cost, -- SP fees will show at the account that purchased them
SUM(CASE WHEN line_item_line_item_type = 'SavingsPlanCoveredUsage' THEN savings_plan_savings_plan_effective_cost
WHEN line_item_line_item_type = 'SavingsPlanRecurringFee' THEN (savings_plan_total_commitment_to_date - savings_plan_used_commitment)
WHEN line_item_line_item_type = 'SavingsPlanNegation' THEN 0
WHEN line_item_line_item_type = 'SavingsPlanUpfrontFee' THEN 0 ELSE line_item_unblended_cost END) AS amortized_cost --SP fees will show at the account that used it, any unused SP fees will stay with the account that bought it
FROM
athenacurcfn_cur1.cur1
WHERE
year = '2025' and month = '8'
and savings_plan_savings_plan_a_r_n != '' and (product_product_name = 'Amazon Elastic Compute Cloud' or product_product_name = 'Savings Plans for AWS Compute usage') and line_item_line_item_type != 'Tax'
-- and (product_product_name = 'Amazon Elastic Compute Cloud' or product_product_name = 'Savings Plans for AWS Compute usage') and line_item_line_item_type != 'Tax'
GROUP BY 1, 2, 3, 4, 5, 6, 7 -- For each dimension you add in, increment the GROUP BY by 1
ORDER BY 1
まずは Cost Explorer との結果を比較していきます。
- AWS アカウントID(xxx35)
- unblended_cost が 2.5296
- amortized_cost が 0
- AWS アカウントID(xxx30)
- unblended_cost が 2.5296(2.5296 + 4.0175 - 4.0175)
- amortized_cost が 2.5296
- AWS アカウントID(xxx30)
- unblended_cost が 2.5296(2.5296 + 4.0175 - 4.0175)
- amortized_cost が 0(4.0175 - 4.0175 + 3.8825 - 3.8825)
- unblended_cost が 5.5056(2.5296 + 0.1000 + 2.8760)
Cost Explorer での表示と一致しました。続いて、コスト配分を行います。今後は SP ID(savings_plan_savings_plan_a_r_n)に注目します。
- SP ID(xxfb)
- unblended_cost が AWS アカウントID(xx35)で 2.5296
- SavingsPlanRecurringFee が AWS アカウントID(xx35)で 2.5296
- SavingsPlanCoveredUsage が AWS アカウントID(xx37)で 4.0175
- SavingsPlanNegation が AWS アカウントID(xx37)で - 4.0175
- amortized_cost が AWS アカウントID(xx30)で 2.5296
- unblended_cost が AWS アカウントID(xx35)で 2.5296
- SP ID(xxdf)
- unblended_cost が AWS アカウントID(xx30)で 2.5296
- amortized_cost が AWS アカウントID(xx30)で 2.5296
- SP ID(xxd7)
- unblended_cost が AWS アカウントID(xx37)で 0
- amortized_cost が AWS アカウントID(xx37)で 2.9760
結果からコスト配分には amortized_cost を用いるため、下記となります。
- AWS アカウントID(xx30)に $2.5296
- AWS アカウントID(xx37)に 2.5296 + 2.9760 = $5.5056
Allocation Based on Spend ①(支出に基づく配分)
(機械翻訳)
支出に基づいて利益を配分するには、AWS 支出総額の割合に基づいて、組織内のリンクされたアカウントまたはビジネスユニット全体に Savings Plans (SP) と Reserved Instances (RI) フリートの合計節約額を分配する必要があります。例えば、リンクアカウント1が組織の総支出の50%を占めている場合、リンクアカウント1は組織が受け取るSP/RIのメリットの50%を受け取ります。この方法は、月間の消費変動を平滑化し、サービスミックスやSP/RIの消費量に関係なく、各グループまたは事業部門に節約メリットの比例配分を与えます。この戦略は計算と実装が簡便であるため、他の方法よりも粒度が低くなります
利用費(支出)に応じて、SP/RI 単体ではなく(ここでは EC2)サービス単位で利用費を配分する方法となります。
この方法では、下記のステップを踏みます。(3番目だけでも可能です)
- 支出に占める割合を計算
- コスト削減額を計算
- 上記1と2から節約額を再割り当てして利用費を算出
一つ実行していきます。(クエリは省略)
- 支出に占める割合を計算
8月分のみに限定して実行します。
percent_of_total は 全ての public_cost を合計した値(15.7954)で各 public_cost を割った値です。これで対象となるサービス(ここでは EC2)の支出の中で、占める割合を取得できました。
- 全体のコスト削減額を計算
こちらも8月分のみに限定して実行します。これで組織全体としての削減額が取得できました。
- 上記1と2から再配分(proportional_cost)
こちらも8月分のみに限定して実行します。
結果からコスト配分には proportional_cost を用いて、EC2 利用費を下記で割り当てます。AWS アカウントID(xx35)はEC2 を利用していないので割り当て先になりません。
- AWS アカウントID(xx30)に $3.0046
- AWS アカウントID(xx37)に 5.9083 + 2.9001 = $8.8084
Allocation Based on Spend ②(アカウント数に基づく配分)
リソースを利用したアカウント数に応じて、SP/RI 単体ではなく(ここでは EC2)サービス単位で利用費を配分する方法となります。
この方法では、下記のステップを踏みます。(3番目だけでも可能です)
- アカウント数を計算
- コスト削減額を計算
- 上記1と2から節約額を再割り当てして利用費を算出
今回は 3だけ実行します。
結果からコスト配分には proportional_split_cost を用いて、EC2 利用費を下記で割り当てます。
- AWS アカウントID(xx30)に $2.0263
- AWS アカウントID(xx37)に $9.7867
追加でやってみよう(リソースID別とタグ別)
CUR にはリソースID情報とコスト配分タグを付与することが可能です。ワークショップの内容ではないのですが、AWS アカウントをアカウントID(xx37)に限定して、消費に基づくコスト配分モデルでリソース別とタグ別での算出が可能かをやってみます。
現在、該当アカウントには同じインスタンスタイプのEC2が3つ稼働しており、SP が2つ適用されています。
(良いタグ付け状態ではありませんが...)
「CmBillingGroup」 が Sandbox02 とタグが未付与で2つのワークロードがあると仮定します。
- リソース別
line_item_resource_id をクエリに追加
SELECT
-- Dimensions Start: These are the dimensions considered during the reallocation: billing_period, usage_account_id, product_code, purchase_option, savings_plan_a_r_n
-- You can add more dimensions here, and should be reflected throughout the query. You will need to increment the GROUP BY for each dimension you add.
date_trunc('month', bill_billing_period_start_date) AS billing_period,
line_item_product_code,
product_product_name, --This column most closely matches with Cost Explorer Service titles
CASE
WHEN (savings_plan_savings_plan_a_r_n <> '') THEN 'SavingsPlan'
WHEN (line_item_usage_type LIKE '%Spot%') THEN 'Spot'
ELSE 'OnDemand' END AS purchase_option,
line_item_resource_id,
savings_plan_savings_plan_a_r_n,
line_item_line_item_type,
SUM(line_item_unblended_cost) AS unblended_cost, -- SP fees will show at the account that purchased them
SUM(CASE WHEN line_item_line_item_type = 'SavingsPlanCoveredUsage' THEN savings_plan_savings_plan_effective_cost
WHEN line_item_line_item_type = 'SavingsPlanRecurringFee' THEN (savings_plan_total_commitment_to_date - savings_plan_used_commitment)
WHEN line_item_line_item_type = 'SavingsPlanNegation' THEN 0
WHEN line_item_line_item_type = 'SavingsPlanUpfrontFee' THEN 0 ELSE line_item_unblended_cost END) AS amortized_cost --SP fees will show at the account that used it, any unused SP fees will stay with the account that bought it
FROM
athenacurcfn_cur1.cur1
WHERE
year = '2025' and month = '8'
and savings_plan_savings_plan_a_r_n != '' and (product_product_name = 'Amazon Elastic Compute Cloud' or product_product_name = 'Savings Plans for AWS Compute usage') and line_item_line_item_type != 'Tax'
GROUP BY 1, 2, 3, 4, 5, 6, 7 -- For each dimension you add in, increment the GROUP BY by 1
ORDER BY 1
- i-04763c668032fa2b8 が $0.3541
- i-0bfc92a3315f789c2 が $2.5124
- i-0f8ad0a9f2d87371d が $2.5387
- 合計:$5.5052(0.3541+2.5124+2.5387+0.1000)
アカウントID(xx37)が負担すべき費用を適切に配分できていそうですね。
- タグ別
resource_tags_user_cm_billing_group をクエリに追加
SELECT
-- Dimensions Start: These are the dimensions considered during the reallocation: billing_period, usage_account_id, product_code, purchase_option, savings_plan_a_r_n
-- You can add more dimensions here, and should be reflected throughout the query. You will need to increment the GROUP BY for each dimension you add.
date_trunc('month', bill_billing_period_start_date) AS billing_period,
line_item_product_code,
product_product_name, --This column most closely matches with Cost Explorer Service titles
CASE
WHEN (savings_plan_savings_plan_a_r_n <> '') THEN 'SavingsPlan'
WHEN (line_item_usage_type LIKE '%Spot%') THEN 'Spot'
ELSE 'OnDemand' END AS purchase_option,
resource_tags_user_cm_billing_group,
savings_plan_savings_plan_a_r_n,
line_item_line_item_type,
SUM(line_item_unblended_cost) AS unblended_cost, -- SP fees will show at the account that purchased them
SUM(CASE WHEN line_item_line_item_type = 'SavingsPlanCoveredUsage' THEN savings_plan_savings_plan_effective_cost
WHEN line_item_line_item_type = 'SavingsPlanRecurringFee' THEN (savings_plan_total_commitment_to_date - savings_plan_used_commitment)
WHEN line_item_line_item_type = 'SavingsPlanNegation' THEN 0
WHEN line_item_line_item_type = 'SavingsPlanUpfrontFee' THEN 0 ELSE line_item_unblended_cost END) AS amortized_cost --SP fees will show at the account that used it, any unused SP fees will stay with the account that bought it
FROM
athenacurcfn_cur1.cur1
WHERE
year = '2025' and month = '8'
and savings_plan_savings_plan_a_r_n != '' and (product_product_name = 'Amazon Elastic Compute Cloud' or product_product_name = 'Savings Plans for AWS Compute usage') and line_item_line_item_type != 'Tax'
GROUP BY 1, 2, 3, 4, 5, 6, 7 -- For each dimension you add in, increment the GROUP BY by 1
ORDER BY 1
- Sandbox02 が $2.8667
- タグ未付与 が $2.5387
- 合計:$5.5054(2.8667+2.5387+0.1)
タグ別も良さそうです。
さいごに
今回は、SP/RI のコスト配分が学べるワークショップをやってみました。
検証した環境ではリソースも少なく、ケースも限定的なため、実運用する際には改めて検証いただきたいですが、個人的にはとても良いワークショップではないかと思います。SP/RI のコスト配分は実現までの負担も高いため、精緻さとトレードオフに簡易なコスト配分モデルを採択するケースもありましたが、このワークショップで紹介されている内容とクエリをベースに始めることで負担が軽減できるかと思います。
SP/RI のコスト配分を検討されている方には、お薦めのワークショップです。