Skip to content

Latest commit

 

History

History
485 lines (430 loc) · 23.7 KB

README.md

File metadata and controls

485 lines (430 loc) · 23.7 KB

Terraform AWS ECS service stack creation

GitHub tag (latest by date)

This module is for whole ECS service stack creation: service, task definition, container definition, alb listener rule, target group, route53 record, security group etc.

Important note:

  • Use connect_to_lb and service_domain to connect service container to load balancer and create route53 A record
  • Use vpc_cidr_block, route_53_zone_name, lb_dns_name only when you dont have previously created resources

Example

Single container

module "ecs_service" {
  source = "zahornyak/ecs-service/aws"

  environment        = "production"
  vpc_id             = "vpc-080fd3099892"
  vpc_cidr_block     = "10.0.0.0/16" # use when you dont have previously created vpc
  service_subnets    = ["subnet-0c264c7154cb", "subnet-09e0d8b22e2"]
  # assign_public_ip = true # if you are using public subnets
  cluster_name       = "production-cluster"
  route_53_zone_id   = "Z01006347593463S0ZFL7A2" # use when you dont have previously created Route53 zone
  route_53_zone_name = "example.com" 
  lb_arn             = "arn:aws:elasticloadbalancing:eu-central-1:1234567890:loadbalancer/app/plugin-development-alb/46555556595fd4b2"
  lb_listener_arn    = "arn:aws:elasticloadbalancing:eu-central-1:1234567890:listener/app/plugin-development-alb/46555556595fd4b2/83d6940f8c9f02db"
  lb_dns_name        = "my-loadbalancer-1234567890.us-west-2.elb.amazonaws.com" # use when you dont have previously created load balancer
  create_ssl         = true # requests ssl for service and attach it to listener rule

  service_name  = "backend"
  desired_count = 1

  container_definitions = {
    proxy = {
      service_domain   = "api-test"
      connect_to_lb    = true
      container_image  = "nginx:latest"
      container_name   = "backend"
      container_cpu    = 256
      container_memory = 256
      containerPort    = 80
      environment      = [
        {
          "name"  = "foo"
          "value" = "bar"
        }
      ]
    }
  }

  service_memory = 1024
  service_cpu    = 512
}

Multiple containers

module "ecs_service" {
  source  = "zahornyak/ecs-service/aws"

  environment     = "production"
  vpc_id          = "vpc-080fd3099892"
  service_subnets = ["subnet-0c264c7154cb", "subnet-09e0d8b22e2"]
  # assign_public_ip = true # if you are using public subnets
  cluster_name     = "production-cluster"
  route_53_zone_id = "Z01006347593463S0ZFL7A2"
  lb_arn           = "arn:aws:elasticloadbalancing:eu-central-1:1234567890:loadbalancer/app/plugin-development-alb/46555556595fd4b2"
  lb_listener_arn  = "arn:aws:elasticloadbalancing:eu-central-1:1234567890:listener/app/plugin-development-alb/46555556595fd4b2/83d6940f8c9f02db"
  create_ssl       = true # requests ssl for service and attach it to listener rule

  service_name  = "backend"
  desired_count = 1

  container_definitions = {
    proxy = {
      service_domain   = "api-test"
      connect_to_lb    = true
      container_image  = "nginx:latest"
      container_name   = "proxy"
      container_cpu    = 256
      container_memory = 256
      containerPort    = 80
      environment = [
        {
          "name"  = "foo"
          "value" = "bar"
        }
      ]
    }
    
    backend = {
      container_image  = "nginx:latest"
      container_name   = "backend"
      container_cpu    = 256
      container_memory = 256
      container_depends_on = [
        {
          containerName = "proxy"
          condition     = "START"
        }
      ]
      containerPort = 3000
      healthcheck = {
        retries     = 5
        command     = ["CMD-SHELL", "curl -f http://localhost:3000"]
        timeout     = 15
        interval    = 30
        startPeriod = 10
      }
      environment = [
        {
          "name"  = "foo"
          "value" = "bar"
        }
      ]
    }
    
    admin = {
      service_domain   = "api-worker"
      connect_to_lb    = true
      container_image  = "nginx:latest"
      container_name   = "worker"
      container_cpu    = 256
      container_memory = 256
      container_depends_on = [
        {
          containerName = "backend"
          condition     = "START"
        }
      ]
      containerPort = 3050
      healthcheck = {
        retries     = 5
        command     = ["CMD-SHELL", "curl -f http://localhost:3050"]
        timeout     = 15
        interval    = 30
        startPeriod = 10
      }
      environment = [
        {
          "name"  = "foo"
          "value" = "bar"
        }
      ]
    }
    }
  service_memory  = 1024
  service_cpu     = 512
}

No load balancer

module "ecs_service" {
  source = "zahornyak/ecs-service/aws"

  environment     = var.environment
  vpc_id          = var.vpc_id
  service_subnets = var.subnets
  vpc_cidr_block  = var.vpc_cidr_block

