How to deploy web app at AWS Fargate using terraform – part 4

H

Hi, devops fans

Welcome to the 4th and last part of tutorial, where we will speak about the TG (Target Group) terraform module and final implementation of fargate deployment from the terraform code side. The 1st 3 parts can be found within the next links:

We will start, as always, from physical structure file’s overview:

Here is terraform code for target group:

# tg.tf

resource "aws_lb_target_group" "this" {
  name        = format("%s-%s", local.name_prefix, var.tg_name)
  port        = var.tg_port
  protocol    = var.tg_protocol
  vpc_id      = var.vpc_id
  target_type = "ip"

  deregistration_delay = var.deregistration_delay

  health_check {
    port                = var.health_check_port
    protocol            = var.health_check_protocol
    enabled             = var.health_check_enabled
    interval            = var.health_check_interval
    path                = var.health_check_path
    timeout             = var.health_check_timeout
    healthy_threshold   = var.health_check_threshold
    unhealthy_threshold = var.health_check_unhealthy_threshold
    matcher             = var.health_check_matcher
  }
}

resource "aws_lb_listener_rule" "host" {
  listener_arn = var.listener_443_arn

  action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.this.arn
  }

  condition {
    host_header {
      values = var.host_headers
    }
  }
}

So, in the module we define aws_lb_target_group terrafrom resource, where we set its name, vpc, port and protocol. As we are working with fargate, we must to set target_type as “ip”, because our container would be registered at awsvpc network with getting own IP address. There is also very interesting property, which is called deregistration_delay. It determines how long the load balancer waits before fully deregistering a target (Fargate task) that has been marked for removal. When deploying new versions of your application or scaling down your services, the deregistration delay ensures that active sessions or requests are not abruptly terminated. In our learning example, the application is very simple, so it does not matter a lot what you will set here, though in real life it is essential to adjust it to application requirements.

Then we have health check options and there is aws_lb_listener_rule, where we pass our ssl listener arn. We want to forward all traffic at our ECS Fargate cluster when host matches the value we pass as variable.

Then we have list of files that does not required a lot of explanations:

# alarms.tf

resource "aws_cloudwatch_metric_alarm" "unhealthy_instance_count" {
  alarm_name          = format("%s-%s-%s", local.name_prefix, var.tg_name, "unhealthy-instances")
  comparison_operator = "GreaterThanOrEqualToThreshold"
  evaluation_periods  = "2"
  metric_name         = "UnHealthyHostCount"
  namespace           = "AWS/ApplicationELB"
  period              = "120"
  statistic           = "Average"
  threshold           = "1"
  datapoints_to_alarm = "1"

  dimensions = {
    LoadBalancer = var.alb_arn_suffix
    TargetGroup  = aws_lb_target_group.this.arn_suffix
  }

  alarm_actions = [data.aws_sns_topic.alarm_topic.arn]
}
# locals.tf

locals {
  name_prefix = format("%s-%s", var.project, var.env)

  common_tags = {
    Env       = var.env
    ManagedBy = "terraform"
    Project   = var.project
  }
}
# main.tf

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.21"
    }
  }

  required_version = "~> 1.6"
}
# outputs.tf

output "tg_arn" {
  value = aws_lb_target_group.this.arn
}
# sns.tf

data "aws_sns_topic" "alarm_topic" {
  name = var.alarm_sns_topic_name
}
# variables.tf

variable "tg_name" {
  type        = string
  description = "TG Name"
}

variable "tg_port" {
  type        = number
  description = "TG Port"
}

variable "tg_protocol" {
  type        = string
  description = "TG Protocol"
}

variable "vpc_id" {
  type        = string
  description = "VPC ID"
}

variable "deregistration_delay" {
  type        = number
  description = "TG deregistration delay. AWS default: 300"
}

variable "health_check_port" {
  type = number
}

variable "health_check_protocol" {
  type = string
}

variable "health_check_enabled" {
  type = bool
}

variable "health_check_interval" {
  type = number
}

variable "health_check_path" {
  type = string
}

variable "health_check_timeout" {
  type = number
}

variable "health_check_threshold" {
  type = number
}

variable "health_check_unhealthy_threshold" {
  type = number
}

variable "health_check_matcher" {
  type = string
}

variable "listener_443_arn" {
  type        = string
  description = "Listener for the TG (443)"
}

