💻

AWS Amazon EventBridge スケジューラでAmazon Auroraクラスターの稼働時間を管理する

2023/09/20に公開

概要

以前、Amazon RDSインスタンスをServerless v2化しましたが、開発環境などでは
常に稼働しておく必要がない場合があります、そのような時は稼働時間を調整してインフラコストを抑えます。
AWS Amazon EventBridgeスケジューラでスケジュールを作成します。

https://zenn.dev/minedia/articles/88bbea2e70a233

3行まとめ

・EventBridgeスケジューラでスケジュールを作成する
・Auroraクラスター(インスタンス)の稼働時間を管理する
・Terraformでコード管理する

前提

Amazon Aurora MySQL Serverless v2を利用している
通常のAuroraインスタンスの場合は設定値が異なります。

手順

ロールを作成する

IAMより、EventBridgeスケジュールに必要なロールを作成します。

カスタム信頼ポリシーを選択します。

{
  "Version": "2012-10-17",
  "Statement": [

      "Effect": "Allow",
      "Principal": {
        "Service": "scheduler.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

作成したロールにポリシーをアタッチします。

StartDBClusterとStopDBClusterができればOKです。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "rds:StartDBCluster",
        "rds:StopDBCluster"
      ],
      "Resource": [
        "*"
      ]
    }
  ]
}

権限の作成は以上です。続いてスケジュールを作成します

EventBridgeスケジュールでスケジュールを作成する

アマゾンのセットアップ EventBridge スケジューラー

続いて、EventBridgeのメニューからスケジュールを作成します。

検索窓で検索し、Amazon EventBridge Schedulerを選択します。

右上のスケジュールの作成を選択します。

スケジュールグループはdefaultのまま作成します。

スケジュールグループの管理

スケジュールはcronで指定します。毎週月曜から金曜日の始業開始前に稼働させ、
終業後に停止してみます。

Cron ベースのスケジュール


毎週月曜日から金曜日の9時から20時まで稼働(UTC時間で記述)

start

cron(0 0 ? * MON-FRI *)

stop

cron(0 11 ? * MON-FRI *)

フレックスタイムはOFFにします。
フレキシブルなタイムウィンドウの設定

rdsで検索し、Amazon RDSを選択します。

startで検索し、StartDBClusterを選択します。

stopを作成する際は、StopDBClusterを選択します。

EventBridgeで利用できるAPI一覧

クラスター名がrds-cluster-1の場合

{
  "DbClusterIdentifier": "rds-cluster-1"
}

Amazon Aurora MySQLのインスタンスを稼働・停止させる場合は
StartDBInstance
StopDBInstance
を指定します。

インスタンス名がrds-instance-1の場合

{
  "DbInstanceIdentifier": "rds-instance-1"
}

ターゲットを「すべてのAPI」にします。

start用とstop用が作成できました。

指定の時間にスケジュールが作動されて、インスタンスが稼働・停止されました。

Terraform

リソースをTerraformで作成します。
(Clusterの例)

Terraform aws_scheduler_schedule

json

role_rds_scheduler.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "scheduler.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

role_rds_scheduler_policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "rds:StartDBCluster",
        "rds:StopDBCluster"
      ],
      "Resource": [
        "*"
      ]
    }
  ]
}

role

resource "aws_iam_role" "role_rds_scheduler" {
  name     = "role_rds_scheduler"
  assume_role_policy = templatefile("json/role_rds_scheduler.json", {
    aws_region      = [region_name]
    aws_account_id  = [account_id]
    principal_arn   = [principal_arn]
    external_id     = [external_id]
  })
}

resource "aws_iam_policy" "role_rds_scheduler_policy" {
  path  = "/"
  name  = "role_rds_scheduler_policy"
  policy = templatefile("json/role_rds_scheduler_policy.json", {
  })
}

resource "aws_iam_role_policy_attachment" "role_rds_scheduler" {
  role       = aws_iam_role.role_rds_scheduler.name
  policy_arn = aws_iam_policy.role_rds_scheduler_policy[0].arn
}

aws_scheduler_schedule

resource "aws_scheduler_schedule" "rds-start" {
  name       = "rds-start"
  group_name = "default"

  flexible_time_window {
    mode = "OFF"
  }

  schedule_expression = "cron(0 0 ? * MON-FRI *)"

  target {
    arn      = "arn:aws:scheduler:::aws-sdk:rds:startDBCluster"
    role_arn = aws_iam_role.role_rds_scheduler.arn

    input = jsonencode({
      DbClusterIdentifier = "rds-cluster-1"
    })

    retry_policy {
      maximum_event_age_in_seconds = 600
      maximum_retry_attempts       = 1
    }
  }
}

resource "aws_scheduler_schedule" "rds-stop" {
  name       = "rds-stop"
  group_name = "default"

  flexible_time_window {
    mode = "OFF"
  }

  schedule_expression = "cron(0 11 ? * * *)"

  target {
    arn      = "arn:aws:scheduler:::aws-sdk:rds:stopDBCluster"
    role_arn = aws_iam_role.role-rds-scheduler.arn

    input = jsonencode({
      DbClusterIdentifier = "rds-cluster-1"
    })

    retry_policy {
      maximum_event_age_in_seconds = 600
      maximum_retry_attempts       = 1
    }
  }
}

ターゲットのペイロードの指定はinputで記述します。DbClusterIdentifierです。
値はAuroraのクラスター名です。インスタンスの方を指定すると動作しませんのでご注意

リトライは10分後に1回にしていますが、適宜変更してください。

注意点としてtargetのarnを指定する際にstartDBCluster、stopDBClusterと先頭が小文字になっています。

arn:aws:scheduler:::aws-sdk:rds:startDBCluster
arn:aws:scheduler:::aws-sdk:rds:stopDBCluster

UIで指定したAPI名の場合(StartDBCluster・StopDBCluster)、

ValidationException: Invalid RequestJson provided. Reason The api StartDBCluster is not valid for the service aws-sdk:rds.

とエラーになってしまいます。

以上となります。AWS Aurora Serverless v2でも問題なく稼働・停止のスケジューリングが実現できました。結構シンプルに実現できるので活用してみてはいかがでしょうか。

参考

アマゾンのセットアップ EventBridge スケジューラー
EventBridgeで利用できるAPI一覧
Cron ベースのスケジュール
スケジュールグループの管理
フレキシブルなタイムウィンドウの設定
Terraform aws_scheduler_schedule
【5分で簡単!】Amazon EventBridge SchedulerでRDSの自動定期停止を実装してみた

株式会社マインディア テックブログ

Discussion