ECS and Application Load Balancer

爱⌒轻易说出口 提交于 2019-12-08 17:20:39

问题


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.


回答1:


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




回答2:


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
  }
}



回答3:


Are you trying to rebuild the entire ECS stack in CF? If you can live with pre-defined clusters, you can just register the instances with user data when they spin up (I use spot fleet, but this should work anywhere you're starting an instance). Something like this in your LaunchSpecifications:

  "UserData":
            { "Fn::Base64" : { "Fn::Join" : [ "", [
              "#!/bin/bash\n",
              "yum update -y\n",
              "echo ECS_CLUSTER=YOUR_CLUSTER_NAME >> /etc/ecs/ecs.config\n",
              "yum install -y aws-cli\n",
              "aws ec2 create-tags --region YOUR_REGION --resources $(curl http://169.254.169.254/latest/meta-data/instance-id) --tags Key=Name,Value=YOUR_INSTANCE_NAME\n"
              ]]}}

I know it's not pure Infrastructure as Code, but it gets the job done with minimal effort, and I my cluster configs don't really change a lot.



来源:https://stackoverflow.com/questions/39185957/ecs-and-application-load-balancer

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!