Search code examples
amazon-web-servicesterraformterraform-provider-awsamazon-elb

aws application load balancer target group has no 'registered' target but cannot assign ECS fargate cluster because it doesn't have valid ipv4?


I have a terraform-defined ECS cluster with fargate task, service, target group and lb.

I'm trying to send requests to the fargate cluster but it's timing out. I've tried to add an attachment as follows:

resource "aws_lb_target_group_attachment" "websocket-server" {
  target_group_arn = aws_lb_target_group.websocket-server.arn
  target_id        = aws_ecs_cluster.websocket-server-cluster.id
  port             = 443
}

But unfortunately this throws:

Error registering targets with target group: ValidationError: The IP address 'arn:aws:ecs:eu-west-2:xxxxxx:cluster/websocket-server-cluster' is not a valid IPv4 address

My LB/target group/ECS definitions:

resource "aws_ecs_cluster" "websocket-server-cluster" {
  name = "websocket-server-cluster"
}


resource "aws_ecs_service" "websocket-server-service" {
  name                               = "websocket-server-service"
  cluster                            = aws_ecs_cluster.websocket-server-cluster.arn
  deployment_maximum_percent         = 200
  deployment_minimum_healthy_percent = 0
  launch_type                        = "FARGATE"
  task_definition                    = aws_ecs_task_definition.websocket-server-task.arn

  load_balancer {
    target_group_arn = aws_lb_target_group.websocket-server.arn
    container_name   = "websocket-server"
    container_port   = 443
  }

  network_configuration {
    assign_public_ip = true
    security_groups  = [aws_security_group.public.id, aws_security_group.private.id]
    subnets          = [aws_subnet.public.id, aws_subnet.private.id]
  }

}

module "websocket-server" {
  source           = "git::https://github.com/cloudposse/terraform-aws-ecs-container-definition.git?ref=tags/0.58.1"
  container_name   = "websocket-server"
  container_image  = "${aws_ecr_repository.websocket-server.repository_url}:latest"
  container_cpu    = "256"
  container_memory = "512"
  port_mappings = [
    {
      containerPort = 443
      hostPort      = 443
      protocol      = "tcp"
    }
  ]

  environment = []


}

resource "aws_ecs_task_definition" "websocket-server-task" {
  family                   = "websocket-server"
  requires_compatibilities = ["FARGATE"]
  memory                   = "512"
  cpu                      = "256"
  task_role_arn            = aws_iam_role.ecs-container-role.arn
  execution_role_arn       = aws_iam_role.ecs-container-role.arn
  network_mode             = "awsvpc"
  container_definitions    = module.websocket-server.json_map_encoded_list
  lifecycle {
    ignore_changes = [
      tags, tags_all
    ]
  }
}


resource "aws_lb" "main" {
  name                       = "main"
  internal                   = false
  load_balancer_type         = "application"
  security_groups            = [aws_security_group.public.id, aws_security_group.private.id]
  enable_deletion_protection = false
  subnets                    = [aws_subnet.public.id, aws_subnet.public-backup.id]
}


resource "aws_lb_target_group" "websocket-server" {
  name        = "websocket-server"
  port        = 443
  protocol    = "HTTPS"
  vpc_id      = aws_vpc.main.id
  target_type = "ip"

  health_check {
    enabled             = true
    healthy_threshold   = 3
    unhealthy_threshold = 3
    timeout             = 10
    protocol            = "HTTPS"
    path                = "/apis/websocket-server/health"
    interval            = "100"
    matcher             = "200"
  }

  depends_on = [
    aws_lb.main
  ]

}

resource "aws_lb_listener" "websocket-server" {
  load_balancer_arn = aws_lb.main.arn
  port              = "443"
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-2016-08"
  certificate_arn   = aws_acm_certificate.main.arn


  default_action {
    target_group_arn = aws_lb_target_group.websocket-server.arn
    type             = "forward"
  }
}

resource "aws_lb_listener" "http" {
  load_balancer_arn = aws_lb.main.arn
  port              = "80"
  protocol          = "HTTP"

  default_action {
    type = "redirect"

    redirect {
      port        = "443"
      protocol    = "HTTPS"
      status_code = "HTTP_301"
    }
  }
}



resource "aws_lb_listener_certificate" "main" {
  listener_arn    = aws_lb_listener.websocket-server.arn
  certificate_arn = aws_acm_certificate.main.arn
}






Solution

  • The attachment definition is not necessary at all. Keep in mind, containers for Fargate services do not use network interfaces of the underlying EC2 instances in the cluster (you don't see the instances at all for that matter). They use AWS VPC networking mode only -- independent network interfaces in the VPC are attached to the containers.

    The target group attachment happens automatically and is configured through the load_balancer block in the aws_ecs_service resource. As ECS starts the containers, they get registered with the target group automatically. There is no static attachment to define in the case of Fargate ECS services.

    Just remove the tg attachment resource from your tf file altogether.

    Check out this resource for a decent reference implementation with terraform.


    As a completely separate side note, you probably also do not want assign_public_ip = true in your service configuration. That would allow access to your containers directly without going through the load balancer which is almost never what you want when you're using a load balancer.