Open In App

How to Push Docker Image to AWS ECS Fargate Using Terraform?

Last Updated : 26 May, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

ECS helps to manage containers and this service is provided by AWS, it allows users to easily manage and scale docker containers on a cluster of ec2 instances, without having to manage the underlying infrastructure.

What is ECS Fargate?

Fargate provides a way to run containerized applications on a serverless infrastructure, which means you only pay for the resources you use, without having to manage servers or clusters. With Fargate, you can launch and manage containers in a matter of minutes, without worrying about managing the underlying infrastructure or scaling your applications.

Before going to terraform step we need to confirm whether we have IAM account or not.For this search iam in services and click on add users on left-hand side and give administrator access to it and add this user to a group.

This will give you aws access key and secret access key, now configure them to your installed AWS CLI. Now you should have dockerfile for your project, here for example django project is used.

Example Docker file:

# Use an official Python runtime as a parent image
FROM python:3.9-slim-buster

# Set the working directory to /app
WORKDIR /app

# Copy the requirements file into the container
COPY requirements.txt .

# Install any needed packages specified in requirements.txt
RUN pip install --no-cache-dir -r requirements.txt

# Copy the rest of the application code into the container
COPY . .

# Set environment variables
ENV PYTHONUNBUFFERED=1 \
    DJANGO_ENV=production

# Expose port 8000
EXPOSE 8000

# Run the command to start the server
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]

You should test your Dockerfile first by building the image and running the conatiner.

Now we can start making terraform file if all these things are working.

Make a terraform file name main.tf inside the same directory where your docker file is and follow the following steps:

Step 1: Add the AWS provider as follows

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

Step 2: Now you have to provide AWS credentials to allow terraform to connect to AWS.

provider "aws" {
  region  = "us-east-1" #The region where the environment 
  #is going to be deployed # Use your own region here
  access_key = "enter_access_key_here" # Enter AWS IAM 
  secret_key = "enter_secret_key_here" # Enter AWS IAM 
  token="enter_token_here" # for private repository only
}

Step 3: Now create an ECR repository in terraform

resource "aws_ecr_repository" "app_ecr" {
  name = "djangoapp-repo"
}

Step 4: Now start by running terraform init command in terminal. 

terraform init will initialize the configuration files in working directory and it will show Terraform has been successfully initialized!

you will see a this as output in terminal.

 

Step 5: Now run terraform plan command

terraform plan is a Terraform command that is used to create an execution plan for your infrastructure deployment. When you run terraform plan, Terraform examines your infrastructure configuration files (written in HCL) and creates a detailed execution plan that shows what actions Terraform will take to achieve the desired infrastructure state.

 

Step 6: Now run terraform apply command.

terraform apply is a Terraform command that applies the changes required to reach the desired infrastructure state. When you run terraform apply, Terraform examines your infrastructure configuration files (written in HCL) and applies the changes needed to reach the desired infrastructure state.

 

You will be asked to approve the process just enter yes

Step 7: Now open ECR in AWS account and go to public repositories you will find the repository named djangoapp-repo inside it.

Step 8: Click on that repository and on the top right you will see view push commands and you can follow the steps there to push the image to from your local to that repository.

 

 

Step 9:  For launching this image you need a target so we have to make a cluster, it acts as a container target.

resource "aws_ecs_cluster" "your_cluster" {
  name = "app-cluster" 
}

Step 10: Now again perform terraform apply. You will see a cluster in ECS.

 

Amazon Elastic Container Service (ECS) provides two launch types for deploying containers: EC2 launch type and Fargate launch type.

EC2 Launch Type: With the EC2 launch type, you can launch containers on a cluster of Amazon Elastic Compute Cloud (EC2) instances that you manage yourself. In this mode, you have complete control over the underlying infrastructure, including the ability to choose the instance types, configure the networking and storage, and manage the EC2 instances directly. The EC2 launch type is recommended for users who have existing EC2 infrastructure and need more control over the underlying infrastructure.

Fargate Launch Type: With the Fargate launch type, you can launch containers on a fully-managed infrastructure without the need to manage the underlying EC2 instances. In this mode, you only need to specify the container images and resource requirements, and AWS manages the rest, including provisioning the infrastructure, scaling, and patching. The Fargate launch type is recommended for users who want a serverless experience for running containers and do not want to manage the underlying infrastructure.

Step 11: Here we are using fargate which is serverless

resource "aws_ecs_task_definition" "djangoapp_task" {
  family                   = "djangoapp-first-task" # Name your task
  container_definitions    = <<DEFINITION
  [
    {
      "name": "app-first-task",
      "image": "${aws_ecr_repository.app_ecr.repository_url}",
      "essential": true,
      "portMappings": [
        {
          "containerPort": 8000,
          "hostPort": 8000
        }
      ],
      "memory": 512,
      "cpu": 256
    }
  ]
  DEFINITION
  requires_compatibilities = ["FARGATE"] # use Fargate as the launch type
  network_mode             = "awsvpc"    # add the AWS VPN network mode as this is required for Fargate
  memory                   = 512         # Specify the memory the container requires you can specify yours
  cpu                      = 256         # Specify the CPU the container requires
  execution_role_arn       = "${aws_iam_role.ecsTaskExecutionRole.arn}"
}

