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.
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.
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.
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.
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
If you're interested in doing this via https://www.terraform.io/ here's an example for two apps that share a domain:
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
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With