EC2インスタンスを定期的に起動/停止する3つの方法
EC2インスタンスを定期的に起動/停止する方法を考えます。
1つ目はEventBridgeを使って、インスタンス起動/停止用のLambda関数を定期的に実行する方法です。
「Instance Scheduler」と呼ばれている方法です。
https://aws.amazon.com/jp/solutions/implementations/instance-scheduler-on-aws/
2つ目はメンテナンスウィンドウを定義して、インスタンス起動/停止用のSSM Automationランブック(AWS-StartEC2Instance, AWS-StopEC2Instance)を定期的に実行する方法です。
3つ目はEventBridge Schedulerを使って、インスタンス起動/停止用のAPIを定期的に呼び出す方法です。
https://docs.aws.amazon.com/ja_jp/eventbridge/latest/userguide/scheduler.html
本ページでは、CloudFormationを使って、上記の3つの方法を試します。
構築する環境
3つのEC2インスタンスを作成します。
OSは最新のAmazon Linux 2023とします。
各インスタンスに対して、冒頭でご紹介した設定を行います。
5分ごとにインスタンスを起動/停止させます。
なおLambda関数のランタイム環境はPython3.8とします。
CloudFormationテンプレートファイル
上記の構成をCloudFormationで構築します。
以下のURLにCloudFormationテンプレートを配置しています。
https://github.com/awstut-an-r/awstut-saa/tree/main/04/004
テンプレートファイルのポイント解説
EventBridgeとLambda関数を使用する方法
Lambda関数
Resources:
StartInstanceFunction:
Type: AWS::Lambda::Function
Properties:
Environment:
Variables:
INSTANCE_ID: !Ref Instance
REGION: !Ref AWS::Region
Code:
ZipFile: |
import boto3
import os
instance_id = os.environ['INSTANCE_ID']
region = os.environ['REGION']
ec2_client = boto3.client('ec2', region_name=region)
def lambda_handler(event, context):
response = ec2_client.start_instances(
InstanceIds=(instance_id,)
)
print(response)
FunctionName: !Sub "${Prefix}-StartInstance"
Handler: !Ref Handler
Runtime: !Ref Runtime
Role: !GetAtt FunctionRole.Arn
Timeout: !Ref Timeout
StopInstanceFunction:
Type: AWS::Lambda::Function
Properties:
Environment:
Variables:
INSTANCE_ID: !Ref Instance
REGION: !Ref AWS::Region
Code:
ZipFile: |
import boto3
import os
instance_id = os.environ['INSTANCE_ID']
region = os.environ['REGION']
ec2_client = boto3.client('ec2', region_name=region)
def lambda_handler(event, context):
response = ec2_client.stop_instances(
InstanceIds=(instance_id,)
)
print(response)
FunctionName: !Sub "${Prefix}-StopInstance"
Handler: !Ref Handler
Runtime: !Ref Runtime
Role: !GetAtt FunctionRole.Arn
Timeout: !Ref Timeout
Code language: YAML (yaml)
2つのLambda関数を作成します。
一方はインスタンスの開始、もう一方は停止を行います。
boto3のEC2用クライアントオブジェクトを作成します。
同オブジェクトのstart_instancesおよびstop_instancesメソッドを実行します。
以下が両関数用のIAMロールです。
Resources:
FunctionRole:
Type: AWS::IAM::Role
DeletionPolicy: Delete
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
Service:
- lambda.amazonaws.com
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: InstanceStartStopPolicy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- ec2:StartInstances
- ec2:StopInstances
Resource: !Sub "arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:instance/${Instance}"
Code language: YAML (yaml)
インスタンスの起動/停止アクションを許可する内容です。
EventBridgeルール
本ページでは、EC2インスタンスを定期的に起動・停止する方法を中心に取り上げます。
EventBridgeルールを使って、Lambda関数を定期的に実行する方法については、以下のページをご確認ください。
Resources:
StartInstanceScheduleRule:
Type: AWS::Events::Rule
Properties:
ScheduleExpression: cron(0,10,20,30,40,50 * * * ? *)
State: ENABLED
Targets:
- Arn: !GetAtt StartInstanceFunction.Arn
Id: !Sub "${Prefix}-StartInstanceScheduleRule"
StopInstanceScheduleRule:
Type: AWS::Events::Rule
Properties:
ScheduleExpression: cron(5,15,25,35,45,55 * * * ? *)
State: ENABLED
Targets:
- Arn: !GetAtt StopInstanceFunction.Arn
Id: !Sub "${Prefix}-StopInstanceScheduleRule"
Code language: YAML (yaml)
一方は起動用で、もう一方は停止用です。
それぞれ先述の関数をターゲットとしています。
cron式で起動状況を設定します。
起動用関数は毎時0, 10, … 50分に呼び出します。
停止用関数は毎時5, 15, … 55分を呼び出します。
これらを組み合わせると、5分ごとに起動・停止を繰り返すことになります。
EventBridgeルールを使ってLambda関数を定期的にする場合、EventBridgeが関数を呼び出す形となります。
ですからEventBridgeに関数を呼び出す権限を与える必要があります。
Resources:
StartInstanceFunctionPermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName: !GetAtt StartInstanceFunction.Arn
Principal: events.amazonaws.com
SourceArn: !GetAtt StartInstanceScheduleRule.Arn
Code language: YAML (yaml)
上記は起動用関数を呼び出すためのパーミッションですが、停止用も同様です。
EventBridgeルールとLambda関数と指定します。
SSM Automationランブックを使用する方法
メンテナンスウィンドウを設定して、SSM Automationランブックを定期的に実行します。
こちらの詳細については、以下のページをご確認ください。
なお今回は使用しませんが、EventBridgeルールを使用して、SSM Automationランブックを定期的に実行することもできます。
こちらについては、以下のページをご確認ください。
まずメンテナンスウィンドウを確認します。
Resources:
MaintenanceWindow1:
Type: AWS::SSM::MaintenanceWindow
Properties:
AllowUnassociatedTargets: true
Cutoff: 1
Description: StartInstance
Duration: 2
Name: !Sub "${Prefix}-MaintenanceWindow1"
Schedule: cron(0,10,20,30,40,50 * * * ? *)
ScheduleTimezone: Asia/Tokyo
Code language: YAML (yaml)
上記は起動用のウィンドウですが、停止用も同様です。
Scheduleプロパティでランブックを実行するタイミングを指定できます。
設定内容は1つ目の方法と同様です。
次にメンテナンスウィンドウのターゲットを確認します。
Resources:
MaintenanceWindowTarget1:
Type: AWS::SSM::MaintenanceWindowTarget
Properties:
Name: !Sub "${Prefix}-MaintenanceWindowTarget1"
ResourceType: INSTANCE
Targets:
- Key: InstanceIds
Values:
- !Ref Instance
WindowId: !Ref MaintenanceWindow1
Code language: YAML (yaml)
IDを指定して、起動・停止する対象のインスタンスを定めます。
最後にメンテナンスウィンドウで実行するタスクを確認します。
Resources:
MaintenanceWindowTask1:
Type: AWS::SSM::MaintenanceWindowTask
Properties:
MaxConcurrency: 1
MaxErrors: 1
Name: !Sub "${Prefix}-MaintenanceWindowTask1"
Priority: 10
Targets:
- Key: WindowTargetIds
Values:
- !Ref MaintenanceWindowTarget1
TaskArn: AWS-StartEC2Instance
TaskInvocationParameters:
MaintenanceWindowAutomationParameters:
Parameters:
AutomationAssumeRole:
- !GetAtt SSMAutomationRole.Arn
InstanceId:
- "{{RESOURCE_ID}}"
TaskType: AUTOMATION
WindowId: !Ref MaintenanceWindow1
Code language: YAML (yaml)
起動用タスクとして、SSM AutomationランブックAWS-StartEC2Instanceを指定します。
停止用では、AWS-StopEC2Instanceを指定します。
以下がこのタスクを実行する上でのIAMロールです。
Resources:
SSMAutomationRole:
Type: AWS::IAM::Role
DeletionPolicy: Delete
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
Service:
- ssm.amazonaws.com
Policies:
- PolicyName: CreateImagePolicy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- ec2:StartInstances
- ec2:StopInstances
Resource: !Sub "arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:instance/${Instance}"
Code language: YAML (yaml)
インスタンスの起動/停止アクションを許可する内容です。
EventBridge Schedulerを使用する方法
Resources:
StartInstanceSchedule:
Type: AWS::Scheduler::Schedule
Properties:
Description: StartInstance
FlexibleTimeWindow:
Mode: "OFF"
Name: !Sub "${Prefix}-StartInstanceSchedule"
ScheduleExpression: cron(0,10,20,30,40,50 * * * ? *)
State: ENABLED
Target:
Arn: arn:aws:scheduler:::aws-sdk:ec2:startInstances
Input: !Sub '{"InstanceIds": ["${Instance}"]}'
RoleArn: !GetAtt SchedulerRole.Arn
StopInstanceSchedule:
Type: AWS::Scheduler::Schedule
Properties:
Description: StopInstance
FlexibleTimeWindow:
Mode: "OFF"
Name: !Sub "${Prefix}-StopInstanceSchedule"
ScheduleExpression: cron(5,15,25,35,45,55 * * * ? *)
State: ENABLED
Target:
Arn: arn:aws:scheduler:::aws-sdk:ec2:stopInstances
Input: !Sub '{"InstanceIds": ["${Instance}"]}'
RoleArn: !GetAtt SchedulerRole.Arn
Code language: YAML (yaml)
上が起動用、下が停止用のスケジュールです。
ポイントは2つです。
1つ目はScheduleExpressionプロパティです。
本プロパティでアクションをスケジュールできます。
こちらもcron式でスケジュールを定義します。
2つ目はTargetプロパティです。
Arnプロパティに実行するAPIオペレーションを指定します。
Arn — ターゲットとする API オペレーションを含む完全なサービス ARN を次の形式で示します
arn:aws:scheduler:::aws-sdk:service:apiAction
ユニバーサルターゲットの使用
EC2用のAPIは以下の記法で指定します。
arn:aws:scheduler:::aws-sdk:ec2:[apiAction]
今回の要件では、起動はstartInstances、停止はstopInstancesを実行します。
Inputプロパティに両APIを呼び出すための引数を指定します。
EventBridge スケジューラがターゲット API に送信するリクエストパラメータで指定する、整形された JSON。
ユニバーサルターゲットの使用
以下のページを見ると、両APIを呼び出す際は、InstanceIdsに対象のインスタンスのIDを渡せば良いことがわかります。
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/APIReference/API_StartInstances.html
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/APIReference/API_StopInstances.html
本プロパティにJSON形式でインスタンスIDを渡します。
RoleArnプロパティは以下の通り説明されています。
RoleArn— ターゲットに使用したい実行ロールの ARN。指定する実行ロールには、スケジュールの対象とする API オペレーションを呼び出す権限が必要です。
ユニバーサルターゲットの使用
以下のIAMロールを指定します。
Resources:
SchedulerRole:
Type: AWS::IAM::Role
DeletionPolicy: Delete
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
Service:
- scheduler.amazonaws.com
Policies:
- PolicyName: CreateImagePolicy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- ec2:StartInstances
- ec2:StopInstances
Resource: !Sub "arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:instance/${Instance}"
Code language: YAML (yaml)
インスタンスの起動/停止アクションを許可する内容です。
環境構築
CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。
CloudFormationスタックを作成し、スタック内のリソースを確認する
CloudFormationスタックを作成します。
スタックの作成および各スタックの確認方法については、以下のページをご確認ください。
各スタックのリソースを確認した結果、今回作成された主要リソースの情報は以下の通りです。
- 1つ目の構成
- EC2インスタンス1:i-0b9e6d15e61ee820c
- Lambda関数1:saa-04-004-StartInstance
- Lambda関数2:saa-04-004-StopInstance
- EventBridgeルール1:saa-04-004-LambdaStack-HT-StartInstanceScheduleRul-RUFGLCMCBAMB
- EventBridgeルール2:saa-04-004-LambdaStack-HT-StopInstanceScheduleRule-Q509A3N87K0U
- 2つ目の構成
- EC2インスタンス2:i-0c9f61ea77b7a59e4
- メンテナンスウィンドウ1:mw-01d6f438ebac17258
- メンテナンスウィンドウ2:mw-03e3e07dfcdd5a7fb
- 3つ目の構成
- EC2インスタンス3:i-0e54794687e579ba1
- EventBridge Scheduler 1:saa-04-004-StartInstanceSchedule
- EventBridge Scheduler 2:saa-04-004-StopInstanceSchedule
準備が整いましたので、AWS Management Consoleから各リソースを確認します。
1つ目の構成
Lambda関数を確認します。
両関数が正常に作成されていることがわかります。
EventBridgeを確認します。
5分ずれで、10分おきに関数を実行するルールが作成されています。
2つ目の構成
メンテナンスウィンドウの作成状況を確認します。
起動用のメンテナンスウィンドウです。
停止用のメンテナンスウィンドウです。
どちらも正常に作成されています。
3つ目の構成
起動用のスケジューラです。
停止用のスケジューラです。
どちらも正常に作成されています。
動作確認
準備が整いましので、EC2インスタンスを確認します。
1台目
2台目
3台目
確かに3台とも5分おきに起動と停止を繰り返します。
3つの全ての方法で、インスタンスを定期的に起動・停止させることができました。
まとめ
EC2インスタンスを定期的に起動・停止する方法を3つご紹介しました。