EventBridge スケジューラを活用して EC2 インスタンスの自動起動・停止を行う - DEV コミュニティ

参考: この記事で述べられているコードはすべてこちらで見ることができます。

最近のある仕事で、EC2 インスタンスの起動・停止をスケジュールする必要がありました。課題は、使いやすく、メンテナンスがほとんど不要な仕組みをデザインすることでした。

仕様書では、ユーザーが API を通じて、指定されたパラメーターに基づいて特定の EC2 インスタンスの起動と停止を開始できるようにすることが求められていました。この目的のために、HTTP POST メソッドを使用する /provision というルートが選ばれました。パラメーターは JSON 形式でリクエストボディに渡されます:

  • start_time (タイムゾーンなしの日時): EC2 インスタンスのアクションを開始する日時で、タイムゾーンは考慮しません。
  • end_time (タイムゾーンなしの日時): EC2 インスタンスのアクションを終了する日時で、タイムゾーンを考慮しません。
  • instance_id (EC2 インスタンスの識別子): EC2 インスタンスに割り当てられた一意の識別子です。

AWS API は EC2 インスタンスの起動と停止に十分です。このタスクは、Python で書かれた Lambda 関数を使用して Boto3 SDK を使って容易に達成できます。

では、これらの Lambda を特定の日時にトリガーすることです。このために EventBridge スケジューラがぴったりです。

これを実装するために、API ルートは EventBridge 内で二つのルールを作成します。一つ目のルールは EC2 インスタンスを起動する Lambda 関数を活性化し、二つ目はそれを停止します。しかし、このアプローチには潜在的な問題があります:既にトリガーされたかもしれないルールを作成したときに何が起こるのでしょうか?

それほど前ではないけれど、AWS はこのジレンマに対する解決策を導入しました – トリガー後のルールを自動的に削除する機能です。この記事を読むと詳しく知ることができます:https://aws.amazon.com/blogs/compute/automatically-delete-schedules-upon-completion-with-amazon-eventbridge-scheduler/

私たちのアーキテクチャを概念化したので、実際に取り掛かります。以下は、図で表された簡単なアウトラインです:

私たちの小さなアプリケーションのアーキテクチャ図

このシナリオでは、プログラミング言語として主に AWS と Python を使用します。インフラストラクチャをデプロイするために、具体的には Terraform を使用します。

実践

最初のステップから始めましょう - 一つは起動用、もう一つは停止用の二つの Lambda。

以下は一つまたは複数の EC2 インスタンスを起動するための Lambda 関数です:

import boto3

def lambda_handler(event, _context):
    region = 'eu-west-3'
    ec2 = boto3.client('ec2', region_name=region)
    ec2.start_instances(InstanceIds=event["instances"])
    for instance in event["instances"]:
        print('Started your instances: ' + instance)

フルスクリーンモードを終了します。

次に、一つまたは複数の EC2 インスタンスを停止する関数です:

import boto3

def lambda_handler(event, _context):
    region = 'eu-west-3'
    ec2 = boto3.client('ec2', region_name=region)
    ec2.stop_instances(InstanceIds=event["instances"])
    for instance in event["instances"]:
        print('Stopped your instances: ' + instance)

フルスクリーンモードを終了します。

インスタンスの管理コードをセットアップしたら、EventBridge でルールを作成するのが次のタスクです。これには、AWS SDK を使用してこれらのルールを生成する第三の Lambda 関数を利用します。手動での介入は必要ありません。

import json
import os
import boto3

def lambda_handler(event, _context):
    body = json.loads(event["body"])
    region = os.environ["REGION"]
    start_time = body["start_time"]
    end_time = body["end_time"]
    ec2_server_id = body["ec2_instances_id"]
    scheduler = boto3.client('scheduler', region_name=region)
    scheduler.create_schedule(
        ActionAfterCompletion="DELETE",
        FlexibleTimeWindow={"Mode": "OFF"},
        Name="ec2-schedule-start-" + str(ec2_server_id),
        ScheduleExpression="at(" + start_time + ")",
        ScheduleExpressionTimezone="Europe/Paris",
        Target={
            "Arn": os.environ["START_EC2_LAMBDA_ARN"],
            "RoleArn": os.environ["EXECUTE_SCHEDULE_ROLE_ARN"],
            "Input": "{\"instances\": [\"" + ec2_server_id + "\"]}"
        },

    )

    scheduler.create_schedule(
        ActionAfterCompletion="DELETE",
        FlexibleTimeWindow={"Mode": "OFF"},
        Name="ec2-schedule-stop-" + str(ec2_server_id),
        ScheduleExpression="at(" + end_time + ")",
        ScheduleExpressionTimezone="Europe/Paris",
        Target={
            "Arn": os.environ["STOP_EC2_LAMBDA_ARN"],
            "RoleArn": os.environ["EXECUTE_SCHEDULE_ROLE_ARN"],
            "Input": "{\"instances\": [\"" + ec2_server_id + "\"]}"
        },
    )

    return {
        'statusCode': 200,
        'headers': {
            'Content-Type': 'application/json',
            'Access-Control-Allow-Origin': '*'
        },
        'body': json.dumps({
            'success': True
        }),
        "isBase64Encoded": False
    }

フルスクリーンモードを終了します。

Terraform コードについては、私のコードを公開しているこの GitHub リンクを参照してください。これを使用すると、全てのコンポーネントを AWS アカウントにデプロイできます(指示に従うために README を読んでください)。

デプロイの後、API Gateway に /provision ルートを持つ API があります。次のボディを使用して POST リクエストでこれにアクセスできます:

{
    "ec2_instances_id": "xxxxxxxxxx",
    "start_time": "2023-04-01T05:00:30",
    "end_time": "2023-04-01T07:00:30" 
}

フルスクリーンモードを終了します。

この API 呼び出しは、EventBridge スケジューラ内で二つのルールを作成することを容易にします。一つのルールは start_time フィールドで定めた時間にあなたのアプリケーションを起動し、もう一つのルールは end_time フィールドで定めた時間にインスタンスを停止します。

このシンプルなセットアップによって、一つまたは多くの EC2 インスタンスを巧みに統制することが非常に単純です。API ベースのインタラクションは非常に便利であり、実行後の自動ルールのクリーンアップは大きな利点です。

しかし、注意が必要です。現在、このスタックは "並行性" を管理していません。例えば、あるイベントが午後 3 時に始まり、午後 6 時に終わる場合、別のイベントが午後 5 時に始まって午後 8 時に終わることもできます。この重複により、二つ目のイベント中にインスタンスが午後 6 時に停止する可能性があります。このスタックをスケジューラーやカレンダーのようなアプリとペアリングするのが最適です(ネタばらし:この統合は私の進行中のプロジェクトの一部です)。

以上です!この記事が役に立ったと思います!

こちらの記事はdev.toの良い記事を日本人向けに翻訳しています。
https://dev.to/aws-builders/automatically-start-and-stop-ec2-instance-leveraging-eventbridge-scheduler-2ph