🦁

AWSコスト、見直していますか? 第2弾 〜夜は止めよう!自動停止・起動でムダな出費にサヨウナラ〜

に公開

こんにちは。株式会社ペライチのインフラエンジニア西野です。

先日、弊社テックブログで
AWS コスト、見直していますか? 〜ムダな出費を抑える 3 つの視点〜を公開しました。

今回はその第 2 弾として、**弊社でも実践している「休日・夜間の ECS・RDS 自動停止・起動」**によるコスト最適化について紹介します。
この記事は、AWS の基本操作に慣れ、 ECS や RDS を少し触ったことがある方向けです。
「本番環境は常に稼働させる必要があるけれど、検証環境や一部の業務系リソースは本当に 24 時間動いている必要があるのか?」
そう考えたことがある方は、ご覧いただければ幸いです。

🗂️ 目次

  1. なぜ夜間や休日にとめるのか?
  2. 構成概要
  3. IAM ロールの作成
  4. Lambda で ECS サービスを停止・起動
  5. Lambda で RDS クラスタを停止・起動
  6. EventBridge でスケジュール設定
  7. 注意点・補足
  8. まとめ
  9. 最後に

1. なぜ夜間や休日にとめるのか?

開発・検証環境は、平日の日中のみ使用されることが多いです。
常に稼働していると、使われていない時間帯にもインスタンスや DB に対するコストが
発生します。
特に Aurora などの RDS や、Fargate ベースの ECS は時間課金のためムダなコストが積み重なります。そこで「使わない時間帯はとめる」ことで、コスト削減につなげていきます。

2. 構成概要

最初に構成の説明をしていきます。
構成自体はいたってシンプルです。EventBridge でスケジュール管理、Lambda か各サービスの API に対して処理を実行します。

  1. EventBridge が定時に Lambda を起動
  2. Lambda が ECS と RDS に対して停止・起動処理を実行

3. IAMロールの作成

まず最初に ECS や RDS、CloudWatchLogs の操作に許可を与えた IAM ロールを作成します。
最低限必要な権限は以下の通りです。
各操作毎のロール若しくは、サービス毎にロールを作成します。
※ Resource などは必要に応じて適切な権限を付与してください。

ECS

アクション 説明
ecs:UpdateService desiredCount の変更(= 停止・起動)に必須
ecs:DescribeServices 状態確認などに必要(任意)

RDS

アクション 説明
rds:StartDBCluster Auroraクラスタを起動
rds:StopDBCluster Auroraクラスタを停止
rds:DescribeDBClusters 起動後の状態確認やログ出力用(任意)

CloudWatchLogs

アクション 説明
logs:CreateLogGroup 指定された名前のロググループを作成します。
logs:CreateLogStream ロググループの中に、ログストリーム(時間単位のログの束)を作成
logs:PutLogEvents 作成されたログストリームに対して、実際のログイベントを送信

例)Resource_Auto_Start_Stop_Role。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "rds:StartDBCluster",
        "rds:StopDBCluster",
        "rds:DescribeDBClusters"
      ],
      "Resource": [
        "arn:aws:rds:ap-northeast-1:123456789012:cluster:dev-*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "ecs:UpdateService",
        "ecs:DescribeServices"
      ],
      "Resource": [
        "arn:aws:ecs:ap-northeast-1:123456789012:service/dev-*/dev-*",
        "arn:aws:ecs:ap-northeast-1:123456789012:cluster/dev-*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": [
        "arn:aws:logs:ap-northeast-1:123456789012:log-group:/aws/lambda/dev-*:*"
      ]
    }
  ]
}

4. LambdaでECS サービスを停止・起動

続いて、Lambda を使って ECS サービスの停止・起動をする処理を作成していきます。
今回は Python 3.12 を使用しています。

ここでは作成手順の詳細は割愛し、コードベースで処理の流れを中心に解説していきます。

以下の Boto3 ドキュメントも併せて参照ください。

import boto3
from botocore.exceptions import ClientError

def lambda_handler(event, context):
    client = boto3.client('ecs')

    # 操作対象のECSクラスター名とサービス名
    services = [
        ('hoge-cluster', 'hoge-service')
    ]

    for cluster_name, service_name in services:
        try:
            # desiredCount=0 で停止、1以上で起動
            desired_count = 1
            client.update_service(
                cluster=cluster_name,
                service=service_name,
                desiredCount=desired_count
            )
            # 成功時は最小限のログ
            print(f"{service_name} を desiredCount={desired_count} に更新しました")

        except ClientError as e:
            # 具体的なエラーだけ出す(機密情報は含まれない)
            print(f"ClientError on {service_name}: {e.response['Error']['Message']}")

        except Exception as e:
            # 想定外エラーも簡潔に
            print(f"Unexpected Error on {service_name}: {str(e)}")

    return 'Done'


