LoginSignup
1
1

ECSでnginxを構築する

Posted at

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"

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1