Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mapping multiple containers to an application load balancer in Terraform

I'm using Terraform to set up a container cluster on the AWS cloud using ECS and running into a problem with mapping a load balancer to direct traffic to more than one container running on the cluster.

To simplify, assume I'm running a service/task with two containers: webrequester listening on 5600 and restserver listening on 5000. I need traffic requesting /api/* to go to the restserver on port 5000 and default traffic to go to webrequester on 5600. This should work regardless of the number of container host instances I'm running.

My best understanding is that I need to create an aws_alb_target_group_attachment to make this association and set its target_id to the Container ID. Is this correct? If so, how do I get the container ID inside of Terraform? I've created a cluster, service, task, application load balancer, and all the required entities to get them running, but I can't figure out which, if any of them, will let me access the container ids.

Below is what I think are the germane parts of the Terraform scripts:

resource "aws_ecs_cluster" "main" {
  name = "jsapps-am${var.am_number}${var.cluster_iteration}"
}

resource "aws_alb_target_group" "https_default" {
  name     = "https-default-tg"
  port     = 443
  protocol = "HTTPS"
  vpc_id   = "${var.vpc_id}"
}

resource "aws_alb" "main" {
  name            = "af-${var.am_number}${var.cluster_iteration}-alb"
  subnets         = ["${var.vpc_subnets}"]
  security_groups = ["${aws_security_group.lb_sg.id}"]
}

resource "aws_alb_listener" "front_end" {
  load_balancer_arn = "${aws_alb.main.id}"
  port              = "443"
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-2015-05"
  certificate_arn   = "${var.https_certificate_arn}"

  default_action {
    target_group_arn = "${aws_alb_target_group.https_default.id}"
    type             = "forward"
  }
}

data "template_file" "task_definition" {
  template = "${file("${path.module}/task-definition.json")}"

  vars {
    image_url        = "${var.task_url}"
    container_name   = "webrequester"
    port_num         = "${var.webrequester_port}"
  }
}

resource "aws_ecs_task_definition" "jsapps" {
  family                = "jsapps_taskdef"
  container_definitions = "${data.template_file.task_definition.rendered}"
}

resource "aws_ecs_service" "jsapps" {
  name            = "jsapps-svc"
  cluster         = "${aws_ecs_cluster.main.id}"
  task_definition = "${aws_ecs_task_definition.jsapps.arn}"
  desired_count   = 1
  iam_role        = "${aws_iam_role.ecs_service.name}"

  load_balancer {
    target_group_arn = "${aws_alb_target_group.https_default.id}"
    container_name   = "webrequester"
    container_port   = "${var.webrequester_port}"
  }

  depends_on = [
    "aws_iam_role_policy.ecs_service",
    "aws_alb_listener.front_end",
  ]
}
like image 326
Yes - that Jake. Avatar asked Jan 04 '23 16:01

Yes - that Jake.


1 Answers

In the preferred setup you would attach a dynamic host port to your container running on an ECS container instance. The reason is simple: you want to have the flexibility to run multiple containers that expose the same port on a single instance. If you use a static host port to bind to, this will not work (because the port is taken). In case of a blue/green deployment, this sits in the way.

You do this by defining only your container port in your task definition. So this part is all the port mapping you need:

"portMappings": [
  {
    "containerPort": ${service_port}
  }
]

Your ALB of course needs to know which port to use, and since this port is assigned 'randomly' by Docker you have no way to configure this upfront. That's why you use a target group to which to send traffic from your loadbalancer. If you setup your load_balancer section of your aws_ecs_service properly, your ECS agent will register the container instance + (dynamic) port when a new container is started.

So... in your ALB setup you don't care about the ports on which the containers are listening, you only care which target groups you need to send traffic to. To do this, you add an aws_alb_listener with a default rule and an extra rule (with the /api/* path specified). For both rules you specify the right target group and type="forward" and the job is done.

To conclude: you don't need a aws_alb_target_group_attachment because those are handled by the ECS service (at runtime) when you setup the load balancer connection.

like image 145
Bram Avatar answered Jan 13 '23 20:01

Bram