Point

アクション 説明
boto3.client('ecs') ECS操作用のクライアントを作成
services = [(cluster名,service名)] ターゲットとなるClusterとサービス名
desiredCount 起動するタスク数を指定します。1 以上 → サービス起動、0 → サービス停止

5.Lambda でRDSクラスタを停止・起動

次に、Lambda を使って Aurora RDS クラスタの停止・起動 を行う処理を作成していきます。
このコードは Aurora RDS を前提とした構成になっており、先ほどの ECS 編と同様に Python 3.12 を使用しています。

以下の Boto3 ドキュメントも併せて参照ください。

import boto3
from botocore.exceptions import ClientError

def lambda_handler(event, context):
    rds_client = boto3.client('rds')

    # 操作対象のAurora DBクラスタ名
    db_clusters = ['hoge-aurora-cluster']

    # 操作種別: "start" または "stop"
    operation = "start"

    for db_cluster in db_clusters:
        try:
            if operation == "start":
                rds_client.start_db_cluster(DBClusterIdentifier=db_cluster)
                print(f"[INFO] {db_cluster} を起動しました")
            else:
                rds_client.stop_db_cluster(DBClusterIdentifier=db_cluster)
                print(f"[INFO] {db_cluster} を停止しました")

        except ClientError as e:
            # 例: すでに起動済み/停止済みの場合
            print(f"[WARN] {db_cluster}: {e.response['Error']['Message']}")

        except Exception as e:
            # 想定外のエラー
            print(f"[ERROR] {db_cluster}: {str(e)}")

    return {"status": "done", "operation": operation}


Point

アクション 説明
boto3.client('rds') RDS操作用のクライアントを作成
db_clusters = ['hoge-aurora-cluster'] ターゲットとなるAurora Cluster名
rds_client.start_db_cluster Clusterを起動
rds_client.stop_db_cluster Clusterを停止

6. EventBridgeでスケジュール設定

Lambda 関数を自動実行するために、EventBridge ルールを使ってスケジュールを設定します。
ここでは「平日の朝 7 時(JST)に起動処理を実行し、夜 22 時(JST)に停止処理を実行する」例を示します。

EventBridge の cron 式は UTC 基準で記述する必要があります。
そのため、JST の時間を −9 時間して UTC に変換します。

cron式の書き方

JST (日本時間) UTC (世界標準時) cron式例 説明
07:00 JST(平日朝) 22:00 (前日 UTC) cron(0 22 ? * Sun-THU *) 毎週月曜〜金曜の朝7時(JST)に実行
22:00 JST(平日夜) 13:00 (同日 UTC) cron(0 13 ? * MON-FRI *) 毎週月曜〜金曜の夜10時(JST)に実行

ルールの作成時のポイント

  • ターゲットに先ほど作成した Lambda 関数を指定する
  • IAMロールは EventBridge から Lambda を実行する権限が必要になります。
    自動で新規作成もしてくれるので、それを利用する形でも問題ないです。

7. 注意点・補足

  • Aurora RDS は停止後 7 日で自動起動されます(停止のしっぱなしは不可)
  • ECS Fargate は、サービスの desiredCount=0 でもタスク定義は保持されるため、再起動もスムーズです。
  • 起動時にクラスタやサービスの状態確認を加えるとより堅牢です。
  • Terraform でこのしくみを構築できれば、より IaC に準拠しますが、まずはマネジメントコンソールで簡単に試すのもお勧めです。

8. まとめ

  • 開発や検証環境において、夜間・休日に使わないリソースを自動停止することで、環境規模に応じて数千円〜数万円のコスト削減が可能です。
    例: t4g.medium の RDS インスタンスを 9 時間/日 × 22 日/月停止した場合、月あたり約 1,500 〜 2,000 円前後の削減が期待できます(AWS 料金はリージョンや契約プランにより異なります)。

  • 本記事では、Lambda + EventBridge による RDS と ECS の自動制御の構成とコードを紹介しました。

9. 最後に

今回は、AWS リソースの自動停止・起動を実現する構成と、その実装コードについて紹介しました。
第 3 弾では、「とめるもの・とめないものを自動で見極める」「複数リソースを効率よく制御する」など、より柔軟で実践的なリソース管理のテクニックを取り上げる予定です。

最後までお読みいただき、ありがとうございました!

ペライチ

Discussion