不要なRDSインスタンスはなるべく止めて節約する

こんばんは! AWSでWebアプリを運用していると毎月のRDSの費用も決して安くないことを思い知ります。 しかし、開発環境用に使っているRDSインスタンスなら、基本的に開発者が起きている時間帯だけ動けば良いので、夜間や休日に稼働させる必要はありませんよね。

というわけで不要な時間帯になったらRDSにはお休みして頂こうと思います。

f:id:tansantktk:20201116200116p:plain

何がしたいのか

  • RDSインスタンスの時間毎にかかる料金を削減する
  • 開発環境用のRDSインスタンスを特定の時刻になったら、停止/起動させるようにする
    • CloudWatchで平日の8:00になったら、RDSインスタンスを起動するLambdaを実行させる
    • CloudWatchで平日の21:00になったら、RDSインスタンスを停止するLambdaを実行させる

RDSインスタンスを停止させるLambda

まずはRDSインスタンスを停止させるLambdaを作成します。 今回はサクサク書けるPython 3.8でLambdaを実装していきます。

f:id:tansantktk:20201116191247p:plain

  • 関数名は任意の名前を付けてください。
  • 実行ロールは特に問題が無ければ基本的なLambdaアクセス権限で新しいロールを作成を指定します。

Lambdaが作成できたら、実行ロールにrds:StopDBInstanceを許可するポリシーを追加します。これが無いとLambdaからRDSを停止できません。 下記は追加するポリシーのサンプルです。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "rds:StopDBInstance",
            "Resource": "*"
        }
    ]
}

あとはザクザクとソースコードを書きます。

lambda_function.py

import json
import boto3

def lambda_handler(event, context):
    rds = boto3.client('rds')
    # ***************にRDSインスタンスのDB識別子を指定
    result = rds.stop_db_instance(DBInstanceIdentifier = '***************')
    print(result)
    
    return True

RDSインスタンスを起動させるLambda

先ほどと同様にLambdaを作成した後、今度は実行ロールにrds:StartDBInstanceを許可するポリシーを追加します。これが無いとLambdaからRDSを開始できません。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "rds:StartDBInstance",
            "Resource": "*"
        }
    ]
}

ポリシーを追加したら、Lambdaのソースコードを書いていきます。 呼び出すメソッド以外は停止させるLambdaと同じですね。

lambda_function.py

import json
import boto3

def lambda_handler(event, context):
    rds = boto3.client('rds')
    # ***************にRDSインスタンスのDB識別子を指定
    result = rds.start_db_instance(DBInstanceIdentifier = '***************')
    print(result)
    
    return True

CloudWatchからLambdaを呼び出す

CloudWatchEventsは、Cron式を設定して特定の日付・曜日・時刻等の条件を満たした場合にAWSの各サービスを実行させることができます。 自分の場合、AWS環境でバッチ処理をさせたい場合なんかに使ったりしています。

では、CloudWatchの「ルール」画面から「ルールの作成」ボタンを押して、新しいルールを作成します。 下記は設定値の例です。 Cron式は開発者の皆さんの勤務実態に合わせて設定して下さい。

月~金曜日の21:00(JST)になったらRDSを停止させるルール f:id:tansantktk:20201116192341p:plain

月~金曜日の8:00(JST)になったらRDSを開始させるルール f:id:tansantktk:20201116192509p:plain

注意する点は、Cron式はGMT(グリニッジ標準時)で指定する必要があるということです。 本来設定したいJST(日本標準時)の時刻から9時間マイナスした時刻で設定する必要があります。 JSTからGMTに変換する過程で日付を跨いだ場合は、Cron式の曜日指定も1日分ズラす必要がありますので気を付けましょう。

設定に間違いがなければ、指定した時刻にRDSインスタンスが停止/開始するはずです。 開発用に使っているRDSインスタンスが多い程、コスト削減の効果は大きくなるでしょう。

余談

  • テストでLambdaを連続実行したところ、RDSの開始に失敗する時があった。
    • よくAWSコンソールを見てみるとRDSのステータスには停止中停止の2種類がある。
    • 停止中の時にRDSを開始するLambdaを実行してもエラーになる。停止処理を実行した後、開始処理が受付可能になるまでインターバルがある模様。