AWSコスト削減の第一歩|EventBridge+Lambdaで実現するEC2自動起動・自動停止の構築手順

AWSを利用した開発環境や検証環境において、「EC2インスタンスが24時間365日稼働したままになっている」というケースは少なくありません。

「明日も使うかもしれないから」「つい停止し忘れてしまった」「誰が起動したインスタンスかわからず停止しづらい」といった理由で放置されたEC2は、運用担当者が気付かないうちに月々のAWS利用料金を増加させる原因となります。

AWSのコスト最適化というと、複雑なアーキテクチャの見直しやインスタンスサイズのチューニングをイメージされるかもしれません。しかし、「利用しない時間帯のリソースを確実に停止する」ことは、手軽に取り組めるコスト削減施策です。

本記事では、Amazon EventBridge SchedulerAWS Lambdaを組み合わせ、EC2インスタンスの起動・停止を完全自動化する仕組みの構築手順をご紹介します。

なぜEC2の自動起動・自動停止が必要なのか?

例えば、次のようなスケジュールで稼働している社内の開発環境を想定してみましょう。

  • 利用時間:平日 9:00〜18:00
  • 夜間・休日:利用しない

この場合、実際の利用時間は1日あたり約9時間です。しかし、EC2インスタンスを24時間起動したままにすると、利用していない残り15時間分に対してもコンピュート料金が発生し続けてしまいます。

【実際の利用時間と稼働時間の差】
■ 実際の利用時間:約9時間/日
■ 実際の稼働時間:24時間/日 ※15時間分の不要なコストが発生

対象となる開発サーバーや検証サーバーが複数台ある場合、こうした不要なコストはさらに増加します。

コスト削減効果の試算(m5.largeの例)

EC2の自動起動・自動停止によって、実際にどの程度のコスト削減が期待できるのでしょうか。開発環境でよく選ばれるm5.largeインスタンス(Linux/オンデマンド)を例に試算します。

【試算条件】

  • インスタンスタイプ:m5.large
  • オンデマンド料金概算:約20円/時間(※為替やリージョンにより変動します)
  • 稼働条件:平日 9:00〜18:00(月20営業日)

24時間稼働し続けた場合

  • 24時間×30日=720時間/月
  • 720時間×20円=約14,400円/月

平日9:00〜18:00のみ稼働させた場合

  • 9時間×20日=180時間/月
  • 180時間×20円=約3,600円/月

削減効果

  • 14,400円−3,600円=約10,800円/月(約75%のコスト削減)

m5.largeを1台自動停止にするだけで、月額約1万円のコスト削減が見込めます。

「Webサーバー」「APサーバー」「DBサーバー」の3台構成の場合、月額約32,400円(年間で約38万8,800円)を削減できます。運用の仕組みを一度整えるだけでこれだけの成果を得られるため、費用対効果の高い取り組みといえるでしょう。

EventBridgeとLambdaの役割

今回の自動化構成では、AWSの2つのサービスが連携して動作します。

[ スケジュール (cron) ]
         │
         ▼
[ EventBridge Scheduler ] (目覚まし時計の役割)
         │
         ▼
[ AWS Lambda ]            (実行作業員の役割)
         │
         ▼
[ Amazon EC2 ]           (起動 / 停止)
  • Amazon EventBridge Scheduler(目覚まし時計):あらかじめ設定したスケジュール(平日9:00、平日18:00など)に従って、処理を起動するトリガーとして機能します。
  • AWS Lambda(作業員):EventBridgeからの通知を受け取り、EC2の起動・停止などの実際の処理を実行します。

構築手順

それでは、実際の構築ステップを解説します。

①IAMロールの作成(信頼関係と許可ポリシーの設定)

AWSのセキュリティベストプラクティスに基づき、「Lambda用」と「EventBridge Scheduler用」の2つのIAMロールを作成します。