variable "host_headers" {
  type        = list(string)
  description = "Host headers for Listener rule"
}

variable "alb_arn_suffix" {
  type = string
}

variable "alarm_sns_topic_name" {
  type = string
}

variables-env.tf – as always the same we had it at previous parts 🙂

So, now it is time to have a look at final implementation example:

# fargate-app/main.tf

terraform {
  backend "s3" {
    bucket         = "terraform-state-fargate"
    dynamodb_table = "terraform-state-fargate"
    encrypt        = true
    key            = "stage-fargate-app.tfstate"
    region         = "eu-central-1"
  }
}

data "terraform_remote_state" "network" {
  backend = "s3"

  config = {
    bucket = "terraform-state-fargate"
    key    = "dev-network.tfstate"
    region = var.region
  }
}

data "terraform_remote_state" "alb" {
  backend = "s3"

  config = {
    bucket = "terraform-state-fargate"
    key    = "dev-alb.tfstate"
    region = var.region
  }
}

provider "aws" {
  allowed_account_ids = [var.account_id]
  region              = var.region
}

locals {
  app_name                 = "app"
  ssm_secret_path_prefix   = "arn:aws:ssm:${var.region}:${var.account_id}:parameter/${var.project}/${var.env}/${local.app_name}"
}

module "fargate" {
  source = "../../modules/fargate"

  account_id = var.account_id
  env        = var.env
  project    = var.project
  region     = var.region

  fargate_cpu       = 256
  fargate_memory    = 512
  # Available X86_64, ARM64; ARM64 - much more cheaper option, especially if take saving compute plan
  # image build for specific platrofm must be used
  runtime_platform = "X86_64"
  #runtime_platform  = "ARM64"

  app_name          = local.app_name
  app_image         = "004571937517.dkr.ecr.eu-central-1.amazonaws.com/udemy-dev-app:latest"
  app_port          = 5000
  app_count         = 1
  app_environments  = [
    {"name": "MODE_TYPE", "value": "app"},
    {"name": "AWS_DEFAULT_REGION","value": var.region},
  ]
  app_secrets = [
    {"name": "AWS_ACCESS_KEY_ID", "valueFrom": "${local.ssm_secret_path_prefix}/AWS_ACCESS_KEY_ID"},
    {"name": "AWS_SECRET_ACCESS_KEY", "valueFrom": "${local.ssm_secret_path_prefix}/AWS_SECRET_ACCESS_KEY"},
  ]

  fargate_subnets     = data.terraform_remote_state.network.outputs.subnets_public
  fargate_ecs_task_sg = data.terraform_remote_state.network.outputs.sg_ecs_fargate_task

  tg_arn           = module.target_group.tg_arn
  listener_443_arn = data.terraform_remote_state.alb.outputs["listener_443_arn"]
}
# fargate-app/tg.tf

module "target_group" {
  source = "../../modules/tg-fargate"

  account_id = var.account_id
  env        = var.env
  project    = var.project
  region     = var.region
  vpc_id     = data.terraform_remote_state.network.outputs.vpc["id"]

  tg_name     = "fargate-app"
  tg_port     = 80
  tg_protocol = "HTTP"

  deregistration_delay = 60

  health_check_enabled             = true
  health_check_port                = 5000
  health_check_protocol            = "HTTP"
  health_check_path                = "/ping"
  health_check_matcher             = "200-399"
  health_check_interval            = 30
  health_check_timeout             = 5
  health_check_threshold           = 2
  health_check_unhealthy_threshold = 2

  listener_443_arn = data.terraform_remote_state.alb.outputs["listener_443_arn"]
  host_headers = [
    "flask.sergiitest.website"
  ]

  alb_arn_suffix       = data.terraform_remote_state.alb.outputs["arn_suffix"]
  alarm_sns_topic_name = "udemy-dev-alerts"
}

I believe this tutorial will be a valuable resource for anyone looking to enhance their skills in cloud deployment. Whether you’re a beginner or have some experience, you’ll find useful information to take your projects to the next level.

Thank you for the attention.

P.S. If you want to pass all material at once in fast and convenient way, with detailed explanations, then welcome to my course: “AWS Fargate DevOps: Autoscaling with Terraform at practice”, here you may find coupon with discount.



architecture AWS cluster cyber-security devops devops-basics docker elasticsearch flask geo high availability java machine learning opensearch php programming languages python recommendation systems search systems spring boot symfony