2
votes

I'm trying to create a ECS cluster with a service but I'm not able to setup the autoscaling, so no instances are started in the cluster:

service my_service was unable to place a task because no container instance met all of its requirements. Reason: No Container Instances were found in your cluster. For more information, see the Troubleshooting section.

This is my Terraform config (only relevant config):

Cluster and service

resource "aws_ecs_cluster" "my_cluster" {
  name = "my_cluster"
}

resource "aws_ecs_service" "my_service" {
  name            = "my_service"
  cluster         = "${aws_ecs_cluster.my_cluster.id}"
  task_definition = "${aws_ecs_task_definition.my_tf.arn}"
  desired_count   = 1
  iam_role        = "${aws_iam_role.ecs-service-role.id}"
}

Autoscaling

resource "aws_launch_configuration" "launch_config" {
  name_prefix          = "my_lc"
  image_id             = "${data.aws_ami.ubuntu.id}"
  instance_type        = "t2.micro"
  user_data            = "${data.template_file.user_data.rendered}"
  security_groups      = ["${aws_security_group.my_sg.id}"]
  iam_instance_profile = "${aws_iam_instance_profile.ecs-instance-profile.id}"

  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_autoscaling_group" "autoscaling_group" {
  name                 = "my_autoscaling_group"
  max_size             = 2
  min_size             = 1
  launch_configuration = "${aws_launch_configuration.launch_config.name}"
  vpc_zone_identifier  = ["${aws_subnet.public.id}"]
}


data "template_file" "user_data" {
  template = "${file("${path.module}/templates/user-data.sh")}"

  vars {
    cluster_name = "${aws_ecs_cluster.my_cluster.name}"
  }
}

templates/user-data.sh

#!/bin/bash

# ECS config
{
  echo "ECS_CLUSTER=${cluster_name}"
} >> /etc/ecs/ecs.config

start ecs

AMI

data "aws_ami" "ubuntu" {
  most_recent = true

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-trusty-14.04-amd64-server-*"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }

  owners = ["099720109477"]
}

As far as I know, cluster and Autoscaling Group are linked via user_data in Launch Configuration, but it seems it's not working.

What am I missing?

2
Which AMI are you using there? Is it a custom one that has the ECS agent installed?ydaetskcoR
No, it's just a standard ubuntu. I have edited the question with the AMI configHéctor
You need the ECS agent installed in the AMI for that user data to do anything. Either you can install and run the ECS agent via user data, bake that step into the AMI or use the Amazon Linux official ECS AMIs.ydaetskcoR
Also, 14.04 goes out of support next month. Is there really a reason you need to use such an ancient version, particularly if it's going to be a container host?ydaetskcoR
@Héctor I don't see any IAM role attached to EC2 ASG where it needs AmazonEC2ContainerServiceforEC2Role permission. did you make sure of this?.Imran

2 Answers

0
votes

You need one of the ECS Optimized AMIs: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-optimized_AMI.html

Or

You need to create your own AMI which is not that easy.

0
votes

In order for this to work, you are missing a key component. Cloudwatch Alarms

Cloudwatch will alert the autoscaling group to take action when needed. Example configuration:

This will create the necessary alarm to scale down when the machine is being underutilized. Tweak to your needs via the vars.

resource "aws_cloudwatch_metric_alarm" "alarm-cpu-down" {
  alarm_name          = "ecs-down"
  comparison_operator = "LessThanOrEqualToThreshold"
  evaluation_periods  = "${var.evaluation_periods}"
  metric_name         = "${var.metric_name}"
  namespace           = "${var.namespace}"
  period              = "${var.period_down}"
  statistic           = "${var.statistic}"
  threshold           = "${var.threshold_down}"
  dimensions          = "${map(var.dimension_name, var.dimension_value == "false" ? var.autoscaling_group_name[count.index] : var.dimension_value)}"

  alarm_description = "This metric monitors CPU utilization down"
  alarm_actions     = ["${aws_autoscaling_policy.down-scale.*.arn}"]
}

This will create the necessary alarm to scale up when a machine is needed (the CPUReservation(=What your services need) is bigger than what is available)

resource "aws_cloudwatch_metric_alarm" "alarm-cpu-up" {
  alarm_name          = "${var.environment}-${var.project}-${var.name}-${var.metric_name}-up${count.index}"
  comparison_operator = "GreaterThanOrEqualToThreshold"
  evaluation_periods  = "${var.evaluation_periods}"
  metric_name         = "${var.metric_name}"
  namespace           = "${var.namespace}"
  period              = "${var.period_up}"
  statistic           = "${var.statistic}"
  threshold           = "${var.threshold_up}"
  dimensions          = "${map(var.dimension_name, var.dimension_value == "false" ? var.autoscaling_group_name[count.index] : var.dimension_value)}"

  alarm_description = "This metric monitors CPU utilization up"
  alarm_actions     = ["${aws_autoscaling_policy.up-scale.*.arn}"]
}

These are the actual autoscaling policies. This is the action the cloudwatch alarm needs to perform, when the alarm is triggered.

resource "aws_autoscaling_policy" "up-scale" {   
    count = "${var.num_asg}"   
    name  = "${var.environment}-${var.project}-${var.name}-${var.metric_name}-up${count.index}" 
    autoscaling_group_name = "${var.autoscaling_group_name[count.index]}"  
    adjustment_type        = "${var.adjustment_type}"   
    policy_type            = "${var.policy_type}"   
    cooldown               = "${var.cooldown_up}"   
    scaling_adjustment     = "${var.adjustment_up}" 
}

resource "aws_autoscaling_policy" "down-scale" {   
    count = "${var.num_asg}"   
    name  = "${var.environment}-${var.project}-${var.name}-${var.metric_name}-down${count.index}" 
    autoscaling_group_name = "${var.autoscaling_group_name[count.index]}"  
    adjustment_type        = "${var.adjustment_type}"   
    policy_type            = "${var.policy_type}"   
    cooldown               = "${var.cooldown_down}"   
    scaling_adjustment     = "${var.adjustment_down}" 
}