1

My issue is about making a calls to API that is written in Next.js app. The Web app is on the same task of Next.js app. My aplication is deployed to AWS ECS with Application Load Balancer and EC2 launch type task. My task has configured network mode with using awsvpc. I'm able to connect through HTTPS and that's prove that Security Groups are configured well.

The egress rules are set up for any protocol and any port. While using pages were are made a calls to Next.js API, I'm receiving 504 Gateway Timeout.

Infrastructure has been uploaded using Terraform and I have created VPC and networking stuff also using terraform. The requests are also communicating with Cognito and DynamoDB but IAM policies are added to IAM Task Role

Do you have any ideas what could be an issue?

I have tried checking routing table rules with Internet Gateway and it still doesn't help.

EDIT: Here is my terraform code. I'm using HTTPS connection with certificate generated in AWS ACM

#####
# VPC and subnets
#####
data "aws_vpc" "default_vpc" {
  default = true
}

data "aws_subnets" "all_default_subnets" {
  filter {
    name   = "vpc-id"
    values = [data.aws_vpc.default_vpc.id]
  }
}

#####
# ALB
#####
resource "aws_lb" "alb" {
  name               = "alb"
  load_balancer_type = "application"
  security_groups    = [ aws_security_group.alb.id ]
  subnets            = data.aws_subnets.all_default_subnets.ids
  enable_deletion_protection = false
}

resource "aws_lb_target_group" "lb_target_group" {
  name        = "target-alb-name"
  port        = "80"
  protocol    = "HTTP"
  vpc_id      = data.aws_vpc.default_vpc.id
  target_type = "ip"


  health_check {
    healthy_threshold   = "3"
    interval            = "10"
    path                = "/api/health"
    protocol            = "HTTP"
    unhealthy_threshold = "3"
  }
}

resource "aws_lb_listener" "alb_80" {
  load_balancer_arn = aws_lb.alb.arn
  port              = "443"
  protocol          = "HTTPS"
# My certificate generated from terraform level
  certificate_arn = aws_acm_certificate.loadbalancer_cert.arn

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.lb_target_group.arn
  }
  tags = {
    environment = "prod"
  }
}

##############################################################
# AWS ECS-SERVICE
##############################################################

resource "aws_ecs_service" "service" {
  cluster                = aws_ecs_cluster.cluster.id
  desired_count          = 1
  launch_type            = "EC2"
  name                   = "service"
  task_definition        = aws_ecs_task_definition.task_definition.arn
  force_new_deployment = true
  deployment_minimum_healthy_percent = 0
  network_configuration {
    subnets = data.aws_subnets.all_default_subnets.ids
    security_groups = [aws_security_group.ecs_tasks.id]
    assign_public_ip = false
  }
  load_balancer {
    container_name = "ecs-container"
    container_port = 3000
    target_group_arn = aws_lb_target_group.lb_target_group.arn
  }
  depends_on = [aws_lb_listener.alb_80]
}

############################################################
# AWS ECS-TASK
############################################################

resource "aws_ecs_task_definition" "task_definition" {
  container_definitions    = data.template_file.task_definition_json.rendered
  execution_role_arn       = var.execution_task_role_arn
  family                   = "web-task"
  network_mode             = "awsvpc"
  cpu                      = "128"
  memory                   = "256"
  requires_compatibilities = ["EC2"]
  task_role_arn            = var.prod_task_role_arn
}

data "template_file" "task_definition_json" {
  template = file("${path.module}/task_definition.json")
  vars = {
    BASE_URL = var.BASE_URL,
    APP_AWS_REGION = var.APP_AWS_REGION,
    COGNITO_WEB_CLIENT_ID = var.COGNITO_WEB_CLIENT_ID,
    COGNITO_USER_POOL_ID = var.COGNITO_USER_POOL_ID,
    AWS_ACCESS_KEY_ID = var.AWS_ACCESS_KEY_ID,
    AWS_SECRET_ACCESS_KEY = var.AWS_SECRET_ACCESS_KEY,
    DYNAMODB_TABLE_NAME = var.DYNAMODB_TABLE_NAME,
    COGNITO_REGION = var.COGNITO_REGION,
  }
}

###########################################################
# AWS ECS-EC2
###########################################################
module "ec2_instance" {
  source  = "terraform-aws-modules/ec2-instance/aws"
  version = "~> 4.3.0"

  name = "ec2-instance"