Step 12: Now we have to create a task definition

resource "aws_iam_role" "ecsTaskExecutionRole" {
  name               = "ecsTaskExecutionRole"
  assume_role_policy = "${data.aws_iam_policy_document.assume_role_policy.json}"
}

data "aws_iam_policy_document" "assume_role_policy" {
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type        = "Service"
      identifiers = ["ecs-tasks.amazonaws.com"]
    }
  }
}

resource "aws_iam_role_policy_attachment" "ecsTaskExecutionRole_policy" {
  role       = "${aws_iam_role.ecsTaskExecutionRole.name}"
  policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
}

Step 13: Now perfrom terraform apply, for the private repository you may need to add an internet gateway just add it.

 

Step 14: Now the configurations are done and we need to connect all these things so we need VPC

A Virtual Private Cloud (VPC) is a virtual network infrastructure that allows you to create isolated, private networks within the AWS Cloud. With a VPC, you have complete control over your virtual networking environment, including the IP address range, subnets, routing tables, and network gateways.

# Provide a default VPC
resource "aws_default_vpc" "default_vpc" {
}

# Provide your default subnets
resource "aws_default_subnet" "default_subnet_a" {
  # Use your own region here but reference to subnet 1a
  availability_zone = "us-east-1a"
}

resource "aws_default_subnet" "default_subnet_b" {
  # Use your own region here
  availability_zone = "us-east-1b"
}

Step 15 : Implement a Load balancer

resource "aws_alb" "application_load_balancer" {
  name               = "load-balancer-django" #name
  load_balancer_type = "application"
  subnets = [ # giving the default subnets
    "${aws_default_subnet.default_subnet_a.id}",
    "${aws_default_subnet.default_subnet_b.id}"
  ]
  # security groups
  security_groups = ["${aws_security_group.load_balancer_security_group.id}"]
}

Step 16: Create a Security group for load balancer

resource "aws_security_group" "load_balancer_security_group" {
  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"] # Allow traffic from all resources
  }

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

Step 17:  Configure the load balancer with vpc created earlier.

resource "aws_lb_target_group" "target_group" {
  name        = "target-group"
  port        = 80
  protocol    = "HTTP"
  target_type = "ip"
  vpc_id      = "${aws_default_vpc.default_vpc.id}" # default VPC
}

resource "aws_lb_listener" "listener" {
  load_balancer_arn = "${aws_alb.application_load_balancer.arn}" #  mention load balancer
  port              = "80"
  protocol          = "HTTP"
  default_action {
    type             = "forward"
    target_group_arn = "${aws_lb_target_group.target_group.arn}" # mention target group
  }
}

Step 18: Create an ECS service, this will run cluster, task and fargate.

resource "aws_ecs_service" "app_service" {
  name            = "app-first-service"     # Name of the service
  cluster         = "${aws_ecs_cluster.your_cluster.id}"   # give the created Cluster
  task_definition = "${aws_ecs_task_definition.djangoapp_task.arn}" # give the task that the service will spin up
  launch_type     = "FARGATE"
  desired_count   = 3 # Set up the number of containers to 3

  load_balancer {
    target_group_arn = "${aws_lb_target_group.target_group.arn}" # give the target group
    container_name   = "${aws_ecs_task_definition.djangoapp_task.family}"
    container_port   = 8000 # give the container port
  }

  network_configuration {
    subnets          = ["${aws_default_subnet.default_subnet_a.id}", "${aws_default_subnet.default_subnet_b.id}"]
    assign_public_ip = true     # give the containers with public IPs
    security_groups  = ["${aws_security_group.service_security_group.id}"] # set up the security group
  }
}

Step 19:To ensure access for ecs service with more secure vpc create a aws sercurity group service.

resource "aws_security_group" "service_security_group" {
  ingress {
    from_port = 0
    to_port   = 0
    protocol  = "-1"
    #allowing the traffic from load balancer security group
    security_groups = ["${aws_security_group.load_balancer_security_group.id}"]
  }

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

Step 20: The ouput should be in url value like django server runs on localhost 8000

output "app_url" {
  value = aws_alb.application_load_balancer.dns_name
}

Now the terraform is set to create and manage infrastructure on AWS.

Now perform following commands:

  • terraform validate: It will validate the syntax.

 

  • terraform plan: It will help us to visualize how terraform perform and create resource.

 

  • terraform apply: It will apply all the provisioned infrastructure

 

  • Give some time after this step and open the output URL

if not in use just run terraform destroy to reduce any cost.



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads