Aurora Serverlessの作成後に自動的に最小APUを最小化(0.5)するようにしてみた

2024.03.22

初めに

先日Aurora Serverlessを検証用に立ち上げた時に停止忘れかつ最小APUが8と大きい指定になってしまっていたため想定以上に大きな料金が発生してしまいました。

マネジメントコンソールでAurora Serverlessを立ち上げる際、初期の最小APUの値が直前まで選択していたインスタンスタイプに依存するようでAurora MySQLの場合現時点でデフォルトはdb.r6g.2xlargeが指定されている関係でこれをServerless v2に切り替えると最小APUが8で指定されます。

一旦バースト可能クラス(db.t3.medium)にしてから変更するとそのvCPU数に合わせて最小APUは2となります。

あまりServerlessで立ち上げることがなくインスタンスと違いAPUはただの数字のため意識の外にあったことが原因でした。

デイリーの停止も一つですがデフォルト値で8とそれなりに大きく今後同じことが起き得る可能性は考えられ、費用としても最小0.5の16倍と考えると1日でもそれなりの料金が発生してしまうため検知し強制的に最小APUが常に0.5に設定されるようにしてみます。

ピッタリなイベントが見つからない

この実装にあたりクラスタの作成完了等単一のイベントをトリガーにすることですぐに実装完了できるものと見込んでおりましたが、意外と適当なイベントが見つからないものとなりました。

Auroraクラスタの設定を変更するためには配下のインスタンス全体を含み全て作成完了している必要がありますが、クラスタの作成完了はあくまでクラスタ自体の作成完了となりこちらのイベントが発生してもは以下のインスタンスの作成は完了していない可能性があります。

一応マネジメントコンソール上クラスタが作成完了していると変更可能そうに見えますが、実際に適用してみると配下のインスタンスが作成中のため変更できない旨が表示されエラーとなります。

そのため個別で通知されるAuroraクラスタの作成完了(RDS-EVENT-0170)と配下のインスタンス完了(RDS-EVENT-0005)を全て待機した後に変更処理を起動させる必要があります。

クラスタ自体の作成完了イベントの例としては以下、

{
  "version": "0",
  "id": "xxxx",
  "detail-type": "RDS DB Cluster Event",
  "source": "aws.rds",
  "account": "xxxxx",
  "time": "2024-01-23T07:19:58Z",
  "region": "ap-northeast-1",
  "resources": [
    "arn:aws:rds:ap-northeast-1:xxxxx:cluster:database-1"
  ],
  "detail": {
    "EventCategories": [
      "creation"
    ],
    "SourceType": "CLUSTER",
    "SourceArn": "arn:aws:rds:ap-northeast-1:xxxxx:cluster:database-1",
    "Date": "2024-01-23T07:19:58.451Z",
    "Message": "DB cluster created",
    "SourceIdentifier": "database-1",
    "EventID": "RDS-EVENT-0170"
  }
}

配下のインスタンスの作成完了イベント例としては以下となります。

{
  "version": "0",
  "id": "xxxxx",
  "detail-type": "RDS DB Instance Event",
  "source": "aws.rds",
  "account": "xxxxx",
  "time": "2024-01-23T08:59:04Z",
  "region": "ap-northeast-1",
  "resources": [
    "arn:aws:rds:ap-northeast-1:xxxxx:db:database-1-instance-1"
  ],
  "detail": {
    "EventCategories": [
      "creation"
    ],
    "SourceType": "DB_INSTANCE",
    "SourceArn": "arn:aws:rds:ap-northeast-1:562773936175:db:database-1-instance-1",
    "Date": "2024-01-23T08:59:04.914Z",
    "Message": "DB instance created",
    "SourceIdentifier": "database-1-instance-1",
    "EventID": "RDS-EVENT-0005"
  }
}

イベントの情報自体にはクラスタとインスタンスを紐づける情報自体は保持しているわけではないので何らかの方法でこれら全て完了を待ち、全て完了後にクラスタの変更を行います。

選択肢となる方式

以下の2つの方式を考えましたが、最終的には前者を採用しております。

インスタンスの状態を定期的にチェックする

Step Functionsの料金形態として稼働時間ではなく状態遷移数に対して課金が発生するため待機を長くしただけ料金がかかるというものではなく同じ1ステップであれば料金は同一です。

そのため素直に特定の状態になるようにループしてチェックさせることで実現させます。

構造として1ステートマシンでシンプルかつ関連リソースも最小限で実装できそうなのがメリットとなりそうです。
デメリットとしては待機時間を短くすればその分料金がかかり、長くすればその分スケールダウンまでのラグが生じるという点ですがここについてはここについては恒常的に大量に作成したり消したりを恒常的に繰り返さなければ問題ないかと考えております。

イベント駆動で発火し完了状態をチェックする

もう一つはクラスタ作成開始時に関連するクラスタやインスタンス情報を何らかのデータストア(ex.DynamoDB)に格納し、完了時にそれらを更新した上でクラスタ配下のものがすべて完了していればスケールダウン処理を呼び出す方法です。

こちらのメリットとしては無駄なWaitを挟まず即時性に優れるところですが、前述の通りクラスタと紐づけるような情報はないためAurora以外のインスタンスの起動でも発火してしまうため巻き込み範囲もより広く、状態を保持するために別のリソースを増やすことあまりしたくなかったためこちらは不採用とし定期的なチェックの方針としました。
(できる限り早くスケールダウンさせたいのですが1秒を争うというほどではない場合がほとんどだと考えているのもあり)

テンプレート

SAMで実装したコードを以下に格納しています。

全体像

ステートマシン自体は以下の作りとなっております。こちらをRDSのクラスタ作成完了イベントで起動します(RDS-EVENT-0170)。

クラスタ作成完了イベント自体にはServerlessかどうかの判別がなく全て拾ってしまうため最初の段階でスケールダウンする必要があるServerlessクラスタかどうかを含め合わせてチェックしています。

チェックについては今回availableのみをチェックしていますが何らか事情でこれ以外のステータスを示す場合無限にループをし続ける危険性があるため念の為ステートマシン自体のタイムアウトを仕込んでおき動作が長い場合は強制的に止まるようにしています。

実行

Aurora Serverlessを最小APU 1.0で作成します。

そうすると先ほどのステートマシンが稼働しチェックを開始し全てが完了するまでチェックループをし、完了後に変更します。
スナップショットを使わない新規作成で3ループ30ステップほどでした。

対象のクラスタを確認してみると最小キャパシティが変更されていることが確認できます。
ただ変更適用タイミング=ModifyDBClusterの実行完了ではなく完了後に変更中になるため実際の適用完了とステートマシン完了タイミングが一致しない点には注意しましょう。

終わりに

安全防止策としてAurora Serverlessの自動スケールダウン機構を作成してみました。Step Funtionsの特徴である稼働時間ではなく状態遷移数による課金という特徴を活かせるような利用例ではないでしょうか。

とはいえ大体チェックできていない場合その日だけではなく翌日も抜けている場合は多いかと思いますのでデイリーでの停止を組み込んでおくとより余分なコストを発生を抑えることができますので併用するのがよさそうではあります。

なお現在のコードでは全てのAuroraクラスタの作成をフックしてしまいますが、変更完了はトリガーとしないためもし0.5以外を指定する必要があれば都度作成完了後に変更する形になります。
個人的にはそこまで必要となる機会はないので困りませんがもし必要であれば命名等から条件分岐を調整して試してみてください。