Table of Contents
Introduction
Project Overview
Technology Stack
Architecture Diagram
Step 1: Prerequisites
Step 2: Configuring GitLab as Version Control
Step 3: Preparing AWS Resources
Step 4: Building and Pushing the Docker Image
Step 5: Setting Up Amazon ECS with Fargate
Step 6: Creating the GitLab CI/CD Pipeline
Step 7: Adding Monitoring with AWS CloudWatch
Conclusion
Introduction
In this project, we will create an automated pipeline for building and deploying a Node.js application to Amazon ECS. The project showcases the use of GitLab as version control, Docker for containerization, and AWS services like ECS, ECR, and CodePipeline for orchestration and deployment.
By the end of this guide, you will have a complete understanding of the CI/CD workflow in AWS, which is critical for modern DevOps practices.
Project Overview
Objective
We will automate the following tasks:
- Build a Node.js application.
- Containerize the application using Docker.
- Push the Docker image to Amazon ECR.
- Deploy the container to Amazon ECS using Fargate.
- Use GitLab CI/CD for continuous integration and deployment.
- Add monitoring and notifications using AWS CloudWatch and SNS.
Technology Stack
AWS Services:
- Amazon ECS (Elastic Container Service)
- Amazon ECR (Elastic Container Registry)
- AWS CodePipeline
- AWS Security Hub
- Amazon EventBridge
- Amazon SNS
- AWS CloudWatch
Other Tools:
GitLab: Source code management and CI/CD pipeline.
Docker: Application containerization.
Node.js: Sample web application framework.
Architecture Diagram
The high-level architecture for this project is as follows:
- Developers push code to GitLab.
- GitLab CI/CD pipeline builds and pushes a Docker image to Amazon ECR.
- The image is deployed to Amazon ECS (Fargate).
- Monitoring and logging are done using AWS CloudWatch.
- Notifications are sent using Amazon SNS.
Here's a visual representation of the flow:
.
2.2: Clone the Repository
- After forking, clone it to your local machine:
git clone https://gitlab.com/<your-username>/node-app.git
cd node-app
- Verify that the repository contains the following:
Application Code (Node.js):
server.jspackage.json
Dockerfile for containerization.
.gitlab-ci.ymlfor CI/CD pipeline (we'll modify this later).
2.3: Push Updates (Optional)
If you want to make changes to the repository (e.g., updating code, adding more files), push the updates back:
git add .
git commit -m "Updated application for CI/CD project"
git push origin main
Step 3: Preparing AWS Resources
This step remains largely the same, but now it aligns with the Node.js application you're deploying.
3.1: Create an Amazon ECR Repository
Create a private ECR repository to store the Docker images for your application:
aws ecr create-repository --repository-name node-app
3.2: Authenticate Docker with ECR
Authenticate your local Docker client with the ECR registry:
aws ecr get-login-password --region <region> | docker login --username AWS --password-stdin <account_id>.dkr.ecr.<region>.amazonaws.com
3.3: Create an ECS Cluster
Create an ECS cluster for running the application containers:
aws ecs create-cluster --cluster-name node-app-cluster
3.4: IAM Role, VPC, and Security Groups
Follow the steps outlined earlier to:
- Create the IAM task execution role.
- Set up the VPC, subnets, and security groups.
- Open port
3000in the security group for application traffic.
Step 4: Building and Pushing the Docker Image
Using the cloned GitLab application, build and push the Docker image.
4.1: Build the Docker Image
Navigate to the repository's root directory and build the image:
docker build -t node-app .
4.2: Tag the Docker Image
Tag the image for your ECR repository:
docker tag node-app:latest <account_id>.dkr.ecr.<region>.amazonaws.com/node-app:latest
4.3: Push the Image to ECR
Push the image to your Amazon ECR repository:
docker push <account_id>.dkr.ecr.<region>.amazonaws.com/node-app:latest
4.4: Verify the Image
Confirm that the image has been successfully pushed:
aws ecr list-images --repository-name node-app
Step 5: Setting Up Amazon ECS with Fargate
Amazon ECS (Elastic Container Service) is a managed service that allows you to run containers. We are using Fargate, a serverless option that eliminates the need to manage EC2 instances manually. Here's a detailed walkthrough of setting up ECS for our project:
5.1: Create a Cluster
A cluster is a logical grouping of resources needed to run your tasks or services.
Run the following command to create a cluster:
aws ecs create-cluster --cluster-name node-app-cluster
This command creates a new cluster named node-app-cluster.
Verify the Cluster:
aws ecs list-clusters
Ensure the node-app-cluster is listed as one of the clusters.
5.2: Define a Task Definition
A task definition specifies the container settings (e.g., memory, CPU, ports) for running your application. Think of it as a blueprint for your containerized application.
- Create a
task-def.jsonfile:
{
"family": "node-app-task",
"executionRoleArn": "arn:aws:iam::account_id:role/ecsTaskExecutionRole",
"networkMode": "awsvpc",
"containerDefinitions": [
{
"name": "node-app-container",
"image": "<account_id>.dkr.ecr.<region>.amazonaws.com/node-app:latest",
"memory": 512,
"cpu": 256,
"essential": true,
"portMappings": [
{
"containerPort": 3000,
"hostPort": 3000,
"protocol": "tcp"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/node-app",
"awslogs-region": "<region>",
"awslogs-stream-prefix": "ecs"
}
}
}
],
"requiresCompatibilities": ["FARGATE"],
"cpu": "256",
"memory": "512"
}
- Replace
<account_id>and<region>with your AWS account ID and region. - Ensure
executionRoleArnpoints to a valid ECS task execution role.
- Register the task definition with ECS:
aws ecs register-task-definition --cli-input-json file://task-def.json
This registers the blueprint with ECS.
5.3: Create a Service to Manage the Task
An ECS service ensures that the required number of tasks are running and enables load balancing for the tasks.
- Create a service:
aws ecs create-service \
--cluster node-app-cluster \
--service-name node-app-service \
--task-definition node-app-task \
--desired-count 1 \
--launch-type FARGATE \
--network-configuration "awsvpcConfiguration={subnets=[subnet-xxx],securityGroups=[sg-xxx],assignPublicIp=ENABLED}" \
--region <region>
- Replace
subnet-xxxandsg-xxxwith the IDs of your VPC's public subnet and security group.
desired-countis the number of tasks to run.
Verify the Service:
aws ecs describe-services --cluster node-app-cluster --services node-app-service
Ensure the service is active and running.
5.4: Test the Application
- Find the public IP of your task:
aws ecs list-tasks --cluster node-app-cluster
Use the task ID to describe the task and find the public IP address:
aws ecs describe-tasks --cluster node-app-cluster --tasks <task_id>
- Access your application in the browser using the public IP:
http://<public_ip>:3000
Step 6: Creating the GitLab CI/CD Pipeline
GitLab CI/CD automates the build and deployment process, ensuring the application is always up to date. Follow these steps to set up the pipeline:
6.1: Add .gitlab-ci.yml
This file defines the stages, jobs, and commands for the pipeline.
- Add the following
.gitlab-ci.ymlfile to the root of your project:
stages:
- build
- deploy
build:
image: docker:latest
services:
- docker:dind
script:
- docker build -t node-app .
- docker tag node-app <account_id>.dkr.ecr.<region>.amazonaws.com/node-app:latest
- aws ecr get-login-password --region <region> | docker login --username AWS --password-stdin <account_id>.dkr.ecr.<region>.amazonaws.com
- docker push <account_id>.dkr.ecr.<region>.amazonaws.com/node-app:latest
deploy:
image: amazon/aws-cli:latest
script:
- aws ecs update-service --cluster node-app-cluster --service node-app-service --force-new-deployment --region <region>
Key Steps Explained:
Build Stage:
- Builds a Docker image from your
Dockerfile. - Tags the image with the ECR repository URL.
- Pushes the image to Amazon ECR.
- Builds a Docker image from your
Deploy Stage:
- Updates the ECS service to use the latest image in Amazon ECR.
6.2: Configure Variables in GitLab
Go to Settings → CI/CD → Variables in your GitLab repository and add the following environment variables:
AWS_ACCESS_KEY_ID: Your AWS access key.
AWS_SECRET_ACCESS_KEY: Your AWS secret key.
AWS_REGION: Your AWS region.
Step 7: Adding Monitoring with AWS CloudWatch
CloudWatch enables monitoring and logging for your application and infrastructure.
7.1: Set Up CloudWatch Logs
Create a Log Group:
aws logs create-log-group --log-group-name /ecs/node-app
Create a Log Stream:
aws logs create-log-stream --log-group-name /ecs/node-app --log-stream-name app-logs
Integrate Logs with ECS Task Definition:
In the task definition (task-def.json), ensure thelogConfigurationsection is as follows:
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/node-app",
"awslogs-region": "<region>",
"awslogs-stream-prefix": "ecs"
}
}
7.2: Set Up Alarms for Monitoring
You can set up alarms in CloudWatch to monitor metrics such as CPU usage, memory, and application errors.
Create an Alarm:
aws cloudwatch put-metric-alarm \
--alarm-name HighCPUUsage \
--metric-name CPUUtilization \
--namespace AWS/ECS \
--statistic Average \
--period 300 \
--threshold 80 \
--comparison-operator GreaterThanThreshold \
--evaluation-periods 1 \
--alarm-actions <sns_topic_arn>
Receive Notifications:
Create an SNS topic to send notifications:
aws sns create-topic --name ecs-alerts
aws sns subscribe --topic-arn <sns_topic_arn> --protocol email --notification-endpoint <your_email>
Now, you will receive email notifications for high CPU usage or other alerts.
👤 Author
|| Follow me on GitHub for more DevOps content!
SOCIAL SHARE CARD GENERATOR