Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ECS and Application Load Balancer

Ive been looking for some information on Cloud Formation with regards to creating a stack with ECS and ELB (Application Load Balancer) but unable to do so.

I have created two Docker images each containing a Node.js microservice that listens on ports 3000 and 4000. How do I go about creating my stack with ECS and ELB as mentioned ? I assume the Application Load Balancer can be configured to listen to both these ports ?

A sample Cloud Formation template would really help.

like image 969
nixgadget Avatar asked Aug 27 '16 21:08

nixgadget


People also ask

Does ECS use load balancer?

Your Amazon ECS service can optionally be configured to use Elastic Load Balancing to distribute traffic evenly across the tasks in your service. When you use tasks sets, all the tasks in the set must all be configured to use Elastic Load Balancing or to not use Elastic Load Balancing.

What is difference between application and load balancer?

While a network load balancer simply forwards requests, application load balancing examines the application layer protocol data from the request header. This examination takes more time than network load balancing, but it enables the balancer to make a more informed decision of where to direct the request.

What is an application load balancer?

Application Load Balancer components A load balancer serves as the single point of contact for clients. The load balancer distributes incoming application traffic across multiple targets, such as EC2 instances, in multiple Availability Zones. This increases the availability of your application.


2 Answers

The Application Load Balancer can be used to load traffic across the ECS tasks in your service(s). The Application Load Balancer has two cool features that you can leverage; dynamic port mapping (port on host is auto-assigned by ECS/Docker) allowing you to run multiple tasks for the same service on a single EC2 instance and path-based routing allowing you to route incoming requests to different services depending on patterns in the URL path.

To wire it up you need first to define a TargetGroup like this

"TargetGroupService1" : {
  "Type" : "AWS::ElasticLoadBalancingV2::TargetGroup",
  "Properties" : {
    "Port": 10,
    "Protocol": "HTTP",
    "HealthCheckPath": "/service1",
    "VpcId": {"Ref" : "Vpc"}
  }
}

If you are using dynamic port mapping, the port specified in the target group is irrelevant since it will be overridden by the dynamically allocated port for each target.

Next you define a ListenerRule that defines the path that shall be routed to the TargetGroup:

"ListenerRuleService1": {
  "Type" : "AWS::ElasticLoadBalancingV2::ListenerRule",
  "Properties" : {
    "Actions" : [
      {
        "TargetGroupArn" : {"Ref": "TargetGroupService1"},
        "Type" : "forward"
      }
    ],
    "Conditions" : [
      {
        "Field" : "path-pattern",
        "Values" : [ "/service1" ]
      }
    ],
    "ListenerArn" : {"Ref": "Listener"},
    "Priority" : 1
  }
}

Finally you associate your ECS Service with the TargetGroup. This enable ECS to automatically register your task containers as targets in the target group (with the host port that you have configured in your TaskDefinition)

"Service1": {
  "Type" : "AWS::ECS::Service",
  "DependsOn": [
    "ListenerRuleService1"
  ],
  "Properties" : {
    "Cluster" : { "Ref" : "ClusterName" },
    "DesiredCount" : 2,
    "Role" : "/ecsServiceRole",
    "TaskDefinition" : {"Ref":"Task1"},
    "LoadBalancers": [
      {
        "ContainerName": "Task1",
        "ContainerPort": "8080",
        "TargetGroupArn" : { "Ref" : "TargetGroupService1" }
      }
    ]
  }
}  

You can find more details in a blog post I have written about this, see Amazon ECS and Application Load Balancer

like image 102
Thomas B. Avatar answered Oct 26 '22 15:10

Thomas B.


If you're interested in doing this via https://www.terraform.io/ here's an example for two apps that share a domain:

  • https://ratelim.it => the Rails App running on container port 8100
  • https://ratelim.it/api => the Java API running on container port 8080

This example supports http & https, and splits traffic between your apps based on the url prefix.

my_app_task.json

"portMappings": [
  {
    "hostPort": 0,
    "containerPort": 8100,
    "protocol": "tcp"
  }
],

