RDS の RI を AWS CLI でまとめて買う Excel を作成した話

記事タイトルとURLをコピーする

マネージドサービス部 佐竹です。
オペレーションミスを回避する目的のため、AWS CLI で RDS の RI を「稟議の通りにまとめて買える」ように Excel を使ったという、現場よりのお話を書いてみました。

はじめに

私はサーバーワークス社内のコスト最適化のために、定期的に Reserved Instance(RI)を購入するオペレーションを担っています。

その時に、ざっくりと以下のフローを実行します。

  1. Reserved Instance の推奨を確認する
  2. どれを買うべきか判断して対象を一覧にする
  3. 購入に必要な金額を計算する
  4. 稟議を出して承認を得る
  5. 対象とした一覧の通りに RI を購入する
  6. ダブルチェックする

このうち「対象とした一覧の通りに RI を購入する」「ダブルチェックする」をこれまで人手でやっていました。

ようは、マネジメントコンソールから1つずつポチポチと買っていたのです。これは「年に何回かしかやらないのでこれでいいか」と思って何年も運用してきたのですが・・・年の瀬の今日この頃、さすがに面倒に思ってきてしまいました。

そこで「AWS CLI を使って稟議に使った Excel の一覧通りに RI を購入できるようにした」という話を以下に記していきます。

RDS の RI を AWS CLI でまとめて買うまで

Reserved Instance の推奨を確認する

「Billing and Cost Management > Reservations > Recommendations」から、Relational Database Service (RDS) の RI の推奨をダウンロードします。

弊社では基本的に1年の RI を運用しているため、以下のパラメータを使っています。

  • Term: 1-year
  • Based on the past: 7 days
  • Recommendation level Info: Payer
  • Payment option Info: All upfront

画面右下の「Download CSV」から結果をダウンロードします。手元に「rds-ri-recommendations.csv」がダウンロードされたら、これを Excel で開き確認します。

どれを買うべきか判断して対象を一覧にする

CSV のままだと見にくいので、ピボットテーブルを作成します。

「挿入」→「おすすめのピボットテーブル」でまずは適当にピボットテーブルを作成します。その後、以下の通り設定します。

  • フィルター: Region
  • 行: Instance Type
  • 列: Database engine→Database edition→License→Availability zone
  • 値: Number of instance to purchase

この後さらに以下の対応をします。

  • 列にある各「集計」の項目が邪魔なので集計の列を右クリックのメニューから「小計を表示しない」で消します
  • Region のフィルターを「東京リージョン」のみにします(※東京リージョンしか購入しないオペレーションのため)

この他に「Expected utilization」をフィルターに追加し、RI の適用率がよくないものを除外する場合もあります。

さて、これでかなり閲覧がしやすくなりました。

あとはこの中から対象を選択して、購入するものを決めていきます。

購入に必要な金額を計算する

先ほどの一覧から必要なものを手で抜き出して、上記のような一覧を別途作成します。今回は上画像の通り、一部に絞っています。

このあと AWS CLI のために必要なものが4つあり、それが「ProductDescription≒DB Engine」「Instance Class」「Multi-AZかどうか」「何台分を買うか」の4つです。

これらが決まったら検索用の列を設けます。

シンプルな関数にしたいため、今回は「CONCAT」を用いて台数以外の3つのパラメータを合体させて、一意になる ID を生成します。関数は =CONCAT([@ProductDescription],[@Class],[@[Multi-AZ]]) です。

ここまで準備ができたら次に AWS CLI で ReservedDBInstancesOfferingId を取得します。

少し手間なのですが、RI や SP (Savings Plans) というのは「組み合わせごとに一意の Offering-ID が割り振られて」おり、その ID を指定しての購入作業になります。

というわけで ReservedDBInstancesOfferingId を取得する CLI である「describe-reserved-db-instances-offerings」を実行します。

この時点で多くの方が「ゲンナリ」してしまうかもしれませんが、やってみると意外にも大したことは無いので是非試してみてください。

さてこのコマンドの使い方ですが、デフォルトのままだと扱いにくいです。describe-reserved-db-instances-offerings は、そのままキックすると返り値が莫大な上に、JSON で出されても Excel で使うには困ります。

試しに1つ CloudShell から実行してみてみましょう。