ここで最も重要なのが「信頼関係(Trust Policy)」の設定です。信頼関係とは、「誰(どのAWSサービス)がこのロールを利用して作業(sts:AssumeRole)することを許可するか」を定義するセキュリティ設定です。この設定が正しくされていない場合、「The role defined for the function cannot be assumed」といったエラーが発生して処理が実行されません。

コンソールでの信頼関係の設定・編集手順

  1. IAMコンソールの「ロール」から対象のロールを選択します。
  2. 「信頼関係(Trust relationships)」 タブを開き、「信頼ポリシーを編集(Edit trust policy)」 をクリックします。
  3. エディタに以下のJSONコードを貼り付けて保存します。
Lambda用IAMロール(ロール名例:Lambda-EC2-StartStop-Role)

Lambda関数がEC2を操作し、CloudWatch Logsへログを出力するために必要なロールです。

  • 信頼関係(Trust Policy):Lambdaサービス(lambda.amazonaws.com)にこのロールの引き受けを許可します。

JSON

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
  • 許可ポリシー(Permissions): AWSマネージドポリシーのAWSLambdaBasicExecutionRoleをアタッチした上で、以下のインラインポリシーを追加します。

JSON

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ec2:StartInstances",
        "ec2:StopInstances",
        "ec2:DescribeInstances"
      ],
      "Resource": "*"
    }
  ]
}
EventBridge Scheduler用IAMロール(ロール名例:Scheduler-InvokeLambda-Role)

EventBridge Schedulerが指定した時刻にLambda関数を呼び出すための実行ロールです。

  • 信頼関係(Trust Policy): Schedulerサービス(scheduler.amazonaws.com)にこのロールの引き受けを許可します。

JSON

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "scheduler.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
  • 許可ポリシー(Permissions)

JSON

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "lambda:InvokeFunction"
      ],
      "Resource": "*"
    }
  ]
}

信頼ポリシーの主要キーの解説

  • Effect: “Allow”:処理の実行を許可します。
  • Principal.Service:許可を与えるサービス(lambda.amazonaws.comやscheduler.amazonaws.com)を指定します。この設定に誤りがあると、サービスがロールを使えなくなります。
  • Action: “sts:AssumeRole”:ロールの権限を引き受ける(一時認証情報を取得する)特別なAPIアクションです。

②EC2起動・停止用Lambda関数の作成

次に、処理を行うPythonコードを準備します。作成した「Lambda用IAMロール」を割り当てて関数を作成します。

EC2起動用Lambda(関数名:ec2-auto-start)

Python

import boto3

ec2 = boto3.client('ec2')

# 対象のインスタンスIDを指定
INSTANCE_IDS = [
    'i-0123456789abcdef0',
    'i-0123456789abcdef1'
]

def lambda_handler(event, context):
    ec2.start_instances(InstanceIds=INSTANCE_IDS)
   
    msg = f'{len(INSTANCE_IDS)} EC2 instances started'
    print(msg)  # CloudWatch Logsにログを出力
   
    return {
        'statusCode': 200,
        'message': msg
    }

EC2停止用Lambda(関数名:ec2-auto-stop)

Python

import boto3

ec2 = boto3.client('ec2')

# 対象のインスタンスIDを指定
INSTANCE_IDS = [
    'i-0123456789abcdef0',
    'i-0123456789abcdef1'
]

def lambda_handler(event, context):
    ec2.stop_instances(InstanceIds=INSTANCE_IDS)
   
    msg = f'{len(INSTANCE_IDS)} EC2 instances stopped'
    print(msg)  # CloudWatch Logsにログを出力
   
    return {
        'statusCode': 200,
        'message': msg
    }

実行ログの確認方法
Lambdaの実行結果やprint()で出力したメッセージは、Amazon CloudWatch Logs(ロググループ:/aws/lambda/関数名)で確認できます。また、Lambdaコンソールの「テスト」実行時にもレスポンス(Response)として直接表示されます。

③EventBridge Schedulerのスケジュール設定

