Three-tier EKS Project : A Full Guide 🚀

In this project we will deploy a 3-Tier Language-Vote-App using AWS EKS.

STEP 1 > Launch Instance Create AWS EC2 instance Type > T2.Large

Connect this instance through SSH

STEP 2 > Git Hub

CMD > git clone https://github.com/NIHALPAPA/EKS-3-tier-app.git

STEP 3 > Build & Push API Image To Docker Hub

Now we will build API image. So first we will install docker for this use command >

CMD > sudo apt install docker.io -y

CMD > sudo usermod -aG docker $USER

CMD > sudo reboot

Now we will make Dockerfile, so go to API directory and write command >

CMD > vim Dockerfile

Now write code as shown below >

CONTENT >

Use an official Golang runtime as a parent image

FROM golang:1.17 as build

Set the working directory inside the container

WORKDIR /app

Copy the local package files to the container's workspace

COPY go.mod . COPY go.sum .

Download and install any required dependencies

RUN go mod download

Copy the rest of your application source code

COPY . .

Build the Go application

RUN go build -o main

Expose the port your application will listen on

EXPOSE 8080

Command to run your application

CMD ["/app/main"]

Now we have to build "Docker Image" from "Dockerfile", for this we use command as shown below >

CMD > docker build . -t voting-app-api:latest

visit docker hub on google.com sign-up for docker account

If you want to push or pull docker than there will be a command for it CMD > docker login (in command prompt or terminal)

ID : docker login -u your Id

PASS : your Id

CMD > docker tag voting-app-api:latest papanihal360/voting-app-api:latest

CMD > docker push papanihal360/voting-app-api:latest

STEP 4 > Install AWS CLI v2 (run this command in home directory)

CMD > 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

CMD > aws configure

Now to control K8s Cluster on EKS we will need to install kubectl tool. To install kubectl use command (run this command in home directory) >

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

Now to make K8s Cluster on EKS we will need to install eksctl tool. To install eksctl use command (run this command in home directory) >

curl --silent --location "[https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname](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

Now to setup EKS Cluster use command >

eksctl create cluster --name voting-app-eks-cluster --region us-west-2 --node-type t2.medium --nodes-min 2 --nodes-max 2

Now go cluster “Add-ons” → Click “Get more add-ons” → select "Amazon EBS CSI Driver"

Now click “Next” → again “Next” → Click “Create”. Now add 1 policy in Node IAM role. Go to node and click “IAM role” → click "Attach Policy" → search "AmazonEBSCSIDriverPolicy" and add this Policy.

Now check nodes, use command >

CMD > kubectl get nodes

We will get a refused error because we haven’t set up the context yet. Lets set up context, for this use command >

CMD > aws eks update-kubeconfig --name voting-app-eks-cluster --region us-east-1

CMD > kubectl get nodes

STEP 5: Setup & Run Mongo

Now we will create a Mongo stateful set yaml file but before that we will create a namespace so for this use command >

kubectl create namespace nihal

Now when we want to work within a specific namespace for our Kubernetes operations. We have to set our namespace as current, for this use command >

kubectl config set-context --current --namespace nihal

After running this command, any subsequent kubectl commands we execute will be scoped to the nihal namespace, unless we specify a different namespace explicitly in our commands. This can be particularly useful when we have multiple namespaces in our Kubernetes cluster and we want to ensure that our operations are isolated to a specific namespace.

CMD > kubectl create statefulset.yaml

CONTENT >

mongo-stateful set yaml file

apiVersion: apps/v1 kind: StatefulSet metadata: name: mongo namespace: nihal spec: serviceName: mongo replicas: 3 selector: matchLabels: role: db template: metadata: labels: role: db env: demo replicaset: rs0.main spec: affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchExpressions: - key: replicaset operator: In values: - rs0.main topologyKey: kubernetes.io/hostname terminationGracePeriodSeconds: 10 containers: - name: mongo image: mongo:4.2 command: - "numactl" - "--interleave=all" - "mongod" - "--wiredTigerCacheSizeGB" - "0.1" - "--bind_ip" - "0.0.0.0" - "--replSet" - "rs0" ports: - containerPort: 27017 volumeMounts: - name: mongodb-persistent-storage-claim mountPath: /data/db volumeClaimTemplates: - metadata: name: mongodb-persistent-storage-claim spec: accessModes: - ReadWriteOnce storageClassName: gp2 resources: requests: storage: 1Gi

Now to apply a Mongo stateful set with Persistent volumes, run the command >

CMD > #to apply mongo-statefulset manifest file kubectl apply -f mongo-statefulset.yaml

CMD > kubectl get pods

Now go to Aws console and click on nodes and storage. We will see now new 1Gb storage has been added to both nodes.

Check whether persistent volumes are created or not

CMD > kubectl get pvc

Now we will create a mongo-service yaml file, write command >

CMD > kubectl create mongo-service.yaml

CONTENT >

mongo-service yaml file

apiVersion: v1 kind: Service metadata: name: mongo namespace: nihal labels: role: db env: demo spec: ports:

  • port: 27017 targetPort: 27017 selector: role: db

Now to apply a mongo-service yaml file, run the command >

CMD > kubectl apply -f mongo-service.yaml kubectl get svc -n nihal

Now let’s go inside the mongo-0 pod and we have to initialise the Mongo database Replica set >

CMD > kubectl get pods kubectl exec -it mongo-0 -- mongo

In the terminal run the following command >

rs.initiate(); sleep(2000); rs.add("mongo-1.mongo:27017"); sleep(2000); rs.add("mongo-2.mongo:27017"); sleep(2000); cfg = rs.conf(); cfg.members[0].host = "mongo-0.mongo:27017"; rs.reconfig(cfg, {force: true}); sleep(5000);

Note: Wait until this command completes successfully, it typically takes 10-15 seconds to finish and completes with the message: bye Load the Data in the database by running this command >

CMD > use langdb

Now paste given below data in the terminal >

db.languages.insert({"name" : "csharp", "codedetail" : { "usecase" : "system, web, server-side", "rank" : 5, "compiled" : false, "homepage" : "https://dotnet.microsoft.com/learn/csharp", "download" : "https://dotnet.microsoft.com/download/", "votes" : 0}}); db.languages.insert({"name" : "python", "codedetail" : { "usecase" : "system, web, server-side", "rank" : 3, "script" : false, "homepage" : "https://www.python.org/", "download" : "https://www.python.org/downloads/", "votes" : 0}}); db.languages.insert({"name" : "javascript", "codedetail" : { "usecase" : "web, client-side", "rank" : 7, "script" : false, "homepage" : "https://en.wikipedia.org/wiki/JavaScript", "download" : "n/a", "votes" : 0}}); db.languages.insert({"name" : "go", "codedetail" : { "usecase" : "system, web, server-side", "rank" : 12, "compiled" : true, "homepage" : "https://golang.org", "download" : "https://golang.org/dl/", "votes" : 0}}); db.languages.insert({"name" : "java", "codedetail" : { "usecase" : "system, web, server-side", "rank" : 1, "compiled" : true, "homepage" : "https://www.java.com/en/", "download" : "https://www.java.com/en/download/", "votes" : 0}}); db.languages.insert({"name" : "nodejs", "codedetail" : { "usecase" : "system, web, server-side", "rank" : 20, "script" : false, "homepage" : "https://nodejs.org/en/", "download" : "https://nodejs.org/en/download/", "votes" : 0}});

Now to see data in proper way use command given below >

CMD > db.languages.find().pretty();

Now to exit from mongo-0 pod use command >

CMD > exit #exit from conatiner

To confirm run this in the terminal >

kubectl exec -it mongo-0 -- mongo --eval "rs.status()" | grep "PRIMARY|SECONDARY"

Now we will create a mongo-secret yaml file, write command >

CMD > kubectl create mongo-secret.yaml

CONTENT >

mongo-secret yaml file

apiVersion: v1 kind: Secret metadata: name: mongodb-secret namespace: nihal data: username: c3VkaGEK
password: eWFkYXYK

In mongo-secret.yaml file we will write encryted password so how we can make encrypted password. Write echo 'sudha' | base64 and enter and we get our encryted username or password. Now how we can decode it? For this write echo 'encrypted username or password' | base64 --decode and we will get our real username or password. Now to apply a mongo-secret yaml file, run the command >

CMD > kubectl apply -f mongo-sercet.yaml

STEP 6: Setup & Run API Now we will create a API deployment yaml file so for this use command >

CMD > kubectl create api-deployment.yaml

api-deployment yaml file

apiVersion: apps/v1 kind: Deployment metadata: name: api namespace: nihal 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: containers: - name: api image: sudhajobs0107/voting-app-api:latest #change image name imagePullPolicy: Always env: - name: MONGO_CONN_STR value: mongodb://mongo-0.mongo,mongo-1.mongo,mongo-2.mongo:27017/langdb?replicaSet=rs0 - name: MONGO_USERNAME valueFrom: secretKeyRef: name: mongodb-secret key: username - name: MONGO_PASSWORD valueFrom: secretKeyRef: name: mongodb-secret key: password ports: - containerPort: 8080 livenessProbe: httpGet: path: /ok port: 8080 initialDelaySeconds: 2 periodSeconds: 5 readinessProbe: httpGet: path: /ok port: 8080 initialDelaySeconds: 5 periodSeconds: 5 successThreshold: 1

Now to apply a api deployment file, run the command

#to apply manifest file

CMD > kubectl apply -f api-deployment.yaml

#to check pods

CMD > kubectl get pods

CMD > kubectl get all

Now we will create a api-service yaml file, write command >

CMD > kubectl create api-service.yaml

api-service yaml file

#Imperative command to create service to expose frontend deployment #kubectl expose deploy api \ --name=api \ --type=LoadBalancer \ --port=80 \ --target-port=8080 apiVersion: v1 kind: Service metadata: name: api labels: app: api spec: selector: app: api ports: - protocol: TCP port: 80 targetPort: 8080 type: LoadBalancer

Now we can apply api-service yaml file or directly expose, run the command >

CMD > kubectl apply -f api-service.yaml

CMD > kubectl get svc

Now we will see 1 Load Balancer will be created in your AWS account

Now set the environment variable >

{ API_ELB_PUBLIC_FQDN=$(kubectl get svc api -ojsonpath="{.status.loadBalancer.ingress[0].hostname}") until nslookup $API_ELB_PUBLIC_FQDN >/dev/null 2>&1; do sleep 2 && echo waiting for DNS to propagate...; done curl $API_ELB_PUBLIC_FQDN/ok echo }

STEP 7: Setup & Run Frontend Now we will make Dockerfile for frontend, so go to frontend directory and write command >

CMD > vim Dockerfile

Use Node.js 16 image

FROM node:16 as build

WORKDIR /app COPY package*.json ./ RUN npm install --legacy-peer-deps COPY . . RUN npm run build

Stage 2: Serve the React app using NGINX

FROM nginx:alpine

Remove the default NGINX welcome page

RUN rm -rf /usr/share/nginx/html/*

Copy the built React app from the previous stage

COPY --from=build /app/build /usr/share/nginx/html

Expose port 80 (default HTTP port)

EXPOSE 80

Start NGINX

CMD ["nginx", "-g", "daemon off;"]

Now we have to build "Docker Image" from "Dockerfile", for this we use command as shown below >

CMD > docker build . -t voting-app-frontend:latest

CMD > docker tag voting-app-frontend:latest papanihal360/voting-app-frontend:latest

CMD > docker push papanihal360/voting-app-frontend:latest

CMD > Now we will create a Frontend deployment yaml file so for this use command >

CMD > kubectl create frontend-deployment.yaml

CONTENT >

api-deployment yaml file

apiVersion: apps/v1 kind: Deployment metadata: name: frontend namespace: nihal labels: role: frontend env: demo spec: replicas: 2 strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 25% selector: matchLabels: role: frontend template: metadata: labels: role: frontend spec: containers: - name: frontend image: papanihal360/voting-app-frontend:latest #change image name imagePullPolicy: Always env: - name: REACT_APP_APIHOSTPORT value: aa00164c0518041098c478e090c33bc6-783999949.us-east-2.elb.amazonaws.com #Here add your API_EXTERNAL_IP manually ports: - containerPort: 80

Now to apply a frontend deployment file, run the command >

CMD > kubectl apply -f frontend-deployment.yaml

CMD > kubectl get pods
kubectl get all

Now we will create a frontend-service yaml file, write command >

CMD > kubectl create frontend-service.yaml

CONTENT >

frontend-service yaml file

#Imperative command to create service to expose frontend deployment #kubectl expose deploy frontend \ --name=frontend \ --type=LoadBalancer \ --port=80 \ --target-port=80 apiVersion: v1 kind: Service metadata: name: frontend labels: app: frontend spec: selector: app: frontend ports: - protocol: TCP port: 80 targetPort: 80 type: LoadBalancer

Now we can apply frontend-service yaml file or directly expose, run the command >

CMD > kubectl apply -f frontnend-service.yaml CMD > kubectl get svc -n nihal

Next, set the environment variable >

{ FRONTEND_ELB_PUBLIC_FQDN=$(kubectl get svc frontend -ojsonpath="{.status.loadBalancer.ingress[0].hostname}") until nslookup $FRONTEND_ELB_PUBLIC_FQDN >/dev/null 2>&1; do sleep 2 && echo waiting for DNS to propagate...; done curl -I $FRONTEND_ELB_PUBLIC_FQDN }

Now generate the Frontend URL for browsing. In the terminal run the following command >

CMD > echo http://$FRONTEND_ELB_PUBLIC_FQDN

After the voting application has loaded successfully, vote by clicking on several of the +1 buttons. This will generate AJAX traffic which will be sent back to the API via the API’s assigned ELB. Query the MongoDB database directly to observe the updated vote data. In the terminal execute the following command:

CMD > kubectl exec -it mongo-0 -- mongo langdb --eval "db.languages.find().pretty()"

Our 3-Tier-Language-Vote-App Project is completed 😄

0
Subscribe to my newsletter

Read articles from NIHAL MOHAMAD ARIF PAPA directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

NIHAL MOHAMAD ARIF PAPA
NIHAL MOHAMAD ARIF PAPA

Aspiring to excel as an AWS DevOps Engineer, combining theoretical expertise with practical project implementation.