How to Deploy a Three-Tier Application on AWS EKS: A DevOps Engineer's Guide
Table of contents
- 💡 Introduction
- 💡 Prerequisites
- 💡 Getting Started (Setting Up a EC2 instance)
- 💡 Cloning the Project
- 💡 Pushing Docker Images to ECR (Elastic Container Registry)
- 💡 Creating an EKS Cluster
- 💡 Creating Manifest files for the Cluster
- 💡 Applying the Manifest files
- 💡 Installing ALB (Application Load Balancer)
- 💡 Conclusion
💡 Introduction
Welcome to the world of DevOps, in today’s cloud-centric world, deploying applications efficiently and reliably is a crucial skill for any DevOps engineer. One of the most powerful setups is a three-tier application architecture, which separates the application into three distinct layers: frontend, backend, and database. In this blog, I will walk you through the process of deploying such a three-tier application on AWS Elastic Kubernetes Service (EKS).
We’ll be using a GitHub project that comprises a React.js frontend, a Node.js backend, and a MongoDB database. To streamline the deployment, we’ll dockerize the frontend and backend components and push these Docker images to Amazon Elastic Container Registry (ECR). For MongoDB, we’ll leverage the pre-built image available on Docker Hub. Finally, we’ll deploy the entire stack on an AWS EKS cluster, configure services for communication between the tiers, and set up an ingress controller with an Application Load Balancer to manage external traffic.
💡 Prerequisites
Before we dive into the deployment process, ensure you have the following in place:
AWS Account: You’ll need an active AWS account with necessary permissions to create and manage EKS clusters, IAM roles, EC2 instances, ECR repositories, and Application Load Balancers.
AWS CLI: The AWS Command Line Interface should be installed and configured on your machine.
kubectl: The Kubernetes command-line tool,
kubectl
, must be installed to interact with your EKS cluster.Docker: Docker should be installed and running on your machine to build and manage Docker images.
GitHub Repository: Access to a GitHub repository containing the source code for the React.js frontend, Node.js backend, and configuration for MongoDB.
Basic Knowledge: Familiarity with Docker, Kubernetes, and AWS services like EKS, IAM, and ECR will be beneficial.
💡 Getting Started (Setting Up a EC2 instance)
Before starting the project, we need to setup up a machine on which we will make deployments on Kubernetes server. For this project, we will create an EC2 instance (Ubuntu, t2.micro). Give a relevant name to the instance (Like three-tier-application) and create a key-pair (named default-ec2.pem) to login to the instance from host machine. Configuration of the instance can be seen below in the image:
After creating the instance, you need to open terminal, navigate to the folder in which you downloaded the key-pair(default-ec2.pem) and run the following command:
chmod 400 "default-ec2.pem"
ssh -i "default-ec2.pem" ubuntu@<Public IPv4 DNS>.compute-1.amazonaws.com
# Change <Public IPv4 DNS> with the actual IPv4 DNS, it will change as you restart the instance.
# Click 'Yes' and Login.
After Login, run the following command to install the necessary softwares:
sudo apt update && sudo apt upgrade
# This will update all the dependencies
# Install Docker
sudo apt install docker.io -y
sudo usermod -aG docker $USER
# After installing Docker, run the following command to reboot the server
sudo reboot
After running these commands, reconnect the server using the command specified above.
💡 Cloning the Project
This project is posted on my github, you can clone it from the following command:
git clone https://github.com/Pravesh-Sudha/TWSThreeTierAppChallenge.git
cd TWSThreeTierAppChallenge/Application-Code/frontend
This command will make clone the project in a directory named TWSThreeTierAppChallenge. We will start with building the frontend, on basis of project requirements, I have made a Dockerfile for the project in the frontend directory. Here is the dockerfile:
# Base Image
FROM node:14
# Work Directory
WORKDIR /usr/src/app
# Copying all the necesary content to run app
COPY package*.json ./
# Building the project
RUN npm insatll
# Copying every files
COPY . .
# Starting the project
CMD ["npm","start"]
After creating the dockerfile, run the following command to make a docker image out of it:
# Make sure you are in the frontend directory of project
cd TWSThreeTierAppChallenge/Application-Code/frontend
docker build -t frontend .
# This will create a docker image for the frontend.
Do the same for backend by navigating to backend folder in Application-code directory. Here is the Dockerfile for backend:
# Base Image
FROM node:14
# Work Directory
WORKDIR /usr/src/app
# Copying all the necesary content to run app
COPY package*.json ./
# Building the project
RUN npm install
# Copying every files
COPY . .
# Starting the project
CMD ["node","index.js"]
# Make sure you are in backend directory of the project
cd TWSThreeTierAppChallenge/Application-Code/backend
docker build -t backend .
This will create two docker images tagged frontend and backend Like shown below:
Before running the docker containers, we need to make sure that ports are open in our security groups of the instances. To do that navigate to the security tab of your instance and click Edit inbound rules.
Add port 3000 and 3500 in the inbound rules and save it.
You can check your frontend is running by using the following command:
docker run -d -p 3000:3000 frontend:latest
Get the Public IP address of your instances like shown below:
Copy this IP address and add ":3000" at the end and the todo list web application's frontend will appear on your screen list this:
💡 Pushing Docker Images to ECR (Elastic Container Registry)
After testing both frontend and backend images, we are good to push them to ECR by making a public/private repository and pushing our images in that. This step require aws CLI, so make sure you have installed it on the instance. Follow these steps to install it:
# Navigate to home directory
cd
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
sudo apt install unzip
unzip awscliv2.zip
sudo ./aws/install -i /usr/local/aws-cli -b /usr/local/bin --update
aws configure
# This will install the AWS CLI in the machine and will ask you to type
# Access key and Secret Access key which you can create in IAM service.
# Go to IAM -> Users -> Select your current user -> Create Access key
# Select creating for AWS CLI
After configuring the credentials, navigate to the ECR dashboard and create two public repositories named three-tier-frontend and three-tier-backend like shown in the image below:
Go in both directories and click view Push commands to get the commands to push it. (You can skip the step 2 as we have already build the images). It will look like this:
Do the same for backend image.
After successfully pushing the images to ECR, we are good to deal with Kubernetes (k8s) cluster. In this project, we will use EKS cluster which will cost you some money but the knowledge and understanding is truly worthy, so definitely give it a try. To use EKS from CLI, we need to have EKSCTL which will facilitate communication between host and cluster. To install eksctl and kubectl (which is controller for K8s cluster), follow the following steps:
# Install Kubectl
curl -o kubectl https://amazon-eks.s3.us-west-2.amazonaws.com/1.19.6/2021-01-05/bin/linux/amd64/kubectl
chmod +x ./kubectl
sudo mv ./kubectl /usr/local/bin
kubectl version --short --client
# Install eksctl
curl --silent --location "https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp
sudo mv /tmp/eksctl /usr/local/bin
eksctl version
After ensuring Kubectl and eksctl are installed properly, we are good to go for setting up our EKS cluster.
💡 Creating an EKS Cluster
We will use the following commands to create a Kubernetes cluster with maximum and minimum nodes set to 2:
eksctl create cluster --name three-tier-cluster --region us-east-1 --node-type t2.medium --nodes-min 2 --nodes-max 2
This will create a EKS cluster named three-tier-cluster in region us-east-1 with 2 t2.medium node. This will take some time to create the cluster. The output will look like this:
After successfully creation of EKS Cluster, we need to configure kubectl for our EKS cluster. Use the following command to do configure it:
aws eks update-kubeconfig --region us-east-1 --name three-tier-cluster
# Use the following command to check whether the config is updated or not
kubectl get nodes
# It will show the two nodes like shown below.
💡 Creating Manifest files for the Cluster
In Kubernetes, we create manifest file for deployment, services, ingress, etc. in YAML format. This method of creating manifest file is called declarative way of applying file. I have created manifest file for kubernetes files which you can from this following link.
Here is the manifest file for deployment and service(backend):
# Backend Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
namespace: three-tier
labels:
role: api
env: demo
spec:
replicas: 2
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 25%
selector:
matchLabels:
role: api
template:
metadata:
labels:
role: api
spec:
imagePullSecrets:
- name: ecr-registry-secret
containers:
- name: api
image: <Your-backend-image-URI-from-URI>
imagePullPolicy: Always
env:
- name: MONGO_CONN_STR
value: mongodb://mongodb-svc:27017/todo?directConnection=true
- name: MONGO_USERNAME
valueFrom:
secretKeyRef:
name: mongo-sec
key: username
- name: MONGO_PASSWORD
valueFrom:
secretKeyRef:
name: mongo-sec
key: password
ports:
- containerPort: 3500
livenessProbe:
httpGet:
path: /ok
port: 3500
initialDelaySeconds: 2
periodSeconds: 5
readinessProbe:
httpGet:
path: /ok
port: 3500
initialDelaySeconds: 5
periodSeconds: 5
successThreshold: 1
#Backend Service
apiVersion: v1
kind: Service
metadata:
name: api
namespace: three-tier
spec:
ports:
- port: 3500
protocol: TCP
type: ClusterIP
selector:
role: api
Here is the manifest file for deployment and service(frontend):
# Frontend Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
namespace: three-tier
labels:
role: frontend
env: demo
spec:
replicas: 1
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 25%
selector:
matchLabels:
role: frontend
template:
metadata:
labels:
role: frontend
spec:
imagePullSecrets:
- name: ecr-registry-secret
containers:
- name: frontend
image: <Your-frontend-image-uri-from-ECR>
imagePullPolicy: Always
env:
- name: REACT_APP_BACKEND_URL
value: "http://backend.amanpathakdevops.study/api"
ports:
- containerPort: 3000
# Frontend Service
apiVersion: v1
kind: Service
metadata:
name: frontend
namespace: three-tier
spec:
ports:
- port: 3000
protocol: TCP
type: ClusterIP
selector:
role: frontend
Here is the ingress.yaml file for routing:
# Ingress file
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: mainlb
namespace: three-tier
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}]'
spec:
ingressClassName: alb
rules:
- host: backend.amanpathakdevops.study
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api
port:
number: 3500
- path: /
pathType: Prefix
backend:
service:
name: frontend
port:
number: 3000
Here is the manifest file for Mongo:
# Mongo Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: three-tier
name: mongodb
spec:
replicas: 1
selector:
matchLabels:
app: mongodb
template:
metadata:
labels:
app: mongodb
spec:
containers:
- name: mon
image: mongo:4.4.6
command:
- "numactl"
- "--interleave=all"
- "mongod"
- "--wiredTigerCacheSizeGB"
- "0.1"
- "--bind_ip"
- "0.0.0.0"
ports:
- containerPort: 27017
env:
- name: MONGO_INITDB_ROOT_USERNAME
valueFrom:
secretKeyRef:
name: mongo-sec
key: username
- name: MONGO_INITDB_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mongo-sec
key: password
volumeMounts:
- name: mongo-volume
mountPath: /data/db
volumes:
- name: mongo-volume
persistentVolumeClaim:
claimName: mongo-volume-claim
# Mongo Service
apiVersion: v1
kind: Service
metadata:
namespace: three-tier
name: mongodb-svc
spec:
selector:
app: mongodb
ports:
- name: mongodb-svc
protocol: TCP
port: 27017
targetPort: 27017
# Mongo Secret
apiVersion: v1
kind: Secret
metadata:
namespace: three-tier
name: mongo-sec
type: Opaque
data:
password: cGFzc3dvcmQxMjM= #Three-Tier-Project
username: YWRtaW4= #admin
Make separate files for deployment.yaml, service.yaml, secrets.yaml for all three directory (frontend, backend and database MongoDB).
All these files are available in the github repository
💡 Applying the Manifest files
After creating all the manifest files, we need to apply them with help of kubectl. We will start with creating MongoDb as it is essential to create backend and frontend. Follow the steps on your instance to apply the manifest files:
cd TWSThreeTierAppChallenge/Kubernetes-Manifests-file/Database
kubectl create namespace three-tier
kubectl apply -f secrets.yaml
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
This will create the MongoDB in Kubernetes cluster. Now we will apply the backend configuration. Use the following command to apply the backend files:
cd TWSThreeTierAppChallenge/Kubernetes-Manifests-file/Backend
# Before applying these files, remember to change the Image URI to ECR URI in deployment file of backend.
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
# Get pod info
kubectl get pods -n three-tier
After Applying these files, you can use the following command to check that the database is connected or not:
kubectl logs <pod name> -n three-tier
As Backend and API is ready, now we can apply frontend manifest file. Use the following command to apply these files:
cd TWSThreeTierAppChallenge/Kubernetes-Manifests-file/Frontend
# Before applying these files, remember to change the Image URI to ECR URI in deployment file of backend.
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
After applying all these, this will the output of all resources in three-tier namespace:
💡 Installing ALB (Application Load Balancer)
To ensure communication with outside world, we need an Application Load Balancer. We can create it by using following commands:
# Navigate to home directory
cd
curl -O https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.5.4/docs/install/iam_policy.json
aws iam create-policy --policy-name AWSLoadBalancerControllerIAMPolicy --policy-document file://iam_policy.json
eksctl utils associate-iam-oidc-provider --region=us-east-1 --cluster=three-tier-cluster --approve
# Change your Policy ARN
eksctl create iamserviceaccount --cluster=three-tier-cluster --namespace=kube-system --name=aws-load-balancer-controller --role-name AmazonEKSLoadBalancerControllerRole --attach-policy-arn=arn:aws:iam::<Your-policy-ARN> --approve --region=us-east-1
After the successful creation of service account, we need to deploy the AWS Load Balancer Controller, use the following commands to do it:
# Navigate to home Directory
cd
sudo snap install helm --classic
helm repo add eks https://aws.github.io/eks-charts
helm repo update eks
helm install aws-load-balancer-controller eks/aws-load-balancer-controller -n kube-system --set clusterName=three-tier-cluster --set serviceAccount.create=false --set serviceAccount.name=aws-load-balancer-controller
kubectl get deployment -n kube-system aws-load-balancer-controller
cd TWSThreeTierAppChallenge/Kubernetes-Manifests-file/
kubectl apply -f ingress.yaml
This will create ingress and now you can test your application. Just add the address with a CNAME and sub domain. You can access the application on that domain:
An example of this application is as follow (Here the host is trainwithshubham.com and sub-domain is challenge):
After successfully completion of project, make sure to delete the resources for avoiding unwanted bills. Use the following command to wipe out every resource:
eksctl delete cluster --name three-tier-cluster --region us-east-1
# This will take 10-15 mins to complete, have patience and lets the CLI
# delete the resources
💡 Conclusion
Deploying a three-tier application on AWS EKS demonstrates the power and flexibility of modern cloud infrastructure. By breaking down the application into frontend, backend, and database layers, we’ve ensured that each component can be managed, scaled, and updated independently. Throughout this blog, we've walked through the entire process—from dockerizing our React.js and Node.js components to configuring the EKS cluster, setting up services, and implementing an ingress controller with an Application Load Balancer.
If you found this blog informative, give this blog a like and checkout my X(twitter) and LinkedIn for more useful post. Till then, Have an amazing day
Happy Coding :)
Subscribe to my newsletter
Read articles from Pravesh Sudha directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Pravesh Sudha
Pravesh Sudha
I am a Self Taught developer from India with major interest in open source, devops and Coding. Pursuing a Bachelors in Philosophy has helped me a lot to drive right in my career. Landed in tech, now I am exploring tech blogging for fun