EventBridge Scheduler + Step Functions で起動順序を制御しながら EC2 → RDS を安全に自動操作する
このハンズオンでは、AWS Step Functions を使って EC2 と RDS の起動・停止に 順序と状態確認を加えた自動化ワークフローを構築します。「EC2 が running になってから RDS を起動する」「RDS が stopped になってから EC2 を停止する」という依存関係を Step Functions のステートマシンで実現します。EventBridge Scheduler で毎朝・毎夜に自動実行します。
| 項目 | 内容 |
|---|---|
| 所要時間 | 90〜120 分 |
| 難易度 | 中〜上級 |
| 主要サービス | Step Functions、EventBridge Scheduler、Lambda(Python)、EC2、RDS |
| シンプル版との違い | 起動完了を確認してから次の処理へ進む。Wait + Choice による状態ポーリング |
シンプル版(Scheduler + Lambda)は起動リクエストを送るだけで順序確認はしません。本番環境でアプリが「RDS が起動してから EC2 で接続を試みる」など依存関係がある場合はこの Step Functions 版を使ってください。
| 関数名 | 役割 |
|---|---|
| handson-sfn-start-ec2 | EC2 に StartInstances を送信 |
| handson-sfn-check-ec2 | EC2 の現在の状態(State.Name)を返す |
| handson-sfn-start-rds | RDS に StartDBInstance を送信 |
| handson-sfn-stop-rds | RDS に StopDBInstance を送信 |
| handson-sfn-check-rds | RDS の現在のステータス(DBInstanceStatus)を返す |
| handson-sfn-stop-ec2 | EC2 に StopInstances を送信 |
まず IAM ポリシーとロールを作成してから、6 つの Lambda 関数を作成します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:StartInstances", "ec2:StopInstances", "ec2:DescribeInstances",
"rds:StartDBInstance", "rds:StopDBInstance", "rds:DescribeDBInstances"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": ["logs:CreateLogGroup","logs:CreateLogStream","logs:PutLogEvents"],
"Resource": "arn:aws:logs:*:*:*"
}
]
}
次に IAM ロールを作成します。
import boto3, os
def lambda_handler(event, context):
instance_id = os.environ['EC2_INSTANCE_ID']
boto3.client('ec2').start_instances(InstanceIds=[instance_id])
print(f'EC2 {instance_id}: 起動リクエスト送信')
return {**event, 'ec2_instance_id': instance_id}
import boto3, os
def lambda_handler(event, context):
instance_id = event.get('ec2_instance_id', os.environ['EC2_INSTANCE_ID'])
resp = boto3.client('ec2').describe_instances(InstanceIds=[instance_id])
state = resp['Reservations'][0]['Instances'][0]['State']['Name']
print(f'EC2 {instance_id}: {state}')
return {**event, 'ec2_state': state}
import boto3, os
def lambda_handler(event, context):
db_id = os.environ['RDS_IDENTIFIER']
boto3.client('rds').start_db_instance(DBInstanceIdentifier=db_id)
print(f'RDS {db_id}: 起動リクエスト送信')
return {**event, 'rds_identifier': db_id}
import boto3, os
def lambda_handler(event, context):
db_id = os.environ['RDS_IDENTIFIER']
boto3.client('rds').stop_db_instance(DBInstanceIdentifier=db_id)
print(f'RDS {db_id}: 停止リクエスト送信')
return {**event, 'rds_identifier': db_id}
import boto3, os
def lambda_handler(event, context):
db_id = event.get('rds_identifier', os.environ['RDS_IDENTIFIER'])
resp = boto3.client('rds').describe_db_instances(DBInstanceIdentifier=db_id)
status = resp['DBInstances'][0]['DBInstanceStatus']
print(f'RDS {db_id}: {status}')
return {**event, 'rds_status': status}
import boto3, os
def lambda_handler(event, context):
instance_id = event.get('ec2_instance_id', os.environ['EC2_INSTANCE_ID'])
boto3.client('ec2').stop_instances(InstanceIds=[instance_id])
print(f'EC2 {instance_id}: 停止リクエスト送信')
return {**event, 'ec2_state': 'stopping'}
Step Functions が Lambda を呼び出すための IAM ロールを作成します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "lambda:InvokeFunction",
"Resource": "arn:aws:lambda:ap-northeast-1:*:function:handson-sfn-*"
},
{
"Effect": "Allow",
"Action": ["logs:CreateLogDelivery","logs:PutLogEvents","logs:CreateLogGroup"],
"Resource": "*"
}
]
}
Step Functions サービスの信頼エンティティは states.amazonaws.com です。ロールを作成する際に「Step Functions」を選択すると自動的に設定されます。
以下の JSON をそのまま貼り付けます。Lambda 関数の ARN は 自動取得のため手動置換不要です(Resource に関数名を指定する方法を使います)。
下記 JSON 内の <ACCOUNT_ID> をご自身の AWS アカウント ID(12 桁)に置き換えてください。アカウント ID はコンソール右上のアカウント名をクリックすると確認できます。
{
"Comment": "EC2 running 確認後に RDS を起動するワークフロー",
"StartAt": "StartEC2",
"States": {
"StartEC2": {
"Type": "Task",
"Resource": "arn:aws:lambda:ap-northeast-1:<ACCOUNT_ID>:function:handson-sfn-start-ec2",
"Next": "WaitEC2Boot",
"ResultPath": "$.start_ec2_result"
},
"WaitEC2Boot": {
"Type": "Wait",
"Seconds": 15,
"Next": "CheckEC2Status"
},
"CheckEC2Status": {
"Type": "Task",
"Resource": "arn:aws:lambda:ap-northeast-1:<ACCOUNT_ID>:function:handson-sfn-check-ec2",
"Next": "IsEC2Running",
"ResultPath": "$"
},
"IsEC2Running": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.ec2_state",
"StringEquals": "running",
"Next": "StartRDS"
}
],
"Default": "WaitEC2Boot"
},
"StartRDS": {
"Type": "Task",
"Resource": "arn:aws:lambda:ap-northeast-1:<ACCOUNT_ID>:function:handson-sfn-start-rds",
"End": true
}
}
}
「作成」をクリックします。
| タイプ | 説明 |
|---|---|
| Task | Lambda などの外部サービスを呼び出す。Resource に ARN を指定 |
| Wait | 指定秒数だけ待機する。ポーリングの間隔として使用 |
| Choice | 条件分岐。Variable の値によって次のステートを決定 |
起動と同じ手順でステートマシンを作成します。
<ACCOUNT_ID> を置き換えてください{
"Comment": "RDS stopped 確認後に EC2 を停止するワークフロー",
"StartAt": "StopRDS",
"States": {
"StopRDS": {
"Type": "Task",
"Resource": "arn:aws:lambda:ap-northeast-1:<ACCOUNT_ID>:function:handson-sfn-stop-rds",
"Next": "WaitRDSStop",
"ResultPath": "$.stop_rds_result"
},
"WaitRDSStop": {
"Type": "Wait",
"Seconds": 30,
"Next": "CheckRDSStatus"
},
"CheckRDSStatus": {
"Type": "Task",
"Resource": "arn:aws:lambda:ap-northeast-1:<ACCOUNT_ID>:function:handson-sfn-check-rds",
"Next": "IsRDSStopped",
"ResultPath": "$"
},
"IsRDSStopped": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.rds_status",
"StringEquals": "stopped",
"Next": "StopEC2"
}
],
"Default": "WaitRDSStop"
},
"StopEC2": {
"Type": "Task",
"Resource": "arn:aws:lambda:ap-northeast-1:<ACCOUNT_ID>:function:handson-sfn-stop-ec2",
"End": true
}
}
}
RDS の停止処理は通常 3〜10 分かかります。WaitRDSStop で 30 秒待機してから状態確認するループが繰り返されます。実際の実行では CheckRDSStatus → IsRDSStopped → WaitRDSStop のループが 5〜15 回程度繰り返されます。
Step Functions を定時実行するための Scheduler を 2 つ作成します。まず Scheduler 用の IAM ロールが必要です。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "states:StartExecution",
"Resource": [
"arn:aws:states:ap-northeast-1:*:stateMachine:handson-start-sfn",
"arn:aws:states:ap-northeast-1:*:stateMachine:handson-stop-sfn"
]
}
]
}
IAM ロールを作成します。
「EventBridge」ではなく 「EventBridge Scheduler」 を選択してください。信頼ポリシーのプリンシパルが scheduler.amazonaws.com になっていることを確認してください。コンソールに「Scheduler」が一覧にない場合は「カスタム信頼ポリシー」で直接入力します。
{} のまま → 「実行の開始」Step Functions の実行画面にステートマシンのフロー図が表示されます。現在実行中のステートが青く、完了したステートが緑に変わります。WaitRDSStop → CheckRDSStatus → IsRDSStopped のループが RDS が停止するまで繰り返されます(3〜10 分程度)。
rds_status フィールドの値が表示される(例: "stopping"、"stopped")"stopped" になった時点で StopEC2 へ進む様子を確認するEC2 と RDS が停止したことを確認してから、handson-start-sfn を同様に手動実行します。「EC2 が running になってから RDS が起動リクエストを受け取る」フローを確認してください。
Step Functions の「ステートマシン」一覧から handson-start-sfn と handson-stop-sfn が消えていれば完了です。Lambda 関数 6 つ、Scheduler 2 つ、IAM リソースの削除も確認してください。