[cloudshell-user@ip-10-136-40-195 ~]$ aws rds describe-reserved-db-instances-offerings --region ap-northeast-1 --offering-type "All Upfront"  --duration 31536000
{
    "ReservedDBInstancesOfferings": [
        {
            "ReservedDBInstancesOfferingId": "003afc12-cfe8-4a06-8fb5-14866498a43c",
            "DBInstanceClass": "db.x2iedn.8xlarge",
            "Duration": 31536000,
            "FixedPrice": 200459.635,
            "UsagePrice": 0.0,
            "CurrencyCode": "USD",
            "ProductDescription": "sqlserver-se(li)",
            "OfferingType": "All Upfront",
            "MultiAZ": false,
            "RecurringCharges": [
                {
                    "RecurringChargeAmount": 0.0,
                    "RecurringChargeFrequency": "Hourly"
                }
            ]
        },
        {
            "ReservedDBInstancesOfferingId": "007b1c62-4bca-42bc-b6cf-e49bde23e134",
            "DBInstanceClass": "db.m5.2xlarge",
            "Duration": 31536000,
            "FixedPrice": 10652.0,
            "UsagePrice": 0.0,
            "CurrencyCode": "USD",
            "ProductDescription": "mariadb",
            "OfferingType": "All Upfront",
            "MultiAZ": true,
            "RecurringCharges": [
                {
                    "RecurringChargeAmount": 0.0,
                    "RecurringChargeFrequency": "Hourly"
                }
            ]
        },
        {
            "ReservedDBInstancesOfferingId": "0087b22d-e744-4ede-bf2e-f0c4521cd0fb",
            "DBInstanceClass": "db.r5d.large",
            "Duration": 31536000,
            "FixedPrice": 2196.0,
            "UsagePrice": 0.0,
            "CurrencyCode": "USD",
            "ProductDescription": "postgresql",
            "OfferingType": "All Upfront",
            "MultiAZ": false,
            "RecurringCharges": [
                {
                    "RecurringChargeAmount": 0.0,
                    "RecurringChargeFrequency": "Hourly"
                }
            ]
        },
...

引数で、指定している「--duration 31536000」は1年の意味であり、「--offering-type "All Upfront"」というのは全額前払いを示しています*1。これによって数は減っているのですがそれでもなお多いと感じます。

ということで結論として、以下のコマンドを用いて一旦テキストに出力してしまいます。

aws rds describe-reserved-db-instances-offerings --region ap-northeast-1 --offering-type "All Upfront"  --duration 31536000 --output text > AllUpfront-1year-RDS-RI-Offering-ID-List

AllUpfront-1year-RDS-RI-Offering-ID-List というファイルに結果がテキスト形式で出力されます。JSON の階層構造上、失われる値が存在しますが今回これらは不要なため問題ありません。

ローカルに本ファイルをダウンロードします。

/home/cloudshell-user/AllUpfront-1year-RDS-RI-Offering-ID-List をダウンロードしたら、これをテキストエディタで開きます。

サクラエディタで開いた状態が上画像です。不要な行があるので、正規表現を使って全部消します。

^RECURRINGCHARGES 0.0 Hourly\n を全て消します。

1306件置換されました。この後、このテキストをそのまま Excel の新しいシートに貼り付けます。

貼り付けると、上画像の通りになります。ヘッダーがないため分かり難いですが、A,B列を削除して以下の通りヘッダーを付与すると良いでしょう。

DBInstanceClass  Duration    FixedPrice  MultiAZ OfferingType    ProductDescription  ReservedDBInstancesOfferingId   UsagePrice

これを利用し、先ほど CONCAT したものと同じ値を検索用にこちらのシートでも生成します。この後 VLOOKUP を使うため、検索がそれより右にしか行けない特性を鑑みてF列とG列の間に新規列を追加し、ここに検索用の値を作成します。

そこには先程と同じように =CONCAT([@ProductDescription],[@DBInstanceClass],[@MultiAZ]) として検索用の値を作成します。

これで、この列を使って VLOOKUP が可能となりました。

さらに、FixedPrice も使う必要があるため、I列を C列の値で上書きしておきます。

この後、先程作成した、購入したいリストの右端に ReservedDBInstancesOfferingId を設け、ここで =VLOOKUP([@検索用],テーブル2[[検索用]:[FixedPrice2]],2,FALSE) などと記載して先ほどの一覧から Offering-ID を得ます。

さらに、=VLOOKUP([@検索用],テーブル2[[検索用]:[FixedPrice2]],3,FALSE) として全額前払いの費用も取得します。

ただこのままだと、Price に購入台数が反映されていないため、台数を掛け算した列を用意します。

「前払い費用」としたものが台数を加味したもので、それを全て足し合わせた金額を「オートSUM」で作成しました。今回は「$24,186」が稟議の費用となります。

ただし、USD のままでは稟議に利用できないため、ドル円レートを掛け算した値も添えておくことになります。

稟議を出して承認を得る

これまで作成した資料を用いて、購入用稟議を起票し承認を得ます。これは各社フローが異なるでしょう。

稟議については特筆する点がないため、今回は割愛致します。

対象とした一覧の通りに RI を購入する

本題です。稟議が承認されたら、作成した Excel の一覧の通りに RI を AWS CLI を用いて購入します。

ReservedDBInstancesOfferingId が生成できているため、あとはこれを引数にした AWS CLI コマンド 「purchase-reserved-db-instances-offering」を作成するだけです。

と言いたいところですが、 purchase-reserved-db-instances-offering には引数として--reserved-db-instance-id が指定可能です*2。せっかくなのでこれを関数で自動生成します。

本 ID ですが、結論として ="ri-"&TEXT(NOW(),"YYYY-MM-DD-hh-mm-ss")&"-"&TEXT( ROW()-1, "00") を関数としました。ここは正直わかれば何でもいいのでお好みの関数を作ってみてください。

後は Excel 上で AWS CLI を生成するだけとなりました。

一番右端の列に AWS CLI 用の列を追加して以下を入力します。

="aws rds purchase-reserved-db-instances-offering --region ap-northeast-1 --db-instance-count "&F2&" --reserved-db-instances-offering-id "&H2&" --reserved-db-instance-id "&K2

そして上記関数を用いて自動生成された AWS CLI コマンドが以下の通りです。

aws rds purchase-reserved-db-instances-offering --region ap-northeast-1 --db-instance-count 3 --reserved-db-instances-offering-id 99e34434-b466-4aa7-a342-822859036369 --reserved-db-instance-id ri-2023-12-21-21-33-59-01
aws rds purchase-reserved-db-instances-offering --region ap-northeast-1 --db-instance-count 1 --reserved-db-instances-offering-id 1a070f19-9076-42ae-b307-850064a29667 --reserved-db-instance-id ri-2023-12-21-21-33-59-02
aws rds purchase-reserved-db-instances-offering --region ap-northeast-1 --db-instance-count 1 --reserved-db-instances-offering-id 98ca1f1b-63c4-45da-a7d0-53fc42bc59e7 --reserved-db-instance-id ri-2023-12-21-21-33-59-03
aws rds purchase-reserved-db-instances-offering --region ap-northeast-1 --db-instance-count 4 --reserved-db-instances-offering-id 4b528f1c-df3c-42e2-bd54-bd145b1caea5 --reserved-db-instance-id ri-2023-12-21-21-33-59-04
aws rds purchase-reserved-db-instances-offering --region ap-northeast-1 --db-instance-count 2 --reserved-db-instances-offering-id 8c93e727-93e3-4b43-ab8b-eb7eb67366cd --reserved-db-instance-id ri-2023-12-21-21-33-59-05
aws rds purchase-reserved-db-instances-offering --region ap-northeast-1 --db-instance-count 1 --reserved-db-instances-offering-id 567d710b-eb0a-48da-9a7f-21ccbb1a468d --reserved-db-instance-id ri-2023-12-21-21-33-59-06
aws rds purchase-reserved-db-instances-offering --region ap-northeast-1 --db-instance-count 2 --reserved-db-instances-offering-id 4fc6fd1f-c412-4ed8-a034-54d6b3691e5d --reserved-db-instance-id ri-2023-12-21-21-33-59-07
aws rds purchase-reserved-db-instances-offering --region ap-northeast-1 --db-instance-count 1 --reserved-db-instances-offering-id 8890fb72-3901-416d-95f3-79499dc26b43 --reserved-db-instance-id ri-2023-12-21-21-33-59-08
aws rds purchase-reserved-db-instances-offering --region ap-northeast-1 --db-instance-count 1 --reserved-db-instances-offering-id 06dbc4ad-9892-496f-9f72-f80ae32329e0 --reserved-db-instance-id ri-2023-12-21-21-33-59-09

これを CloudShell から実行すれば購入は完了です。

RI は購入する AWS アカウントが運用上決まっている場合もありますので、購入作業を行う AWS アカウントを間違えないように注意して実行してください。

なお、上記コマンドは実行すると普通に RI が購入できてしまうので、試しに実行するなどはなさらないようにお願いします。

コマンドの検証をされたい場合は台数 (--db-instance-count) を 0 とするとよいでしょう。

ダブルチェックする

証跡として、上記 AWS CLI コマンドを保存しておきます。

さらに購入後 AWS マネジメントコンソールにおいて RDS RI の一覧画面から結果を取得します。

RDS RI の一覧画面は簡単に Excel にコピペができるためそれを証跡として保存すると楽だと考えています。

上画像は、Excel にコピペした後に整形して見やすくした一覧です。後はこれを事前に作成した Excel のリストと見比べればダブルチェックとなります。

以上が、AWS CLI を用いた RDS の RI をまとめて購入する方法でした。

余談

プログラミング言語や、もっと凝ったコマンドを実行することでさらに楽に作業ができるかもしれません。

これを読んだ方がもっと効率的に作業ができる方法があればそれを実施して頂いて問題はありませんが、私は必要以上に自動化しないようにしています。

というのも、「何をやっているのかわからない」ようにしたくないためです。

シンプルな作業は自動化すべきと思いますが、RI の購入は意外にも複雑な処理だと感じます。どの程度購入するかという購入数や、支払いオプション(前払いするかどうか)なども会社の状況によって変化します。

このため(私の感覚ではありますが)可能な限りシンプルな関数で生成するようにしてみました。

というわけで、RI 購入の現場からは以上です。

本ブログが何かの参考になれば幸いです。

*1:本引数はユーザによって異なるため、適切な値に変更してください

*2:指定しない場合は AWS 側が購入日時を自動で補完する

佐竹 陽一 (Yoichi Satake) エンジニアブログの記事一覧はコチラ

マネージドサービス部所属。AWS資格全冠。2010年1月からAWSを利用してきています。2021-2022 AWS Ambassadors/2023 Japan AWS Top Engineers/2020-2023 All Certifications Engineers。AWSのコスト削減、最適化を得意としています。