🌅

EventBridge Scheduler でRDSクラスター内のインスタンスを自動的に止める仕組みを導入する ※30%以上安くなる

2025/01/04に公開

結論

本記事では、営業時間外にRDSクラスター内のインスタンスを自動停止することで、RDSの利用料金を削減する方法を紹介します。

AWS料金見積もりツールの結果

AWS料金見積もりツールによる試算では、夜間(21:00〜8:00)や土日にRDSを停止する運用に切り替えた場合は、約32.20% のコスト削減が見込めるとのことです。

見積もりツール試算結果
月額料金:91.25 USD ➔ 61.87 USD
月額料金(日本円換算):約14,343円 ➔ 約9,717円

更に、祝日含むと更なるコスト削減を見込むことができます。

画像

URL:AWS料金見積もりツール

また、本記事は自身のスキルを証明する一つの材料として執筆しております。

構成図

画像

  • AWS
    • VPC
      • プライベートサブネット
        • RDS
      • プライベートサブネット
        • RDS
    • EventBridge Scheduler
    • IAMロール
      • EventBridge Schedulerにアタッチする

EventBridge Schedulerで実装している理由

EventBridge Schedulerで実装している理由については、EC2 や RDS の起動・停止をスケジュールする一番簡単な方法を参考にしております。この内容については下記のように説明されています。

以前から利用できる方法

料金を節約するために EC2 や RDS のインスタンスを自動的に止めたいというニーズはありふれているため、これを実現するための手法はさまざまにあります。そして、より簡単に設定できるよう AWS それ自体の機能も進化してきました。
プリミティブな方法を含めて、以前から利用可能だった手法には、例えば次のものがあります。

  • AWS 外のシステム上の cron から AWS CLI コマンドを実行する
  • 常時起動している EC2 インスタンス上の cron から AWS CLI コマンドを実行する
  • CloudWatch イベントから Lambda 関数を実行する
  • EventBridge ルールから Lambda 関数を実行する
  • EventBridge ルールから Systems Manager オートメーションを実行する

一番簡単な方法

現在では、AWS コンソールの設定だけで EC2 や RDS の停止と再開をスケジュールでき、もはや Lambda 関数を書く必要もなくなっています。
具体的には、Amazon EventBridge Scheduler を使用してスケジュールを作成します。

ソースコード

ソースコードは下記リンクにて公開しております。Terraformにて構築しております。

https://github.com/takakura0318/tech-blog-code/pull/1/files

下記にポイントなる箇所を一部抜粋します

eventbridge-scheduler_aurora_start_stop.tf
//////////////////////////////////////////////////
// EventBridgeScheduler
// Aurora クラスタを起動
//////////////////////////////////////////////////
resource "aws_scheduler_schedule" "aurora_start" {
  name       = "aurora-start"
  group_name = "default"
  flexible_time_window {
    // フレックスタイムウィンドウは使用しない
    mode = "OFF"
  }

  // 平日 (MON-FRI) の 8:00 に起動
  // cron(分 時 日 月 曜日 年) の順で指定
  // 年の指定は * にすることで毎年有効となる
  schedule_expression = "cron(0 8 ? * MON-FRI *)"
  schedule_expression_timezone = "Asia/Tokyo"

  target {
    arn      = "arn:aws:scheduler:::aws-sdk:rds:startDBCluster"
    role_arn = aws_iam_role.eventbridge-scheduler-rds-start-stop-role.arn
    input = jsonencode({
      DbClusterIdentifier = aws_rds_cluster.default.cluster_identifier
    })

    retry_policy {
      maximum_event_age_in_seconds = 600 // リトライする最大のイベントの経過時間:10分
      maximum_retry_attempts       = 1   // リトライ最大回数
    }
  }
}

//////////////////////////////////////////////////
// EventBridgeScheduler
// Aurora クラスタを停止
//////////////////////////////////////////////////
resource "aws_scheduler_schedule" "aurora_stop" {
  name       = "aurora-stop"
  group_name = "default"
  flexible_time_window {
    // フレックスタイムウィンドウは使用しない
    mode = "OFF"
  }

  // 平日 (MON-FRI) の 21:00 に停止
  schedule_expression = "cron(0 21 ? * MON-FRI *)"
  schedule_expression_timezone = "Asia/Tokyo"

  target {
    arn      = "arn:aws:scheduler:::aws-sdk:rds:stopDBCluster"
    role_arn = aws_iam_role.eventbridge-scheduler-rds-start-stop-role.arn
    input = jsonencode({
      DbClusterIdentifier = aws_rds_cluster.default.cluster_identifier
    })

    retry_policy {
      maximum_event_age_in_seconds = 600 // リトライする最大のイベントの経過時間:10分
      maximum_retry_attempts       = 1   // リトライ最大回数
    }
  }
}

動作確認

リソースが作成されていることを確認

画像

画像

自動停止されていることを確認

画像

一時的に毎時指定20時50分にて動作確認した結果、自動的に停止されることを確認しております。

動作確認専用
//////////////////////////////////////////////////
// EventBridgeScheduler
// Aurora クラスタを停止 (定期的: 毎日 20:50 JST)
//////////////////////////////////////////////////
resource "aws_scheduler_schedule" "aurora_stop" {
  name       = "aurora-stop"
  group_name = "default"
  flexible_time_window {
    mode = "OFF"
  }

  // 毎日 (day-of-week は問わない) 20:50 JST
  // cron(Minute Hour Day-of-month Month Day-of-week Year)
  // 日・月は「*」=毎日、曜日は「?」としています。
  schedule_expression = "cron(50 20 * * ? *)"
  schedule_expression_timezone = "Asia/Tokyo"

  target {
    arn      = "arn:aws:scheduler:::aws-sdk:rds:stopDBCluster"
    role_arn = aws_iam_role.eventbridge-scheduler-rds-start-stop-role.arn
    input = jsonencode({
      DbClusterIdentifier = aws_rds_cluster.default.cluster_identifier
    })

    retry_policy {
      maximum_event_age_in_seconds = 600
      maximum_retry_attempts       = 1
    }
  }
}

Discussion