Deploying Containers on AWS Fargate using Terraform

A comprehensive guide to deploying containerized applications on AWS Fargate using Terraform Infrastructure as Code

Deploying Containers on AWS Fargate using Terraform

AWS Fargate is a serverless compute engine for containers that works with both Amazon Elastic Container Service (ECS) and Amazon Elastic Kubernetes Service (EKS). In this guide, we’ll walk through deploying a containerized application using AWS Fargate and Terraform.

Prerequisites

Before we begin, make sure you have:

  • AWS CLI configured with appropriate credentials
  • Terraform installed (version 1.0 or later)
  • Basic understanding of Docker and containers
  • A Docker image ready to deploy

Project Structure

First, let’s set up our Terraform project structure:

aws-fargate-terraform/
├── main.tf
├── variables.tf
├── outputs.tf
└── terraform.tfvars

Setting Up the Network Infrastructure

First, we’ll create the VPC and networking components:

# main.tf
provider "aws" {
  region = var.aws_region
}

# VPC
resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = {
    Name = "${var.project_name}-vpc"
  }
}

# Public Subnets
resource "aws_subnet" "public" {
  count             = length(var.availability_zones)
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.${count.index + 1}.0/24"
  availability_zone = var.availability_zones[count.index]

  tags = {
    Name = "${var.project_name}-public-${count.index + 1}"
  }
}

Creating the ECS Cluster and Fargate Service

Next, we’ll set up the ECS cluster and Fargate service:

# ECS Cluster
resource "aws_ecs_cluster" "main" {
  name = "${var.project_name}-cluster"
}

# Task Definition
resource "aws_ecs_task_definition" "app" {
  family                   = "${var.project_name}-task"
  network_mode             = "awsvpc"
  requires_compatibilities = ["FARGATE"]
  cpu                      = var.container_cpu
  memory                   = var.container_memory

  container_definitions = jsonencode([
    {
      name  = "${var.project_name}-container"
      image = var.container_image
      portMappings = [
        {
          containerPort = var.container_port
          hostPort      = var.container_port
          protocol      = "tcp"
        }
      ]
    }
  ])
}

# ECS Service
resource "aws_ecs_service" "main" {
  name            = "${var.project_name}-service"
  cluster         = aws_ecs_cluster.main.id
  task_definition = aws_ecs_task_definition.app.arn
  desired_count   = var.service_desired_count
  launch_type     = "FARGATE"

  network_configuration {
    subnets         = aws_subnet.public[*].id
    security_groups = [aws_security_group.ecs_tasks.id]
  }
}

Variables Configuration

Here’s how to set up your variables:

# variables.tf
variable "aws_region" {
  description = "AWS region to deploy resources"
  type        = string
  default     = "us-west-2"
}

variable "project_name" {
  description = "Name of the project"
  type        = string
}

variable "availability_zones" {
  description = "List of availability zones"
  type        = list(string)
  default     = ["us-west-2a", "us-west-2b"]
}

variable "container_image" {
  description = "Docker image to run in the ECS cluster"
  type        = string
}

variable "container_port" {
  description = "Port exposed by the docker image"
  type        = number
  default     = 80
}

variable "container_cpu" {
  description = "CPU units to allocate"
  type        = number
  default     = 256
}

variable "container_memory" {
  description = "Memory to allocate"
  type        = number
  default     = 512
}

variable "service_desired_count" {
  description = "Number of tasks to run"
  type        = number
  default     = 2
}

Security Group Configuration

Don’t forget to set up security groups:

resource "aws_security_group" "ecs_tasks" {
  name        = "${var.project_name}-sg"
  description = "Allow inbound traffic for ECS tasks"
  vpc_id      = aws_vpc.main.id

  ingress {
    protocol    = "tcp"
    from_port   = var.container_port
    to_port     = var.container_port
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    protocol    = "-1"
    from_port   = 0
    to_port     = 0
    cidr_blocks = ["0.0.0.0/0"]
  }
}

Deploying the Infrastructure

To deploy your infrastructure:

  1. Initialize Terraform:
terraform init
  1. Review the plan:
terraform plan
  1. Apply the configuration:
terraform apply

Best Practices and Considerations

  1. Cost Management

    • Use appropriate instance sizes
    • Implement auto-scaling based on demand
    • Regular monitoring of resource utilization
  2. Security

    • Use private subnets for tasks when possible
    • Implement least privilege access
    • Regular security group audits
  3. Monitoring

    • Set up CloudWatch logs
    • Configure alarms for critical metrics
    • Implement proper tagging strategy

Cleaning Up

When you’re done, don’t forget to destroy the resources to avoid unnecessary charges:

terraform destroy

Conclusion

AWS Fargate with Terraform provides a powerful way to deploy containerized applications without managing the underlying infrastructure. This setup gives you a solid foundation for running containers in a production environment with proper networking, security, and scalability configurations.

Remember to:

  • Always version your Terraform code
  • Use workspaces for different environments
  • Implement proper state management
  • Regular testing of your infrastructure code

The complete code for this setup is available in the repository, along with additional examples and configurations for more complex scenarios.