はじめに
この記事では、AWS Step Functionsを使用してECSコンテナを開始し、10秒後に自動的に停止させるプロセスを解説します。このプロジェクトには、IAM、ECR、CloudTrail、EventBridge、Step Functions、Lambdaなどの主要なAWSサービスを利用します。
作成したレポジトリーはこちらです。
こちらの記事を参考にしました。
目的
このプロジェクトの目的は、AWS Step Functionsを用いて、ECSコンテナのライフサイクルを効率的に管理し、自動化することです。特に、コンテナを起動してから10秒後に自動で停止させることで、リソースの効率的な使用と運用の簡素化を図ります。
機能一覧
- ECSコンテナの開始と停止: Step Functionsを使用してECSコンテナを開始し、10秒後に自動的に停止させます。
- インフラ管理: IAM、ECR、CloudTrail、EventBridge、Step Functions、Lambdaを利用したインフラ構成。
- ログとトラッキング: CloudTrailを使用して操作の監査ログを取得し、EventBridgeでイベントをトリガーします。
インフラ構成
- IAM: IAMポリシーとロールを設定し、Step FunctionsとLambdaが必要な権限を持つようにします。
- ECR: ECSで使用するコンテナイメージを格納するためのリポジトリを設定します。
- ECS: コンテナの実行環境としてECSクラスターを設定します。
- CloudTrail: すべてのAPIコールを監査し、セキュリティとコンプライアンスを強化します。
- EventBridge: 特定のイベントをトリガーし、Step Functionsを起動します。
- Step Functions: ECSコンテナの開始と停止を管理するステートマシンを設定します。
- Lambda: Step Functionsのタスクとして使用され、コンテナの停止を実行します。
実装方法
この記事では、AWS Step Functionsを使用してECSコンテナを開始し、10秒後に停止させる自動化プロセスの実装方法について詳しく説明します。
iam
以下に、必要なIAMロールおよびポリシーのTerraformコードを示します。
resource "aws_iam_role" "ecs_role" {
name = "ecs_role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid : ""
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ecs-tasks.amazonaws.com"
}
},
]
})
tags = {
Name = "${var.app_name}-ecs-iam-role"
}
}
resource "aws_iam_role" "lambda_role" {
name = "lambda_role"
assume_role_policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Action = "sts:AssumeRole",
Effect = "Allow",
Principal = {
Service = "lambda.amazonaws.com",
},
},
],
})
tags = {
Name = "${var.app_name}-lambda-iam-role"
}
}
resource "aws_iam_role" "cloudtrail_role" {
name = "CloudTrail_CloudWatchLogs_Role"
assume_role_policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Principal = {
Service = "cloudtrail.amazonaws.com"
},
Action = "sts:AssumeRole"
}
]
})
}
# IAM Role for Step Functions
resource "aws_iam_role" "step_functions_role" {
name = "stepFunctionsRole"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "states.amazonaws.com"
}
},
]
})
}
# IAM Role for EventBridge to Step Functions
resource "aws_iam_role" "eventbridge_role" {
name = "EventBridgeToStepFunctionsRole"
assume_role_policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Principal = {
Service = "events.amazonaws.com"
},
Action = "sts:AssumeRole"
}
]
})
}
resource "aws_iam_policy" "ecr_policy" {
name = "${var.app_name}-ecr-policy"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = [
"ecr:*"
]
Effect = "Allow"
Resource = "*"
},
]
})
}
resource "aws_iam_policy" "cloudwatch_policy" {
name = "${var.app_name}-cloudwatch-policy"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = [
"logs:*"
]
Effect = "Allow"
Resource = "arn:aws:logs:*:*:*"
},
]
})
}
resource "aws_iam_policy" "cloudtrail_policy" {
name = "CloudTrail_CloudWatchLogs_Policy"
policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Action = [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
Resource = "arn:aws:logs:*:*:log-group:${var.webserver_log_group_name}:log-stream:*"
}
]
})
}
resource "aws_iam_policy" "lambda_policy" {
name = "lambda_policy"
policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Action = [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"ecs:StopTask",
],
Resource = "*",
Effect = "Allow"
}
]
})
}
resource "aws_iam_policy" "lambda_invoke_policy" {
name = "lambda-invoke-policy"
description = "Policy to allow stepFunctionsRole to invoke Lambda functions"
policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Action = "lambda:InvokeFunction",
Resource = "arn:aws:lambda:${var.region}:${local.account_id}:function:lambda_function"
}
]
})
}
resource "aws_iam_policy" "eventbridge_to_stepfunctions_policy" {
name = "EventBridgeToStepFunctionsPolicy"
policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Action = [
"states:StartExecution"
],
Resource = "${var.sfn_ecs_stop_state_machine_arn}"
}
]
})
}
resource "aws_iam_policy_attachment" "ecr_attach" {
name = "${var.app_name}-ecr-attach"
roles = ["${aws_iam_role.ecs_role.name}"]
policy_arn = aws_iam_policy.ecr_policy.arn
}
resource "aws_iam_policy_attachment" "cloudwatch_attach" {
name = "${var.app_name}-cloudwatch-attach"
roles = ["${aws_iam_role.ecs_role.name}"]
policy_arn = aws_iam_policy.cloudwatch_policy.arn
}
resource "aws_iam_policy_attachment" "lambda_role_attachment" {
name = "${var.app_name}-lambda-attach"
roles = ["${aws_iam_role.lambda_role.name}"]
policy_arn = aws_iam_policy.lambda_policy.arn
}
resource "aws_iam_policy_attachment" "cloudtrail_role_attachment" {
name = "${var.app_name}-cloudtrail-attach"
roles = ["${aws_iam_role.cloudtrail_role.name}"]
policy_arn = aws_iam_policy.cloudtrail_policy.arn
}
resource "aws_iam_role_policy_attachment" "step_functions_policy_attachment" {
role = aws_iam_role.step_functions_role.name
policy_arn = "arn:aws:iam::aws:policy/AWSStepFunctionsFullAccess"
}
resource "aws_iam_role_policy_attachment" "step_functions_to_lambda_policy_attachment" {
role = aws_iam_role.step_functions_role.name
policy_arn = aws_iam_policy.lambda_invoke_policy.arn
}
resource "aws_iam_role_policy_attachment" "eventbridge_to_stepfunctions_policy_attachment" {
role = aws_iam_role.eventbridge_role.name
policy_arn = aws_iam_policy.eventbridge_to_stepfunctions_policy.arn
}
これらのIAMロールおよびポリシーは、ECSタスク、Lambda関数、CloudTrail、Step Functions、およびEventBridgeが必要な権限を持つように設定されています。次のステップでは、ECRリポジトリの設定、ECSタスク定義、Step Functionsステートマシン、EventBridgeルール、およびLambda関数を設定します。
CloudTrail
AWS CloudTrailは、AWSアカウントのAPIコールやその他のイベントを記録し、ログファイルをAmazon S3バケットに保存します。これにより、AWSリソースの操作を監査し、コンプライアンスを維持することができます。
resource "aws_cloudtrail" "main" {
name = "my-cloudtrail"
s3_bucket_name = var.s3_bucket_id
include_global_service_events = true
s3_key_prefix = "prefix"
enable_logging = true
cloud_watch_logs_group_arn = "${var.ecs_cloudwatch_logs_group_arn}:*"
cloud_watch_logs_role_arn = var.iam_role_arn_for_cloudtrail
}
CloudTrailが適切に設定され、ECSコンテナのイベントを監査し、CloudWatchログに記録する準備が整いました。詳細な設定手順については、各サービスの公式ドキュメントを参照してください。
EventBridge
Amazon EventBridgeは、異なるAWSサービス間でイベントをルーティングするためのイベントバスサービスです。ここでは、EventBridgeを使用してECSタスクの状態変化を監視し、Step Functionsステートマシンをトリガーする方法について説明します。
EventBridgeルールの設定
ECSタスクの状態が「RUNNING」に変わった時にStep Functionsをトリガーするルールを作成します。
resource "aws_cloudwatch_event_rule" "ecs_task_start_rule" {
name = "ecsTaskStartRule"
description = "Rule to trigger Step Functions on ECS task start"
event_pattern = jsonencode({
source = ["aws.ecs"]
detail-type = ["ECS Task State Change"]
detail = {
lastStatus = ["RUNNING"]
}
})
}
EventBridgeターゲットの設定
次に、上記のルールにマッチしたイベントが発生した時に、Step Functionsステートマシンをトリガーするターゲットを設定します。
# EventBridge Target
resource "aws_cloudwatch_event_target" "ecs_task_start_target" {
rule = aws_cloudwatch_event_rule.ecs_task_start_rule.name
target_id = "ecsTaskStartTarget"
arn = var.sfn_ecs_stop_state_machine_arn
role_arn = var.iam_role_arn_for_eventbrifge
}
step functiion
AWS Step Functionsは、複数のAWSサービスを連携させ、ビジネスプロセスを自動化するためのワークフローを構築するためのサービスです。ここでは、Step Functionsを使用してECSタスクを停止するためのステートマシンを設定する方法について説明します。
Step Functionsステートマシンの作成
ECSタスクを停止するためのステートマシンを作成します。このステートマシンは、10秒の待機ステートの後にLambda関数を呼び出してECSタスクを停止します。
# Step Functions State Machine
resource "aws_sfn_state_machine" "ecs_stop_state_machine" {
name = "ecsStopStateMachine"
role_arn = var.iam_role_arn_for_stepfunction
definition = <<DEFINITION
{
"Comment": "State machine to stop ECS task",
"StartAt": "Wait",
"States": {
"Wait": {
"Type": "Wait",
"Seconds": 10,
"Comment": "10s",
"Next": "StopTask"
},
"StopTask": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "${var.lambda_function_arn}",
"Payload": {
"Cluster": "${var.ecs_cluster_arn}",
"Task": "$.TaskArn"
}
},
"End": true
}
}
}
DEFINITION
}
これで、ECSタスクを停止するためのStep Functionsステートマシンの設定が完了しました。
Lambda
AWS Lambdaは、コードをサーバーレスで実行するためのサービスです。ここでは、AWS Lambda関数を作成し、ECSタスクを停止するための設定方法について説明します。
以下に、必要なTerraformコードとLambda関数のPythonコードを示します。
data "archive_file" "lambda" {
type = "zip"
source_dir = "./modules/lambda/src/in"
output_path = "./modules/lambda/src/out/lambda_function_payload.zip"
}
resource "aws_lambda_function" "main" {
filename = "./modules/lambda/src/out/lambda_function_payload.zip"
function_name = var.lambda_function_name
description = "lambda_function"
role = var.iam_role_arn_for_lambda
architectures = ["x86_64"]
handler = "index.lambda_handler"
source_code_hash = data.archive_file.lambda.output_base64sha256
timeout = 30
runtime = "python3.9"
depends_on = [aws_cloudwatch_log_group.lambda]
environment {
variables = {
CLUSTER_ARN = var.ecs_cluster_arn
CLUSTER_NAME = var.ecs_cluster_name
TASK_ARN = var.ecs_task_arn
}
}
tags = {
Name = "${var.app_name}-lamdba"
}
}
ECSタスクを停止するためのLambda関数のコードです。
import boto3
import os
def lambda_handler(event, context):
ecs = boto3.client('ecs')
cluster_arn = os.environ['CLUSTER_ARN']
cluster_name = os.environ['CLUSTER_NAME']
task_definition_arn = os.environ['TASK_ARN']
try:
# ECSタスクの一覧を取得
response = ecs.list_tasks(cluster=cluster_name, taskDefinition=task_definition_arn)
task_arns = response['taskArns']
# タスクを停止
for task_arn in task_arns:
ecs.stop_task(
cluster=cluster_arn,
task=task_arn,
reason='Stopped by Lambda function after 10 seconds'
)
return {
'statusCode': 200,
'body': {
'message': 'Tasks stopped successfully',
'task_arns': task_arns
}
}
except Exception as e:
return {
'statusCode': 500,
'body': {
'message': str(e)
}
}
EventBridgeがLambda関数を呼び出すための権限を設定します。
# Permissions for EventBridge to invoke Step Functions
resource "aws_lambda_permission" "allow_eventbridge_invoke" {
statement_id = "AllowExecutionFromEventBridge"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.main.function_name
principal = "events.amazonaws.com"
source_arn = var.cloudwatch_ecs_task_start_rule_arn
}
Lambda関数のログをCloudWatchに記録するためのロググループを作成します。
resource "aws_cloudwatch_log_group" "lambda" {
name = "/aws/lambda/${var.lambda_function_name}"
}
これで、ECSタスクを停止するためのLambda関数の設定が完了しました。
結果
step functionsの画面で以下の画面が作られました。
実行結果です。
まとめ
この記事では、AWS Step Functionsを利用してECSコンテナを開始し、10秒後に停止させる方法を紹介しました。このインフラ構成を通じて、リソースの効率的な管理と運用の自動化が可能になります。詳細な設定手順については、各サービスの公式ドキュメントを参照してください。