How to Deploy a Three-Tier Application on AWS EKS: A DevOps Engineer's Guide

Pravesh SudhaPravesh Sudha
11 min read

💡 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:

  1. 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.

  2. AWS CLI: The AWS Command Line Interface should be installed and configured on your machine.

  3. kubectl: The Kubernetes command-line tool, kubectl, must be installed to interact with your EKS cluster.

  4. Docker: Docker should be installed and running on your machine to build and manage Docker images.

  5. GitHub Repository: Access to a GitHub repository containing the source code for the React.js frontend, Node.js backend, and configuration for MongoDB.

  6. 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 :)

1
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