my_api_task.json

"portMappings": [
  {
    "hostPort": 0,
    "containerPort": 8080,
    "protocol": "tcp"
  }
],

Terraform code:

## ALB for both
resource "aws_alb" "app-alb" {
  name = "app-alb"
  security_groups = [
    "${aws_security_group.albs.id}"]
}

## ALB target for app
resource "aws_alb_target_group" "my_app" {
  name = "my_app"
  port = 80
  protocol = "HTTP"
  vpc_id = "${aws_vpc.myvpc.id}"

  deregistration_delay = 30
  health_check {
    protocol = "HTTP"
    path = "/healthcheck"
    healthy_threshold = 2
    unhealthy_threshold = 2
    interval = 90
  }
}

## ALB Listener for app
resource "aws_alb_listener" "my_app" {
  load_balancer_arn = "${aws_alb.app-alb.id}"
  port = "80"
  protocol = "HTTP"

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

## ALB Listener for app https
resource "aws_alb_listener" "my_app_https" {
  load_balancer_arn = "${aws_alb.app-alb.id}"
  port = "443"
  protocol = "HTTPS"
  ssl_policy = "ELBSecurityPolicy-2015-05"
  certificate_arn = "${data.aws_acm_certificate.my_app.arn}"
  default_action {
    target_group_arn = "${aws_alb_target_group.my_app.id}"
    type = "forward"
  }
}

## ALB Target for API
resource "aws_alb_target_group" "my_api" {
  name = "myapi"
  port = 80
  protocol = "HTTP"
  vpc_id = "${aws_vpc.myvpc.id}"

  deregistration_delay = 30

  health_check {
    path = "/api/v1/status"
    healthy_threshold = 2
    unhealthy_threshold = 2
    interval = 90
  }
}

## ALB Listener Rule for API
resource "aws_alb_listener_rule" "api_rule" {
  listener_arn = "${aws_alb_listener.my_app.arn}"
  priority = 100

  action {
    type = "forward"
    target_group_arn = "${aws_alb_target_group.my_api.arn}"
  }

  condition {
    field = "path-pattern"
    values = [
      "/api/*"]
  }
}


## ALB Listener RUle for API HTTPS    
resource "aws_alb_listener_rule" "myapi_rule_https" {
  listener_arn = "${aws_alb_listener.app_https.arn}"
  priority = 100

  action {
    type = "forward"
    target_group_arn = "${aws_alb_target_group.myapi.arn}"
  }

  condition {
    field = "path-pattern"
    values = [
      "/api/*"]
  }
}


## APP Task
resource "aws_ecs_task_definition" "my_app" {
  family = "my_app"
  container_definitions = "${data.template_file.my_app_task.rendered}"
}
## App Service
resource "aws_ecs_service" "my_app-service" {
  name = "my_app-service"
  cluster = "${aws_ecs_cluster.default.id}"

  task_definition = "${aws_ecs_task_definition.my_app.arn}"
  iam_role = "${aws_iam_role.ecs_role.arn}"
  depends_on = [
    "aws_iam_role_policy.ecs_service_role_policy"]

  load_balancer {
    target_group_arn = "${aws_alb_target_group.my_app.id}"
    container_name = "my_app"
    container_port = 8100
  }

}


## API Task
resource "aws_ecs_task_definition" "myapi" {
  family = "myapi"
  container_definitions = "${data.template_file.myapi_task.rendered}"
}

## API Servcice
resource "aws_ecs_service" "myapi-service" {
  name = "myapi-service"
  cluster = "${aws_ecs_cluster.default.id}"

  task_definition = "${aws_ecs_task_definition.myapi.arn}"
  iam_role = "${aws_iam_role.ecs_role.arn}"
  depends_on = [
    "aws_iam_role_policy.ecs_service_role_policy"]

  load_balancer {
    target_group_arn = "${aws_alb_target_group.myapi.id}"
    container_name = "myapi"
    container_port = 8080
  }
}
like image 2
jdwyah Avatar answered Oct 26 '22 15:10

jdwyah