コスト削減のため、開発環境のECS Serviceを夜間と休日は停止する際に行った内容のメモです
AuroraやEC2を夜間に停止するというのは、以下の SSM Automation を EventBridge 経由で実行することで可能ですが、ECS Serviceは対応するAutomationがないため SSM Document を自作してみました
SSM Documentの作成
SSM Document でAWS APIを実行するため以下の Automation Action を使用します
ECS Service を停止するためにはDesired Countを 0 にすることで停止できるため、UpdateService でDesiredCount だけを変更するようにします
parameters で受け取る引数と mainSteps で実際の処理を書きます
resource "aws_ssm_document" "start_stop_ecs_service" {
name = "StartStopECSService"
document_format = "YAML"
document_type = "Automation"
content = <<EOT
description: ecs service start and stop automation runbook
schemaVersion: '0.3'
parameters:
EcsClusterName:
type: String
EcsServiceName:
type: String
DesiredCount:
type: Integer
default: 1
mainSteps:
- name: ECS
action: 'aws:executeAwsApi'
inputs:
Service: ecs
Api: UpdateService
cluster: '{{ EcsClusterName }}'
service: '{{ EcsServiceName }}'
desiredCount: '{{ DesiredCount }}'
EOT
}
SSM Automationを実行するIAMロールを作成
EventBridge から実行させるためのIAMロールを作成します
data "aws_iam_policy_document" "events" {
statement {
actions = ["sts:AssumeRole"]
effect = "Allow"
principals {
type = "Service"
identifiers = [
"events.amazonaws.com"
]
}
}
}
resource "aws_iam_role" "ecs_automation" {
name = "ecs-automation"
path = "/"
assume_role_policy = data.aws_iam_policy_document.events.json
}
上記のIAMロールに SSM Document を実行する権限とECSの UpdateService の権限を付与します
data "aws_iam_policy_document" "ecs_stop_start" {
statement {
actions = [
"ecs:Describe*",
"ssm:*"
]
effect = "Allow"
resources = ["*"]
}
statement {
actions = [
"ecs:UpdateService"
]
effect = "Allow"
resources = [
(停止させたいECS ServiceのARN)
]
}
}
resource "aws_iam_role_policy" "ecs_stop_start" {
name = "ecs-stop-start"
role = aws_iam_role.ecs_automation.name
policy = data.aws_iam_policy_document.ecs_stop_start.json
}
EventBridgeの作成
月曜~金曜の22時に ECS Service を停止します
aws_ecs_service の cluster はARNなので split でCluster名を取得しています
resource "aws_cloudwatch_event_rule" "ecs_service_stop" {
name = "ecs-service-stop"
schedule_expression = "cron(0 13 * * MON-FRI *)"
is_enabled = true
}
resource "aws_cloudwatch_event_target" "ecs_service_stop" {
target_id = aws_ecs_service.api.name
rule = aws_cloudwatch_event_rule.ecs_service_stop.name
arn = "${aws_ssm_document.ecs_service_stop_start.arn}:$DEFAULT"
role_arn = aws_iam_role.ecs_automation.arn
input = <<EOT
{
"EcsClusterName": ["${split("/", aws_ecs_service.api.cluster)[-1]}"],
"EcsServiceName": ["${aws_ecs_service.api.name}"],
"DesiredCount": ["0"]
}
EOT
}
月曜~金曜の9時30分に ECS Service を起動させます
resource "aws_cloudwatch_event_rule" "ecs_service_start" {
name = "ecs-service-start"
schedule_expression = "cron(30 0 ? * * *)"
is_enabled = true
resource "aws_cloudwatch_event_target" "ecs_service_start" {
target_id = aws_ecs_service.api.name
rule = aws_cloudwatch_event_rule.ecs_service_start.name
arn = "${aws_ssm_document.ecs_service_stop_start.arn}:$DEFAULT"
role_arn = aws_iam_role.ecs_automation.arn
input = <<EOT
{
"EcsClusterName": ["${split("/", aws_ecs_service.api.cluster)[-1]}"],
"EcsServiceName": ["${aws_ecs_service.api.name}"],
"DesiredCount": ["1"]
}
EOT
}
応用として
今回は開発環境を停止するという用途でしたが、本番環境で深夜のようなほぼアクセスが来ない状況では desiredCount を 1 にするということや、特定の時間帯だけ増やすということが可能になりそうです
スケジュールで desiredCount を増減するというのは AotoScaling で可能でありますが、この手順のほうがお手軽かなと個人的には思います