🦔
TerraformでECSタスクを自動停止する
やること
時間を指定してECSタスクを自動停止します。
おなじやり方で自動起動もできます。
全体像
EventBridgeでLambdaを起動してECSサービスのdesired count
を0にします。
Lambdaはコンテナイメージを使用します。
対応ディレクトリ
.
├── ecr.tf
├── event_bridge.tf
└── iam_roles.tf
└── modules
│ └── iam_role
│ ├── main.tf
│ └── variable.tf
├── lambda
│ ├── Dockerfile
│ ├── package.json
│ └── app.js
事前準備
Lambdaイメージ管理用のECR作成
./ecr.tf
resource "aws_ecr_repository" "lambda" {
name = "stop-ecr-lambda"
}
Lambdaイメージの作成
Lambdaコード
今回はNode.jsを使います。
./lambda/app.js
'use strict';
const AWS = require('aws-sdk');
const desiredCount = 0;
const cluster = 'your-ecs-cluster-name';
const service = 'your-ecs-service-name';
exports.handler = async function (event, context) {
const ecs = new AWS.ECS();
return await ecs
.updateService({
desiredCount,
cluster,
service
})
.promise();
};
ビルドしてECRにpush
こちらご参照ください => Lambda コンテナイメージの作成
./lambda/Dockerfile
FROM public.ecr.aws/lambda/nodejs:14
# Assumes your function is named "app.js", and there is a package.json file in the app directory
COPY app.js package.json ${LAMBDA_TASK_ROOT}
# Install NPM dependencies for function
RUN npm install
# Set the CMD to your handler (could also be done as a parameter override outside of the Dockerfile)
CMD [ "app.handler" ]
$ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin 012345678910.dkr.ecr.ap-northeast-1.amazonaws.com
$ docker build -t stop-ecr-lambda .
$ docker tag stop-ecr-lambda:latest 012345678910.dkr.ecr.ap-northeast-1.amazonaws.com/stop-ecr-lambda:latest
$ docker push 012345678910.dkr.ecr.ap-northeast-1.amazonaws.com/stop-ecr-lambda:latest
LambdaにアタッチするIAMロールの定義
LambdaにECSタスク数を制御できる権限を与えます。
./iam_roles.tf
data "aws_iam_policy_document" "ecs_startstop" {
statement {
effect = "Allow"
resources = ["*"]
actions = [
"ecs:DescribeServices",
"ecs:UpdateService"
]
}
statement {
effect = "Allow"
resources = ["arn:aws:logs:*:*:*"]
actions = [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
]
}
}
module "ecs_startstop_role" {
source = "./modules/iam_role"
name = "ecs_startstop"
identifier = "lambda.amazonaws.com"
policy = data.aws_iam_policy_document.ecs_startstop.json
}
./modules/iam_role/main.tf
resource "aws_iam_role" "default" {
name = var.name
assume_role_policy = data.aws_iam_policy_document.assume_role.json
}
data "aws_iam_policy_document" "assume_role" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = [var.identifier]
}
}
}
resource "aws_iam_policy" "default" {
name = var.name
policy = var.policy
}
resource "aws_iam_role_policy_attachment" "default" {
role = aws_iam_role.default.name
policy_arn = aws_iam_policy.default.arn
}
output "iam_role_arn" {
value = aws_iam_role.default.arn
}
output "iam_role_name" {
value = aws_iam_role.default.name
}
EventBridgeの作成
今回は平日の20:30(JST)にECSサービスを停止します。
cronの書き方はコンソールで操作してみるとわかりやすいです。
resource "aws_lambda_permission"
を使ってEventBridgeにLambdaの起動を許可する必要があるのが注意点です。参照
event_bridge.tf
locals {
stop_function_name = "lambda-ecs-stop-container"
}
resource "aws_lambda_function" "ecs_stop" {
function_name = local.stop_function_name
role = module.ecs_startstop_role.iam_role_arn
package_type = "Image"
image_uri = "${aws_ecr_repository.lambda.repository_url}:latest"
timeout = 60
lifecycle {
ignore_changes = [image_uri]
}
depends_on = [aws_cloudwatch_log_group.ecs_stop_lambda]
}
resource "aws_cloudwatch_log_group" "ecs_stop_lambda" {
name = "/aws/lambda/${local.stop_function_name}"
retention_in_days = 30
}
resource "aws_cloudwatch_event_rule" "ecs_stop" {
name = "ecs-stop"
description = "stop ecs task"
is_enabled = "true"
schedule_expression = "cron(30 11 ? * MON-FRI *)" # 月〜金 20:30(JST)に停止
}
resource "aws_cloudwatch_event_target" "ecs_stop" {
arn = aws_lambda_function.ecs_stop.arn
rule = aws_cloudwatch_event_rule.ecs_stop.name
target_id = "ecs-stop"
}
resource "aws_lambda_permission" "allow_cloudwatch_to_call_lambda_stop" {
statement_id = "AllowExecutionFromCloudWatch"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.ecs_stop.function_name
principal = "events.amazonaws.com"
source_arn = aws_cloudwatch_event_rule.ecs_stop.arn
}
Discussion