TerraformでECS構築
ref
作成するリソース
Cluster
ContainerInsightsの有効化
security group
ALBからの3000番ポートのみ許可
ECSTaskExecution
ECSServicePolicy
SSMReadOnlyAccess
CloudWatchAgentServer
TaskDef
LBの設定
awslogsに指定するCloudWatchLogGroup
SSMから秘匿値を読み取る
Service
Repository
コンテナイメージを置く所
脆弱性のスキャニングを有効化
Life Cycle Policy
イメージの量に応じて従量課金のため、定期的に消すことでコスト削減になる
適当に14日にした
別で作成してある想定
ネットワーク
VPC
ECSのプライベートサブネット
target group
security gruop
秘匿値をSSMに入れておく
code:variables.tf
variable "name" {}
variable "container_name" {}
variable "region" {}
variable "aws_account_id" {}
variable "vpc_id" {}
variable "vpc_cidr_block" {}
variable "lb_sg_id" {}
variable "lb_tg_arn" {}
variable "ecs_subnet_ids" {
type = list(string)
}
variable "ssm_value_from_keys" {
type = object({
db_host = string
db_port = string
db_user = string
db_password = string
db_name = string
})
}
code:main.tf
resource "aws_ecs_cluster" "main_app_ecs" {
name = "${var.name}-app-ecs"
# ContainerInsightsの有効化
setting {
name = "containerInsights"
value = "enabled"
}
}
resource "aws_security_group" "main_app_ecs_sg" {
name = "${var.name}-ecs-sg"
description = "Alow HTTP 3000 inbound traffic"
vpc_id = var.vpc_id
ingress {
description = "HTTP 3000 from lb"
from_port = 3000
to_port = 3000
protocol = "tcp"
security_groups = [
var.lb_sg_id
]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
}
}
resource "aws_ecr_repository" "main_app_ecr" {
name = "${var.name}-app"
image_tag_mutability = "MUTABLE"
# 脆弱性スキャニングの有効化
image_scanning_configuration {
scan_on_push = true
}
}
resource "aws_ecr_lifecycle_policy" "main_app_ecr_policy" {
repository = aws_ecr_repository.main_app_ecr.name
policy = <<EOF
{
"rules": [
{
"rulePriority": 1,
"description": "Expire images older than 14 days",
"selection": {
"tagStatus": "untagged",
"countType": "sinceImagePushed",
"countUnit": "days",
"countNumber": 14
},
"action": {
"type": "expire"
}
}
]
}
EOF
}
resource "aws_iam_role" "main_app_ecs_task_execution_role" {
name = "${var.name}-ecs-task-execution-role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": [
"ecs-tasks.amazonaws.com"
]
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "main_app_ecs_task_execution_policy" {
role = aws_iam_role.main_app_ecs_task_execution_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
}
resource "aws_iam_role_policy_attachment" "main_app_ecs_service_policy" {
role = aws_iam_role.main_app_ecs_task_execution_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceRole"
}
# SSMから秘匿値を取ってくるためのポリシー
resource "aws_iam_role_policy_attachment" "main_app_ecr_read_policy" {
role = aws_iam_role.main_app_ecs_task_execution_role.name
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMReadOnlyAccess"
}
# CloudWatchにログを吐き出すためのポリシー
resource "aws_iam_role_policy_attachment" "main_app_cloudwatch_agent_policy" {
role = aws_iam_role.main_app_ecs_task_execution_role.name
policy_arn = "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy"
}
resource "aws_ecs_task_definition" "main_app_ecs_task_def" {
family = "${var.name}-app-ecs-task-def"
cpu = "512"
memory = "1024"
network_mode = "awsvpc"
task_role_arn = aws_iam_role.main_app_ecs_task_execution_role.arn
execution_role_arn = aws_iam_role.main_app_ecs_task_execution_role.arn
container_definitions = <<EOF
[
{
"name": "${var.container_name}",
"image": "${var.aws_account_id}.dkr.ecr.${var.region}.amazonaws.com/${aws_ecr_repository.main_app_ecr.name}:latest",
"portMappings": [
{
"containerPort": 3000,
"protocol": "tcp"
}
],
"secrets": [
{
"name": "DB_HOST",
"valueFrom": "arn:aws:ssm:${var.region}:${var.aws_account_id}:parameter/${var.ssm_value_from_keys.db_host}"
},
{
"name": "DB_PORT",
"valueFrom": "arn:aws:ssm:${var.region}:${var.aws_account_id}:parameter/${var.ssm_value_from_keys.db_port}"
},
{
"name": "DB_USER",
"valueFrom": "arn:aws:ssm:${var.region}:${var.aws_account_id}:parameter/${var.ssm_value_from_keys.db_user}"
},
{
"name": "DB_PASSWORD",
"valueFrom": "arn:aws:ssm:${var.region}:${var.aws_account_id}:parameter/${var.ssm_value_from_keys.db_password}"
},
{
"name": "DB_NAME",
"valueFrom": "arn:aws:ssm:${var.region}:${var.aws_account_id}:parameter/${var.ssm_value_from_keys.db_name}"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "${var.name}-app-ecs",
"awslogs-region": "${var.region}",
"awslogs-stream-prefix": "${var.name}-app-ecs"
}
}
}
]
EOF
}
resource "aws_cloudwatch_log_group" "main_app_ecs_log_group" {
name = "${var.name}-app-ecs"
}
resource "aws_ecs_service" "main_app_ecs_service" {
name = "${var.name}-app-ecs-service"
cluster = aws_ecs_cluster.main_app_ecs.name
launch_type = "FARGATE"
task_definition = aws_ecs_task_definition.main_app_ecs_task_def.arn
# ここらへんの値は適当
health_check_grace_period_seconds = "60"
network_configuration {
subnets = var.ecs_subnet_ids
assign_public_ip = false
}
load_balancer {
target_group_arn = var.lb_tg_arn
container_name = var.container_name
container_port = "3000"
}
lifecycle {
# ecs-deploy等のツールで更新してもtask_defの更新を無視するように
ignore_changes = [
task_definition
]
}
}