This project is initiated with a basic structure that automates the deployment procedures, including ECR,ECS and GitHub Actions as CI/CD. All AWS resources are created via Infrastructure as Code (IaC) using Terraform
The basic overview of the project is as follows: it has a GitHub repository where developers usually make changes and push them via MR. Once the Merge Requests are reviewed and approved, they are merged into the main branch. Upon merging into the main branch, the GitHub Actions pipeline is triggered. It builds && tags the image, and then pushes it to the ECR.
Once the image is received in the Elastic Container Registry,a CloudWatch Event Rule is established to trigger the ECS (Elastic Container Service) service, ensuring seamless deployment of the updated containerized application. The below-created environment has basic infrastructure and can be used for testing purposes. If planning to use it in production, update it with necessary security practices.
- Automated Image Build && Push where images on code changes and push them to the specified ECR repository.
- Dynamically provision AWS resources such as ECR repository, ECS cluster, task definition, service, security group, and IAM role using Terraform.
- Structure Terraform code in a reusable manner for better maintainability and scalability.
- Fargate launch type is used as the launch type for ECS services to abstract infrastructure management and scale applications easily.
- Event-Driven Deployment as the CloudWatch Event Rule is used here to trigger ECS service based on ECR image push events, enabling event-driven deployments.
- ECR Repository
- ECS Cluster
- ECS Task Definition
- ECS Service
- IAM Roles
- Security Groups
- CloudWatch Event Rules
- VPC with 2 public subnets
- Basic knolwdge in AWS resources and Terrform
- IAM User with Access Key ID and Secret Access Key with the necessary permissions for creating and managing resources.
- Secure the Credintilas in the GitHub Actions secrets
Initially, created AWS resources, which include the following:
- Creation of an Amazon Elastic Container Registry (ECR) repository and an Amazon ECS (Elastic Container Service) cluster.
- Task Definition of an ECS task for running containers within the cluster. This involves specifying container configuration, networking mode, and resource requirements. The task utilizes the container image from the ECR repository.
- Setup of an ECS service, configuring it to use the Fargate launch type. This includes specifying the task definition and defining the network configuration, including subnets, security groups, and public IP assignment.
- Establishment of a security group for the ECS service.
- Creation of an IAM role for ECS tasks and events, equipped with policies allowing ECS task execution and the necessary ECR permissions.
# AWS ECR Repository
resource "aws_ecr_repository" "project01" {
name = "project01"
image_scanning_configuration {
scan_on_push = true
}
}
# AWS ECS Cluster
resource "aws_ecs_cluster" "project01" {
name = "project01"
setting {
name = "containerInsights"
value = "enabled"
}
}
# AWS ECS Task Definition
resource "aws_ecs_task_definition" "project01_task" {
family = "project-task"
network_mode = "awsvpc"
requires_compatibilities = ["FARGATE"]
cpu = "256"
memory = "512"
execution_role_arn = aws_iam_role.ecs_execution_role.arn
# ECS Container Definition
container_definitions = jsonencode([
{
name = "project-container"
image = aws_ecr_repository.project01.repository_url
portMappings = [
{
containerPort = 80
hostPort = 80
}
]
}
])
}
# AWS ECS Service
resource "aws_ecs_service" "project01_service" {
name = "project-service"
cluster = aws_ecs_cluster.project01.id
task_definition = aws_ecs_task_definition.project01_task.arn
launch_type = "FARGATE"
desired_count = 2
deployment_maximum_percent = 200
network_configuration {
subnets = [aws_subnet.public1.id,aws_subnet.public2.id]
security_groups = [aws_security_group.my_security_group.id]
assign_public_ip = "true"
}
depends_on = [aws_ecs_task_definition.project01_task]
}
# AWS Security Group
resource "aws_security_group" "my_security_group" {
name = "my-security-group"
description = "My Security Group Description"
vpc_id = aws_vpc.main.id
// Allow all incoming traffic
ingress {
from_port = 0
to_port = 0
protocol = "-1" # Indicates all protocols
cidr_blocks = ["0.0.0.0/0"]
}
// Allow all outgoing traffic
egress {
from_port = 0
to_port = 0
protocol = "-1" # Indicates all protocols
cidr_blocks = ["0.0.0.0/0"]
}
}
# AWS IAM Role for ECS Execution
resource "aws_iam_role" "ecs_execution_role" {
name = "ecs_execution_role"
assume_role_policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Action = "sts:AssumeRole",
Effect = "Allow",
Principal = {
Service = "ecs-tasks.amazonaws.com",
},
},
{
Action = "sts:AssumeRole",
Effect = "Allow",
Principal = {
Service = "events.amazonaws.com",
},
},
],
})
// Attach a policy granting ECR permissions
inline_policy {
name = "ecr_permissions"
policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Action = [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:BatchGetImage",
],
Resource = "*",
},
],
})
}
}
# AWS IAM Role Policy Attachment
resource "aws_iam_role_policy_attachment" "ecs_execution_role_attachment" {
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" # AmazonECSTaskExecutionRolePolicy grants permissions to pull images from ECR
role = aws_iam_role.ecs_execution_role.name
}
- Next, creates the CloudWatch Event rules, and this rule is triggered when there is a push action on the specified ECR repository.
- The CloudWatch Event Target is set to update the ECS service with the specified ECS cluster, IAM role, and ECS task definition.
# AWS CloudWatch Event Rule for ECR Image Push
resource "aws_cloudwatch_event_rule" "ecr_image_push" {
name = "ecr-image-push"
description = "Triggers ECS service update on ECR image push"
event_pattern = <<PATTERN
{
"source": [
"aws.ecr"
],
"detail-type": [
"ECR Image Action"
],
"detail": {
"action-type": [
"PUSH"
],
"repository-name": [
"${aws_ecr_repository.project01.name}"
]
}
}
PATTERN
}
# AWS CloudWatch Event Target for ECS Service Update
resource "aws_cloudwatch_event_target" "ecs_service_update" {
rule = aws_cloudwatch_event_rule.ecr_image_push.name
target_id = "ecs-service-update"
arn = aws_ecs_cluster.project01.arn
role_arn = aws_iam_role.ecs_execution_role.arn
ecs_target {
task_definition_arn = aws_ecs_task_definition.project01_task.arn
launch_type = "FARGATE"
platform_version = "LATEST"
network_configuration {
subnets = [aws_subnet.public1.id, aws_subnet.public2.id]
security_groups = [aws_security_group.my_security_group.id]
assign_public_ip = "true"
}
}
}
- A VPC is created with 2 public subnets, and please refer to the file as it contains the basics of VPC, which are easy to understand.
- Github actions automates the process of building and pushing a Docker image to an Amazon Elastic Container Registry (ECR)
- The job named
build-and-push
which runs on an Ubuntu latest virtual machine. - Here
actions/checkout
action, fetches the contents of the repository. - In the next step sets up AWS CLI with access key, secret key, and region stored in GitHub Secrets. It then uses the aws
ecr get-login-password
command to obtain a Docker login token for the specified ECR registry. - The workflow is named
Docker Build and Push
and it is triggered whenever there is a push event to the main branch. It builds the Docker image using the Dockerfile in the repository and tags it. It then pushes the image to the specified ECR repository with the latest tag.
- Once Infrastructure as Code (IAC) code for the AWS resources is applied, it will create the VPC, ECR, IAM roles, CloudWatch event rules, ECS, and task definitions.
- Updating the image with new changes and merging with the
main
branch triggers the GitHub Actions. Here, a sample Nginx Dockerfile is provided, which will be updated to the Elastic Container Registry (ECR) with the latest tag.
- The image is uploaded into the Elastic Container Registry.
- The update in the ECR triggers the Amazon CloudWatch Event Rule
- As part of the rule, it updates the cluster service with the new image change, which triggers the creation of a new task.
- Once the task is in the running state, assess it via the provided public IP
Here, I have created a sample CI/CD deployment to ECS via ECR using GitHub Actions as the CI/CD. This can be used for testing environments or by those who wish to learn these deployment procedures.