nginxをECSで構築してみました。
├── auto-scale.tf
├── ecs.tf
├── load_balancer.tf
├── network.tf
├── outputs.tf
├── provider.tf
├── security.tf
├── templates
│ └── task-definition.json
├── terraform.tfstate
├── terraform.tfvars.example
└── variables.tf
/templete/task-definition.json
[
{
"name": "nginx",
"image": "${app_image}",
"cpu": ${app_cpu},
"memory": ${app_memory},
"networkMode": "awsvpc",
"portMappings": [
{
"containerPort": 80,
"hostPort": 80
}
]
}
]
auto-scale.tf
# Create an autoscaling based on the cluster and service
resource "aws_appautoscaling_target" "ecs_target" {
max_capacity = var.autoscale_config.max
min_capacity = var.autoscale_config.min
resource_id = "service/${aws_ecs_cluster.ecs.name}/${aws_ecs_service.service.name}"
scalable_dimension = "ecs:service:DesiredCount"
service_namespace = "ecs"
}
# =========== Target Scaling
# This is an example: app/EC2Co-EcsEl-1TKLTMITMM0EO/f37c06a68c1748aa/targetgroup/EC2Co-Defau-LDNM7Q3ZH1ZN/6d4ea56ca2d6a18d.
resource "aws_appautoscaling_policy" "target_scaling" {
name = "nginx-target-policy"
policy_type = "TargetTrackingScaling"
resource_id = aws_appautoscaling_target.ecs_target.resource_id
scalable_dimension = aws_appautoscaling_target.ecs_target.scalable_dimension
service_namespace = aws_appautoscaling_target.ecs_target.service_namespace
target_tracking_scaling_policy_configuration {
predefined_metric_specification {
predefined_metric_type = "ALBRequestCountPerTarget"
resource_label = "app/${aws_alb.lb.name}/${basename("${aws_alb.lb.id}")}/targetgroup/${aws_alb_target_group.target.name}/${basename("${aws_alb_target_group.target.id}")}"
}
target_value = 10
}
}
# # =========== Step Scaling
# # Scale up
# resource "aws_appautoscaling_policy" "up_policy" {
# name = "nginx_scale_up_policy"
# resource_id = aws_appautoscaling_target.ecs_target.resource_id
# scalable_dimension = aws_appautoscaling_target.ecs_target.scalable_dimension
# service_namespace = aws_appautoscaling_target.ecs_target.service_namespace
# step_scaling_policy_configuration {
# adjustment_type = "ChangeInCapacity"
# cooldown = var.autoscale_metric.cooldown
# metric_aggregation_type = "Maximum"
# step_adjustment {
# metric_interval_lower_bound = 0
# scaling_adjustment = var.autoscale_metric.up
# }
# }
# }
# # Scale down
# resource "aws_appautoscaling_policy" "down_policy" {
# name = "nginx-scale-down-policy"
# resource_id = aws_appautoscaling_target.ecs_target.resource_id
# scalable_dimension = aws_appautoscaling_target.ecs_target.scalable_dimension
# service_namespace = aws_appautoscaling_target.ecs_target.service_namespace
# step_scaling_policy_configuration {
# adjustment_type = "ChangeInCapacity"
# cooldown = var.autoscale_metric.cooldown
# metric_aggregation_type = "Maximum"
# step_adjustment {
# metric_interval_upper_bound = 0
# scaling_adjustment = var.autoscale_metric.down
# }
# }
# }
# # =========== Define metrics for the cpu
# # High
# resource "aws_cloudwatch_metric_alarm" "high_cpu_service" {
# alarm_name = "nginx-cpu-high"
# comparison_operator = "GreaterThanOrEqualToThreshold"
# period = var.autoscale_metric.period
# evaluation_periods = var.autoscale_metric.evaluation_periods
# metric_name = "CPUUtilization"
# namespace = "AWS/ECS"
# statistic = "Average"
# threshold = var.autoscale_metric.max_threshold
# dimensions = {
# ClusterName = aws_ecs_cluster.ecs.name
# ServiceName = aws_ecs_service.service.name
# }
# alarm_actions = [aws_appautoscaling_policy.up_policy.arn]
# }
# # Low
# resource "aws_cloudwatch_metric_alarm" "low_cpu_service" {
# alarm_name = "nginx-cpu-down"
# comparison_operator = "LessThanOrEqualToThreshold"
# period = var.autoscale_metric.period
# evaluation_periods = var.autoscale_metric.evaluation_periods
# metric_name = "CPUUtilization"
# namespace = "AWS/ECS"
# statistic = "Average"
# threshold = var.autoscale_metric.min_threshold
# dimensions = {
# ClusterName = aws_ecs_cluster.ecs.name
# ServiceName = aws_ecs_service.service.name
# }
# alarm_actions = [aws_appautoscaling_policy.down_policy.arn]
# }
ecs.tf
# =========== Creating aws ecs cluster
resource "aws_ecs_cluster" "ecs" {
name = "nginx-cluster"
}
# =========== Creating task definition
data "template_file" "nginx" {
template = file("./templates/task-definition.json")
vars = {
app_image = var.app_type.image
app_cpu = var.aws_launch_type.cpu
app_memory = var.aws_launch_type.memory
}
}
resource "aws_ecs_task_definition" "td" {
family = "nginx-task"
network_mode = "awsvpc"
requires_compatibilities = ["FARGATE"]
cpu = var.aws_launch_type.cpu
memory = var.aws_launch_type.memory
container_definitions = data.template_file.nginx.rendered
}
# =========== Creating service for the cluster
resource "aws_ecs_service" "service" {
name = "nginx-service"
cluster = aws_ecs_cluster.ecs.id
task_definition = aws_ecs_task_definition.td.arn
desired_count = var.app_type.count
launch_type = var.aws_launch_type.type
network_configuration {
security_groups = [aws_security_group.ecs.id]
subnets = aws_subnet.private.*.id
assign_public_ip = true
}
load_balancer {
target_group_arn = aws_alb_target_group.target.id
container_name = "nginx"
container_port = var.app_type.port
}
depends_on = [
aws_alb_listener.listen,
]
}
load_balancer.tf
# =========== Creating a load balancer
resource "aws_alb" "lb" {
name = "nginx-load-balancer"
subnets = aws_subnet.public.*.id
security_groups = [aws_security_group.lb.id]
}
# =========== Createing a target group
resource "aws_alb_target_group" "target" {
vpc_id = aws_vpc.main.id
name = "nginx-target-group"
port = var.app_type.port
protocol = "HTTP"
target_type = "ip"
health_check {
path = "/"
matcher = "200"
}
}
# =========== Redirect incoming traffic to target from lb
resource "aws_alb_listener" "listen" {
load_balancer_arn = aws_alb.lb.arn
port = var.app_type.port
default_action {
type = "forward"
target_group_arn = aws_alb_target_group.target.arn
}
}
network.tf
# =========== Get all available zones in current region
data "aws_availability_zones" "az" {
state = "available"
}
# =========== Creating vpc
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr_block
}
# =========== Creating 2 /24 private subnet
resource "aws_subnet" "private" {
vpc_id = aws_vpc.main.id
count = var.availability_zones_count
availability_zone = data.aws_availability_zones.az.names[count.index]
cidr_block = cidrsubnet(aws_vpc.main.cidr_block, 8, count.index)
}
# =========== Creating 2 /24 public subnet
# Adding availability_zones_count to differ the cidr numnetwork
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
count = var.availability_zones_count
availability_zone = data.aws_availability_zones.az.names[count.index]
cidr_block = cidrsubnet(aws_vpc.main.cidr_block, 8, var.availability_zones_count + count.index)
map_public_ip_on_launch = true
}
# Create gateway to make public accessible
resource "aws_internet_gateway" "gateway" {
vpc_id = aws_vpc.main.id
}
# =========== Create gateway for nat
# Elastic IP
resource "aws_eip" "nat" {
count = var.availability_zones_count
depends_on = [aws_internet_gateway.gateway]
vpc = true
}
# NAT
# Element needed to map to each private ip
resource "aws_nat_gateway" "gateway" {
count = var.availability_zones_count
subnet_id = element(aws_subnet.public.*.id, count.index)
allocation_id = element(aws_eip.nat.*.id, count.index)
}
# ============ Routing table
# Public
resource "aws_route" "internet" {
route_table_id = aws_vpc.main.main_route_table_id
destination_cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.gateway.id
}
# Private
resource "aws_route_table" "private" {
count = var.availability_zones_count
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = element(aws_nat_gateway.gateway.*.id, count.index)
}
}
# =========== Attaching routing table to the private subnet
resource "aws_route_table_association" "private" {
count = var.availability_zones_count
subnet_id = element(aws_subnet.private.*.id, count.index)
route_table_id = element(aws_route_table.private.*.id, count.index)
}
outputs.tf
# =========== Outputing the created dns server
output "alb_hostname" {
value = aws_alb.lb.dns_name
}
provider.tf
# =========== Creating AWS Provider
provider "aws" {
region = var.region
access_key = var.aws_access_key_id
secret_key = var.aws_secret_access_key
}
security.tf
# =========== Creating security group for load balancer
resource "aws_security_group" "lb" {
name = "nginx-load-balancer-security-group"
description = "Allow http to be accepted and forwared to 80"
vpc_id = aws_vpc.main.id
# inbound rule accept 80:80
ingress {
protocol = "tcp"
description = "Accepting 80 and forward to 80"
from_port = var.app_type.port
to_port = var.app_type.port
cidr_blocks = ["0.0.0.0/0"]
}
# outbound rule
egress {
protocol = "-1" # set to all
from_port = 0
to_port = 0
cidr_blocks = ["0.0.0.0/0"]
}
}
# =========== Creating security group for ecs
resource "aws_security_group" "ecs" {
name = "nginx-ecs-tasks-security-group"
description = "Limiting access of ecs to get from alb only"
vpc_id = aws_vpc.main.id
# inbound rule accept 80:80 for lb only
ingress {
protocol = "tcp"
description = "Accepting 80 and forward to 80"
from_port = var.app_type.port
to_port = var.app_type.port
security_groups = [aws_security_group.lb.id]
}
# outbound rule
egress {
protocol = "-1" # set to all
from_port = 0
to_port = 0
cidr_blocks = ["0.0.0.0/0"]
}
}
variables.tf
variable "aws_access_key_id" {
description = "Your aws access key id"
type = string
}
variable "aws_secret_access_key" {
description = "Your aws secret access key"
type = string
}
variable "region" {
description = "AWS region"
default = "us-east-1"
type = string
}
# What image you want to build
# How many container should be spawned
# port
variable "app_type" {
description = "Application and configuration"
type = object({
image = string
count = number
port = number
})
default = {
image = "nginx:latest"
count = 2
port = 80
}
}
variable "aws_launch_type" {
description = "ECS Launch type. (1vCPU = 1024, memory in MiB)"
type = object({
type = string
cpu = number
memory = number
})
default = {
type = "FARGATE"
cpu = 256
memory = 512
}
}
variable "availability_zones_count" {
description = "Many instance that will be created"
type = number
default = 2
}
variable "vpc_cidr_block" {
description = "CIDR block for your vpc"
type = string
default = "172.16.0.0/16" # 16 bit hosts, 2^16 which is maximal
}
variable "autoscale_config" {
description = "Configuration for app autoscaling target"
type = object({
min = number
max = number
})
default = {
min = 1
max = 4
}
}
variable "autoscale_metric" {
description = "Configuration for cloud metric alarm"
type = object({
period = string
cooldown = number
evaluation_periods = string
max_threshold = string
min_threshold = string
up = number
down = number
})
default = {
period = "120"
cooldown = 120
evaluation_periods = "3"
max_threshold = "80"
min_threshold = "10"
up = 1
down = -1
}
}
ここにリージョンとaws_access_key_idとaws_secret_access_key入れる。
terraform.tfvars.example
region = "prefered-region"
aws_access_key_id = "YOUR_AWS_ACCESS_KEY"
aws_secret_access_key = "YOUR_AWS_SECRET_KEY"