概要
コストカットの観点で例えば平日のみECSを起動させたい、などのユースケースがあるかと思います
今回はEventBridge Schedulerを使って行う方法についてCloudFormationのテンプレートを交えて解説します
前提
- ECS Cluster及びECSタスクを作成済み
実装
以下がEventBridge Scheduler用のテンプレートです
# 本テンプレートの使用方法
# ECS Service の複数指定は不可のため、複数 Service のスケジュール設定を
# 行いたい場合は Service 単位で Stack をデプロイすること
AWSTemplateFormatVersion: 2010-09-09
Description: "EventBridge Scheduler Stack For ECS Auto Start And Stop"
# -------------------------------------
# Metadata
# -------------------------------------
Metadata:
AWS::CloudFormation::Interface:
# パラメータの並び順を記載
ParameterGroups:
- Label:
default: "Project Configuration"
Parameters:
- ProjectName
- Environment
- Label:
default: "EventBridge Scheduler Configuration"
Parameters:
- ECSClusterName
- ECSServiceName
- ECSTaskDesiredCount
- ScheduleStartTime
- ScheduleStopTime
- ScheduleTimeZone
- Label:
default: "SQS Configuration"
Parameters:
- MessageRetentionPeriod
- Label:
default: "CloudWatch For SNS Topic configuration"
Parameters:
- WarningTopicName
# -------------------------------------
# Parameters
# -------------------------------------
Parameters:
ProjectName:
Description: "Enter the project name. (ex: my-project)"
Type: String
MinLength: 1
ConstraintDescription: "ProjectName must be entered."
Default: my-project
Environment:
Description: "Select a environment name."
Type: String
AllowedValues:
- dev
- stg
ConstraintDescription: "Environment name must be entered."
ECSClusterName:
Description: "Enter the ECS cluster name. (ex: my-project-dev-cluster)"
Type: String
ECSServiceName:
Description: "Enter the ECS service name. (ex: my-project-dev-back-service)"
Type: String
ECSTaskDesiredCount:
Description: "Number of ECS Tasks to be started at any time."
Type: Number
Default: 1
ScheduleStartTime:
Description: "Enter the schedule to stop ECS with cron expression."
Type: String
Default: cron(0 7 ? * Mon-Fri *)
ScheduleStopTime:
Description: "Enter the schedule to start ECS with cron expression."
Type: String
Default: cron(0 22 ? * Mon-Fri *)
ScheduleTimeZone:
Description: "Enter the time zone for Eventbridge Scheduler. (default: Asia/Tokyo)"
Type: String
Default: Asia/Tokyo
MessageRetentionPeriod:
Description: "Enter the time to hold messages as a queue. (default: 3600)"
Type: Number
Default: 3600
MinValue: 60
MaxValue: 1209600
ConstraintDescription: "MessageRetentionPeriod must be entered between the values 60 - 1209600."
WarningTopicName:
Description: "Enter the SNS Topic name for warning notification."
Type: String
Default: warning
# -------------------------------------
# Resources
# -------------------------------------
Resources:
# -------------------------------------
# IAM Role
# -------------------------------------
EventBridgeRoleForECSAutoStartStop:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub EventBridgeRoleForECSAutoStartStop
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- scheduler.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: !Sub EventBridgeAccessForECSAutoStartStop
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- ecs:DescribeServices
- ecs:UpdateService
Resource: "*"
- Effect: Allow
Action:
- sqs:SendMessage
Resource: !GetAtt DeadLetterQueue.Arn
# -------------------------------------
# EventBridge
# -------------------------------------
# 自動起動スケジューラー
ECSStartSchedule:
Type: AWS::Scheduler::Schedule
Properties:
Name: !Sub ecs-auto-start-for-${ECSServiceName}
Description: "Scheduler to auto start ECS"
ScheduleExpression: !Ref ScheduleStartTime
ScheduleExpressionTimezone: !Ref ScheduleTimeZone
FlexibleTimeWindow:
Mode: "OFF"
State: ENABLED
Target:
Arn: arn:aws:scheduler:::aws-sdk:ecs:updateService
Input: !Sub |-
{
"Cluster": "${ECSClusterName}",
"Service": "${ECSServiceName}",
"DesiredCount": ${ECSTaskDesiredCount}
}
RoleArn: !GetAtt EventBridgeRoleForECSAutoStartStop.Arn
DeadLetterConfig:
Arn: !GetAtt DeadLetterQueue.Arn
ECSStopSchedule:
Type: AWS::Scheduler::Schedule
Properties:
Name: !Sub ecs-auto-stop-for-${ECSServiceName}
Description: "Scheduler to auto stop ECS"
ScheduleExpression: !Ref ScheduleStopTime
ScheduleExpressionTimezone: !Ref ScheduleTimeZone
FlexibleTimeWindow:
Mode: "OFF"
State: ENABLED
Target:
Arn: arn:aws:scheduler:::aws-sdk:ecs:updateService
Input: !Sub |-
{
"Cluster": "${ECSClusterName}",
"Service": "${ECSServiceName}",
"DesiredCount": 0
}
RoleArn: !GetAtt EventBridgeRoleForECSAutoStartStop.Arn
DeadLetterConfig:
Arn: !GetAtt DeadLetterQueue.Arn
# -------------------------------------
# SQS
# -------------------------------------
DeadLetterQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub dead-letter-queue-cloudwatch-alarm-auto-start-and-stop-for-backend
MessageRetentionPeriod: !Ref MessageRetentionPeriod
Tags:
- Key: ProjectName
Value: !Ref ProjectName
- Key: Environment
Value: !Ref Environment
DeadLetterQueuePolicy:
Type: AWS::SQS::QueuePolicy
Properties:
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
AWS: !GetAtt EventBridgeRoleForECSAutoStartStop.Arn
Action:
- sqs:SendMessage
- sqs:ReceiveMessage
- sqs:DeleteMessage
Resource: !GetAtt DeadLetterQueue.Arn
Queues:
- !Ref DeadLetterQueue
# -------------------------------------
# CloudWatch Alarm
# -------------------------------------
# DLQ にメッセージが入ったことを検知するアラーム
# ref: https://dev.classmethod.jp/articles/sqs-dead-letter-queue-metrics-for-alert/
ApproximateNumberOfMessagesVisibleOver1:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: !Sub
- Custom_${DeadLetterQueueName}_ApproximateNumberOfMessagesVisible-Over1
- { DeadLetterQueueName: !GetAtt DeadLetterQueue.QueueName }
AlarmDescription: !Sub "${ECSServiceName} の自動起動 / 停止スケジューラーが正常に実行できていないおそれがあります、${ECSServiceName} の稼働状況および EventBridge Scheduler の設定を確認してください"
Namespace: AWS/SQS
MetricName: ApproximateNumberOfMessagesVisible
Dimensions:
- Name: QueueName
Value: !GetAtt DeadLetterQueue.QueueName
AlarmActions:
- !Sub arn:aws:sns:${AWS::Region}:${AWS::AccountId}:${WarningTopicName}
ComparisonOperator: GreaterThanOrEqualToThreshold
EvaluationPeriods: 1
Period: 300
Statistic: Sum
Threshold: 1
1つずつ解説します
ロールの作成
- EventBridge用のECSタスクの詳細表示、更新用のロール
- 後述するデッドSQS用のメッセージの送信、削除、取得用のロール
を作成します
# -------------------------------------
# IAM Role
# -------------------------------------
EventBridgeRoleForECSAutoStartStop:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub EventBridgeRoleForECSAutoStartStop
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- scheduler.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: !Sub EventBridgeAccessForECSAutoStartStop
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- ecs:DescribeServices
- ecs:UpdateService
Resource: "*"
- Effect: Allow
Action:
- sqs:SendMessage
Resource: !GetAtt DeadLetterQueue.Arn
# -------------------------------------
# SQS
# -------------------------------------
DeadLetterQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub dead-letter-queue-cloudwatch-alarm-auto-start-and-stop-for-backend
MessageRetentionPeriod: !Ref MessageRetentionPeriod
Tags:
- Key: ProjectName
Value: !Ref ProjectName
- Key: Environment
Value: !Ref Environment
DeadLetterQueuePolicy:
Type: AWS::SQS::QueuePolicy
Properties:
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
AWS: !GetAtt EventBridgeRoleForECSAutoStartStop.Arn
Action:
- sqs:SendMessage
- sqs:ReceiveMessage
- sqs:DeleteMessage
Resource: !GetAtt DeadLetterQueue.Arn
Queues:
- !Ref DeadLetterQueue
DeadLetterQueue
EventBridge実行後、失敗したイベントをQueueに送信することができるので専用のQueue(デッドレターキュー)を作成します
# -------------------------------------
# SQS
# -------------------------------------
DeadLetterQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub dead-letter-queue-cloudwatch-alarm-auto-start-and-stop-for-backend
MessageRetentionPeriod: !Ref MessageRetentionPeriod
Tags:
- Key: ProjectName
Value: !Ref ProjectName
- Key: Environment
Value: !Ref Environment
DeadLetterQueuePolicy:
Type: AWS::SQS::QueuePolicy
Properties:
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
AWS: !GetAtt EventBridgeRoleForECSAutoStartStop.Arn
Action:
- sqs:SendMessage
- sqs:ReceiveMessage
- sqs:DeleteMessage
Resource: !GetAtt DeadLetterQueue.Arn
Queues:
- !Ref DeadLetterQueue
EventBridge Schedulerの作成
ECSタスクの起動、削除用のSchedulerを作成します
今回は以下の条件下での起動、停止を行います
- 平日7時から22時の間はタスクを1つ起動
- 平日7時から22時以外の時間帯(土日、営業時間外)にタスクを0にする
どちらも失敗時はDLQへメッセージが届くよう設定します
# -------------------------------------
# EventBridge
# -------------------------------------
# 自動起動スケジューラー
ECSStartSchedule:
Type: AWS::Scheduler::Schedule
Properties:
Name: !Sub ecs-auto-start-for-${ECSServiceName}
Description: "Scheduler to auto start ECS"
ScheduleExpression: !Ref ScheduleStartTime
ScheduleExpressionTimezone: !Ref ScheduleTimeZone
FlexibleTimeWindow:
Mode: "OFF"
State: ENABLED
Target:
Arn: arn:aws:scheduler:::aws-sdk:ecs:updateService
Input: !Sub |-
{
"Cluster": "${ECSClusterName}",
"Service": "${ECSServiceName}",
"DesiredCount": ${ECSTaskDesiredCount}
}
RoleArn: !GetAtt EventBridgeRoleForECSAutoStartStop.Arn
DeadLetterConfig:
Arn: !GetAtt DeadLetterQueue.Arn
ECSStopSchedule:
Type: AWS::Scheduler::Schedule
Properties:
Name: !Sub ecs-auto-stop-for-${ECSServiceName}
Description: "Scheduler to auto stop ECS"
ScheduleExpression: !Ref ScheduleStopTime
ScheduleExpressionTimezone: !Ref ScheduleTimeZone
FlexibleTimeWindow:
Mode: "OFF"
State: ENABLED
Target:
Arn: arn:aws:scheduler:::aws-sdk:ecs:updateService
Input: !Sub |-
{
"Cluster": "${ECSClusterName}",
"Service": "${ECSServiceName}",
"DesiredCount": 0
}
RoleArn: !GetAtt EventBridgeRoleForECSAutoStartStop.Arn
DeadLetterConfig:
Arn: !GetAtt DeadLetterQueue.Arn
実際に作成してみよう!
上記のCloudFormationテンプレートをデプロイします
以下のようにEventBridge Schedulerが作成され、平日以外の時間帯だとタスク数が0になったら成功です
参考