作成したLambda関数を定期実行するため、EventBridge Schedulerでスケジュールを設定します。実行ロールには「EventBridge Scheduler用IAMロール」を指定します。

設定時の注意点(タイムゾーン表記)
EventBridgeのcron設定はUTC(協定世界時)で指定する必要があります。日本時間(JST)で運用する場合は、指定したい時刻から9時間引いた時刻を入力してください。

  • 日本時間9:00(起動)=UTC 0:00→cron(0 0 ? * MON-FRI *)
  • 日本時間18:00(停止)=UTC 9:00→cron(0 9 ? * MON-FRI *)
EventBridge Schedulerでスケジュールを設定

cron式の基本書式

Plaintext

cron(分 時 日 月 曜日 年)

項目指定値補足
0〜59 
0〜23※ UTC時間で指定
1〜31曜日を指定する場合は ?
1〜12 
曜日SUN-SAT日を指定する場合は ?
1970〜2199 

設定する2つのスケジュール

  1. 起動用スケジュール(平日9:00 JST)
    1. cron指定:cron(0 0 ? * MON-FRI *)
    1. ターゲット:Lambda関数ec2-auto-start
  2. 停止用スケジュール(平日 18:00 JST)
    1. cron指定:cron(0 9 ? * MON-FRI *)
    1. ターゲット:Lambda関数ec2-auto-stop

発展編:タグベース運用で管理負荷を軽減する

先ほどのコードではインスタンスIDを直接コード内に記載(ハードコード)していましたが、開発サーバーが増減するたびにLambdaのコードを修正するのは手間がかかります。

そこで有効なのが、特定のタグが付与されたEC2インスタンスを動的に検知して処理する「タグベース運用」です。

タグ設定例

  • Tag Key:AutoSchedule
  • Tag Value:Enable

タグ判定付きLambdaコード(起動用の例)

Python

import boto3

ec2 = boto3.client('ec2')

def lambda_handler(event, context):
    # AutoSchedule=Enable かつ 停止中(stopped) のインスタンスを検索
    response = ec2.describe_instances(
        Filters=[
            {'Name': 'tag:AutoSchedule', 'Values': ['Enable']},
            {'Name': 'instance-state-name', 'Values': ['stopped']}
        ]
    )
   
    instance_ids = []
    for reservation in response['Reservations']:
        for instance in reservation['Instances']:
            instance_ids.append(instance['InstanceId'])
           
    if instance_ids:
        ec2.start_instances(InstanceIds=instance_ids)
        msg = f"Successfully started instances: {instance_ids}"
    else:
        msg = "No stopped target instances found."

    print(msg)
    return {'statusCode': 200, 'message': msg}

この設定を行うことで、新しく検証用サーバーを構築した際も、AutoSchedule=Enableタグを付与するだけで自動起動・自動停止の対象に加えることができ、運用負荷を大幅に削減できます。

まとめ

AWSのコスト最適化においては、大掛かりな構成変更だけでなく、不要な時間にサーバーを停止するといった基本的な運用の見直しも有効です。

今回ご紹介したEventBridge SchedulerとLambdaの組み合わせは、比較的少ない手順で導入でき、EC2の利用状況に応じたコスト削減につなげられる施策です。特に注意が必要なIAMの信頼関係(Trust Policy)設定のポイントさえ押さえておけばスムーズに構築できます。開発環境の停止忘れにお悩みの方は、ぜひ導入を検討してみてください。

サーバー運用・構築支援ならベアサポート

ベアサポートでは、長年のサーバー運用実績に基づき、クラウド・オンプレミスを問わずインフラの設計・構築から運用自動化、障害一次対応代行まで、お客さまの環境に合わせた最適なインフラ運用ソリューションをご提供しています。

「自社内のリソースだけではクラウドのコスト最適化や自動化まで手が回らない」「24時間365日の監視・運用体制を効率的に構築したい」といったお悩みがございましたら、ぜひベアサポートにご相談ください。

インフラ運用を24時間365日サポート|ベアサポート