Bootstrap FreeKB - Amazon Web Services (AWS) Elastic Container Service (ECS) - Deploying a container to a Cluster using Terraform
Amazon Web Services (AWS) Elastic Container Service (ECS) - Deploying a container to a Cluster using Terraform


Elastic Container Service (ECS) is an Amazon Web Services (AWS) service that can be used to manage containers, similar to Kubernetes and OpenShift.

Let's say you have the following files on your Terraform server.

├── required_providers.tf
├── elastic_container_services (directory)
│   ├── cluster.tf
│   ├── listener.tf
│   ├── load_balancer.tf
│   ├── provider.tf
│   ├── security_group.tf
│   ├── services.tf
│   ├── subnets.tf
│   ├── target_group.tf
│   ├── task_definitions.tf
│   ├── virtual_private_cloud.tf

 

required_providers.tf will almost always have this.

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
    }
  }
}

 

Let's say provider.tf has the following. In this example, the "default" profile in /home/username/.aws/config and /home/username/.aws/credentials is being used. This assumes you have setup Terraform as described in Amazon Web Services (AWS) - Getting Started with Terraform.

provider "aws" {
  alias   = "default"
  profile = "default"
  region  = "default"
}

 

First you will create a cluster. Let's say cluster.tf has the following.

resource "aws_ecs_cluster" "my-ecs-cluster" {
  name = "my-ecs-cluster"
}

 

Next you will create a task definition. A Task Definition is kind of like a Dockerfile or a Kubernetes deployment YAML file, in that it contains keys and values that are used to define how something should be, such as the settings for a container. 

Let's say task-definitions.tf includes the following, to create a task definitation for a Flask container which will use FARGATE, meaning this will be serverless with no EC2 instances.

resource "aws_ecs_task_definition" "flask-task-definition" {
  family                   = "flask"
  network_mode             = "awsvpc"
  requires_compatibilities = ["FARGATE"]
  cpu                      = 1024
  memory                   = 2048

  container_definitions = jsonencode([
    {
      name   = "flask"
      cpu    = 10
      memory = 512
      image: "https://hub.docker.com/r/tiangolo/uwsgi-nginx-flask:python3.11",
      portMappings: [
        {
          containerPort: 80,
          hostPort: 80
        }
      ]
    }
  ])
}

 

We are going to need Virtual Private Cloud ID, so let's do something like this in virtual_private_cloud.tf.

data "aws_vpc" "virtual-private-cloud" {
  filter {
    name = "tag-key"
    values = ["Name"]
  }
  filter {
    name = "tag-value"
    values = ["my-vpc"]
  }
}

 

We are also going to need the Virtual Private Cloud Subnet IDs, perhaps something like this in subnets.tf.

data "aws_subnets" "subnets" {
  filter {
    name = "vpc-id"
    values = [data.aws_vpc.virtual-private-cloud.id]
  }
}

 

And security_group.tf.

data "aws_security_group" "default-security-group" {
  filter {
    name = "group-name"
    values = ["default"]
  }
  filter {
    name = "tag:Name"
    values = ["default"]
  }
}

 

In this example, the container will be fronted by an Application Load Balancer, so let's do something like this in load_balancer.tf.

resource "aws_lb" "ecs-application-load-balancer" {
  name               = "ecs-application-load-balancer"
  internal           = false
  load_balancer_type = "application"
  enable_deletion_protection = true
  security_groups    = [data.aws_security_group.default-security-group.id]
  subnets            = [data.aws_subnets.subnets.ids[0],data.aws_subnets.subnets.ids[1]]

  tags = {
    Name = "ecs-application-load-balancer"
  }
}

 

And something like this in target_group.tf.

resource "aws_lb_target_group" "ecs-application-load-balancer-target-group" {
  name = "ecs-target-group"
  port = 80
  protocol = "HTTP"
  target_type = "ip"
  vpc_id = data.virtual-private-cloud.id

  tags = {
    Name = "ecs-target-group"
  }
}

 

And something like this in listener.tf.

resource "aws_lb_listener" "ecs-application-load-balancer-listener" {
  load_balancer_arn = aws_lb.ecs-application-load-balancer.arn
  port = 80
  protocol = "HTTP"

  default_action {
    type = "forward"
    target_group_arn = aws_lb_target_group.ecs-application-load-balancer-target-group.arn
  }

  tags = {
    Name = "ecs-listener"
  }
}

 

And now you can create a JSON file that will be used to create a service. Perhaps services.tf has something like this.

resource "aws_ecs_service" "my-flask-service" {
  name = "my-flask-service"
  launch_type = "FARGATE"
  cluster = aws_ecs_cluster.my-ecs-cluster.id
  task_definition = aws_ecs_task_definition.flask-task-definition.id
  desired_count = 1

   load_balancer {
     target_group_arn = aws_lb_target_group.ecs-application-load-balancer-target-group.arn
     container_name = aws_ecs_task_definition.flask-task-definition.id
     container_port = 80
  }

  network_configuration {
    subnets = [data.aws_subnets.subnets.ids[0],data.aws_subnets.subnets.ids[1]]
    security_groups = [data.aws_security_group.ecs-security-group.id]
    assign_public_ip = false
  }
}

 




Did you find this article helpful?

If so, consider buying me a coffee over at Buy Me A Coffee



Comments


Add a Comment


Please enter 27c13f in the box below so that we can be sure you are a human.