  create_spot_instance = false
  iam_instance_profile = aws_iam_instance_profile.ecs_agent.name
  user_data            = data.template_file.user_data.rendered

  ami                    = var.prod_ami_id
  instance_type          = var.prod_instance_type
  key_name               = "key-pairs"
  monitoring             = true
  vpc_security_group_ids = [aws_security_group.ecs_tasks.id]
  subnet_id              = data.aws_subnets.all_default_subnets.ids[0]


  root_block_device = [
    {
      volume_type           = "gp3"
      volume_size           = 30
      delete_on_termination = true
    }
  ]

  tags = {
    environment = "prod"
  }
}

data "template_file" "user_data" {
  template = file("${path.module}/user_data.tpl")
}

resource "aws_security_group" "ecs_tasks" {
  name = "sg-ecs-tasks"
  vpc_id = data.aws_vpc.default_vpc.id
  ingress {
    protocol="tcp"
    from_port=3000
    to_port=3000
    cidr_blocks=["0.0.0.0/0"]
  }
  ingress {
    protocol="tcp"
    from_port=443
    to_port=443
    cidr_blocks=["0.0.0.0/0"]
  }
  ingress {
    protocol="tcp"
    from_port=80
    to_port=80
    cidr_blocks=["0.0.0.0/0"]
  }
  egress {
    protocol="-1"
    from_port=0
    to_port=0
    cidr_blocks=["0.0.0.0/0"]
  }
}

#####
# Security Group Config
#####

resource "aws_security_group" "ecs_tasks" {
  name = "sg-ecs-tasks"
  vpc_id = data.aws_vpc.default_vpc.id
  ingress {
    protocol="tcp"
    from_port=3000
    to_port=3000
    cidr_blocks=["0.0.0.0/0"]
  }
  ingress {
    protocol="tcp"
    from_port=443
    to_port=443
    cidr_blocks=["0.0.0.0/0"]
  }
  ingress {
    protocol="tcp"
    from_port=80
    to_port=80
    cidr_blocks=["0.0.0.0/0"]
  }
  egress {
    protocol="-1"
    from_port=0
    to_port=0
    cidr_blocks=["0.0.0.0/0"]
  }
}

resource "aws_security_group" "alb" {
  name = "sg-alb"
  vpc_id = data.aws_vpc.default_vpc.id
  ingress {
    protocol="tcp"
    from_port=3000
    to_port=3000
    cidr_blocks=["0.0.0.0/0"]
  }
  ingress {
    protocol="tcp"
    from_port=443
    to_port=443
    cidr_blocks=["0.0.0.0/0"]
  }
  ingress {
    protocol="tcp"
    from_port=80
    to_port=80
    cidr_blocks=["0.0.0.0/0"]
  }
  egress {
    protocol="-1"
    from_port=0
    to_port=0
    cidr_blocks=["0.0.0.0/0"]
  }
}

user_data for EC2 instance

#!/bin/bash

cat <<'EOF' >> /etc/ecs/ecs.config
ECS_CLUSTER=ecs-cluster
ECS_ENABLE_SPOT_INSTANCE_DRAINING=true
ECS_ENABLE_CONTAINER_METADATA=true
ECS_AVAILABLE_LOGGING_DRIVERS=["json-file","awslogs"]
ECS_IMAGE_PULL_BEHAVIOR=always
EOF

yum update -y yum install -y httpd sudo systemctl start httpd sudo systemctl enable httpd

KamilMastalerz
  • 205
  • 2
  • 11
  • This usually means the backend didn't respond in 29 seconds: https://docs.aws.amazon.com/apigateway/latest/developerguide/supported-gateway-response-types.html. – Marko E Feb 07 '23 at 07:31
  • In my solution the communication with internet is through Internet Gateway. Then loadbalancer communicates with public EC2 instance that is connected with ECS. I'm not using API Gateway. There is only 1 container in the Task with Next.js app on port 3000. Next.js is both Frontend and Backed API. Only the backend API calls returns 504 – KamilMastalerz Feb 07 '23 at 10:03
  • Ah, my bad. Can you add any terraform code to the question? – Marko E Feb 07 '23 at 10:08
  • Alright, I've just added it – KamilMastalerz Feb 07 '23 at 15:19

1 Answers1

0

Based on quesstion asked under the link I've noticed that I can use bridge network mode and it really works. However based on AWS Best Practices it's recommended to use awsvpc mode. Would anyone mind help me fond a solution how to figure it out?

KamilMastalerz
  • 205
  • 2
  • 11