  # assign_public_ip = true # if you are using public subnets
  cluster_name = aws_ecs_cluster.ecs_cluster.name

  service_name  = "backend"
  desired_count = 1

  container_definitions = {
    proxy = {
      container_image  = "nginx:latest"
      container_name   = "proxy"
      container_cpu    = 256
      container_memory = 256
      containerPort    = 80
      environment      = [
        {
          "name"  = "foo"
          "value" = "bar"
        }
      ]
    }
    
    backend = {
      container_image      = "nginx:latest"
      container_name       = "backend"
      container_cpu        = 256
      container_memory     = 256
      container_depends_on = [
        {
          containerName = "proxy"
          condition     = "START"
        }
      ]
      containerPort = 3000
      healthcheck   = {
        retries     = 5
        command     = ["CMD-SHELL", "curl -f http://localhost:3000"]
        timeout     = 15
        interval    = 30
        startPeriod = 10
      }
      environment = [
        {
          "name"  = "foo"
          "value" = "bar"
        }
      ]
    }
  }

  service_memory = 1024
  service_cpu    = 512
}

Example of using environment valiables for containers(using ssm_secrets which creates ssm parameters and puts them into container definition)

module "ecs-service" {
  source  = "zahornyak/ecs-service/aws"
  # insert the 7 required variables here
  container_definitions = {
    proxy = {
      container_image  = "nginx:latest"
      container_name   = "proxy"
      container_cpu    = 256
      container_memory = 256
      containerPort    = 80
      environment      = [
        {
          "name"  = "foo"
          "value" = "bar"
        }
      ]
      ssm_secrets = {
        DEBUG = {
          value = "true"
        }
      }
    }
  }
}

Example of using environment valiables for containers(using ssm_env_file which parses and creates ssm parameters and puts them into container definition)

module "ecs-service" {
  source  = "zahornyak/ecs-service/aws"
  # insert the 7 required variables here
  container_definitions = {
    proxy = {
      container_image  = "nginx:latest"
      container_name   = "proxy"
      container_cpu    = 256
      container_memory = 256
      containerPort    = 80
      environment      = [
        {
          "name"  = "foo"
          "value" = "bar"
        }
      ]
      ssm_env_file = "./env"
    }
  }
}


.env example

LOG_LEVEL=verbose
LOG_TARGET=console
LOG_FORMAT=json

CRONJOB_ENABLED=true
DEPLOYMENT=develop

Autoscaling with scaling values

module "ecs-service" {
  source = "zahornyak/ecs-service/aws"
  # insert the 7 required variables here

  min_service_tasks = 1
  max_service_tasks = 6

  cpu_scaling_target_value = 40
  cpu_scale_in_cooldown    = 350
  cpu_scale_out_cooldown   = 200

  memory_scaling_target_value = 90
  memory_scale_in_cooldown    = 350
  memory_scale_out_cooldown   = 300
}

Autoscaling with scaling values (no memory or cpu scaling)

module "ecs-service" {
  source = "zahornyak/ecs-service/aws"
  # insert the 7 required variables here

  min_service_tasks = 1
  max_service_tasks = 6

  cpu_scaling_target_value = 40
  cpu_scale_in_cooldown    = 350
  cpu_scale_out_cooldown   = 200
  
}

Capacity provider strategy, ordered placement, placement_constraints strategy example configuration

module "ecs-service" {
  source = "zahornyak/ecs-service/aws"
  # insert the 7 required variables here

  capacity_provider_strategy = {
    main = {
      capacity_provider = "FARGATE_SPOT"
      base              = 1
      weight            = 1
    }
  }


  ordered_placement_strategy = {
    test = {
      type  = "binpack"
      field = "cpu"
    }
  }


  placement_constraints = {
    example = {
      type       = "memberOf"
      expression = "attribute:ecs.availability-zone in [us-west-2a, us-west-2b]"
    }
  }
}

Service discovery example

module "ecs-service" {
  source = "zahornyak/ecs-service/aws"
  # insert the 7 required variables here

  create_service_discovery = true
  discovery_registry_id    = "service_discovery_registry_id"
  
}

Requirements

Name Version
terraform >= 1.4
aws >= 4.37

Providers

Name Version
aws >= 4.37

Modules

Name Source Version
acm terraform-aws-modules/acm/aws ~> 3.3
ecs_task_exec_policy terraform-aws-modules/iam/aws//modules/iam-policy ~> 4.4
ecs_task_execution_role terraform-aws-modules/iam/aws//modules/iam-assumable-role ~> 4.4
ecs_task_policy terraform-aws-modules/iam/aws//modules/iam-policy ~> 4.4
ecs_task_role terraform-aws-modules/iam/aws//modules/iam-assumable-role ~> 4.4
env_variables zahornyak/multiple-ssm-parameters/aws 0.0.11
service_container_definition registry.terraform.io/cloudposse/ecs-container-definition/aws ~> 0.58
service_container_sg registry.terraform.io/terraform-aws-modules/security-group/aws ~> 4.3

Resources

Name Type
aws_appautoscaling_policy.target_tracking_scaling_cpu_service resource
aws_appautoscaling_policy.target_tracking_scaling_memory_service resource
aws_appautoscaling_target.service_scaling resource
aws_cloudwatch_log_group.service_logs resource
aws_ecs_service.service resource
aws_ecs_task_definition.service resource
aws_lb_listener_certificate.this resource
aws_lb_listener_rule.service resource
aws_lb_target_group.service resource
aws_route53_record.lb_records resource
aws_service_discovery_service.service resource
aws_caller_identity.current data source
aws_iam_policy_document.ecs_task_exec_policy data source
aws_iam_policy_document.ecs_task_policy data source
aws_lb.this data source
aws_region.current data source
aws_route53_zone.this data source
aws_vpc.this data source

Inputs

Name Description Type Default Required
assign_public_ip Assign_public_ip set true if you are using public subnets. bool false no
capacity_provider_strategy capacity_provider_strategy any {} no
cluster_name Name of the ECS Cluster. string n/a yes
container_definitions Custom container definitions. any {} no
cpu_scale_in_cooldown cpu scale_in_cooldown number null no
cpu_scale_out_cooldown cpu scale_out_cooldown number null no
cpu_scaling_target_value cpu_scaling target_value number null no
create_service_discovery creates service discovery service and connects in to ecs service bool false no
create_ssl defines if create ssl for services domains bool true no
deployment_circuit_breaker deployment_circuit_breaker configuration any {} no
deployment_maximum_percent deployment_maximum_percent. For example 200 will create twice more container and if everything is ok, deployment is succesfull. number 200 no
deployment_minimum_healthy_percent deployment_minimum_healthy_percent. number 100 no
deregistration_delay Deregistration delay for target group. number 5 no
desired_count Desired count for service. number null no
discovery_registry_id service discovery registry_id string null no
docker_volume docker volume any null no
efs_volume efs volume any null no
environment Environment name. For example 'production' string n/a yes
external_dns when you dont have route53 zone, you can use external dns any null no
health_check Custom healthcheck for target group. any null no
health_check_grace_period_seconds health_check_grace_period_seconds number null no
launch_type Launch type for service: 'FARGATE', 'EC2' etc. string "FARGATE" no
lb_arn Load balancer arn. string null no
lb_dns_name Load balancer dns name. Use only if you dont have previously created Load Balancer string null no
lb_listener_arn Listener arn for load balancer connection string null no
lb_zone_id load balancer zone id string null no
max_service_tasks Maximum service tasks. number null no
memory_scale_in_cooldown memory scale_in_cooldown number null no
memory_scale_out_cooldown memory scale_out_cooldown number null no
memory_scaling_target_value memory scaling_target_value number null no
min_service_tasks Minimum service tasks. number null no
network_mode Network mode for task. For example 'awsvpc' or 'bridge' etc. string "awsvpc" no
ordered_placement_strategy ordered_placement_strategy any {} no
parameter_prefix prefix for parameter store parameter. For example '/develop/service/'. So parameter 'DEBUG' will have '/develop/service/DEBUG' name on the parameter store string null no
placement_constraints placement_constraints any {} no
protocol_version target group protocol version string null no
requires_compatibilities Compatibilities for ECS task. Available: 'FARGATE', 'FARGATE_SPOT', 'EC2' etc. list(string)
[
"FARGATE"
]
no
retention_in_days retention_in_days number 60 no
route_53_zone_id Route 53 zone id. string null no
route_53_zone_name route 53 zone name. Use only when you dont have previously created Route53 zone string null no
runtime_platform runtime platform any null no
security_groups additional security_groups for service list(string) [] no
service_cpu CPU amount for the service. number n/a yes
service_memory Memory amount for the service. number n/a yes
service_name Name of the service. string n/a yes
service_subnets Subnets for service list(string) n/a yes
task_exec_role_policy_arns Additional policies to attach to task execution role of ECS container. list(string) [] no
task_role_policy_arns Additional policies to attach to task role of ECS container. list(string) [] no
tg_protocol target group protocol(for example 'HTTP' or 'TCP') string "HTTP" no
tg_target_type target group target type(ip or instance etc) string "ip" no
vpc_cidr_block cidr block for vpc. Use that variable when you dont have previously created VPC string null no
vpc_id VPC id. string n/a yes

Outputs

Name Description
acm_arn acm arn
cloudwatch_log_group_arns aws cloudwatch log group arns
container_definitions container definitions of your task definition
ecs_service_arn ecs_service_arn
ecs_service_name ecs service name
ecs_service_security_group_ids ecs service security group ids
ecs_task_definition_arn task definition arn
ecs_task_execution_role_arn ecs task execution role arn
ecs_task_policy_arn ecs task policy arn
ecs_task_role_arn ecs task role arn
lb_listener_certificate lb listener certificate
lb_listener_rule_arns load balancer listener rules arns
records_lb_names load balancers records names
service_container_sg_ids service container sg ids
target_group_arns target group arns