Deploy a Full Stack Application to a Kubernetes Cluster on Amazon EKS

Deploy a Full Stack Application to a Kubernetes Cluster on Amazon EKS

Introduction

In this tutorial, we will deploy a full-stack application to an Amazon EKS cluster. This is a voting application made up of a React front end, Go API and MongoDB database. We'll use Kubernetes(k8s) resources such as; Services, Deployments, Secrets, and StatefulSets. Below is the architecture diagram for this project:

kubernetes project architecture diagram

This project was inspired by a YouTube channel called Cloud Champ, you can watch their full video tutorial here: https://youtu.be/pTmIoKUeU-A?si=lZ3ev7Fgyqcw-7yI

Prerequisites

To follow along with this project, you will need the following items:

  • An AWS Account

  • AWS CLI installed locally and configured with an IAM user

  • Kubectl CLI tool for k8s

  • Git

  • And a text editor (VS Code)

N/B: In order not to run into complicated issues, I highly recommend using the same IAM user in your AWS console and AWS CLI to do this project. Because when you create an EKS cluster, only the user who created the cluster can access it by default. So if you created the cluster with your root user account and are signed into a different IAM user in your AWS CLI, you will have trouble running commands against your cluster.

Procedure

Creating the IAM roles

We need two IAM roles, one for the EKS Cluster and the other for the EKS Nodegroup. To create the an IAM role for the EKS cluster, you can follow this tutorial from the official AWS Documention https://docs.aws.amazon.com/eks/latest/userguide/service_IAM_role.html#create-service-role.

Similarly, you can follow this tutorial from the AWS documentation to create an IAM role for the EKS nodegroup: https://docs.aws.amazon.com/eks/latest/userguide/create-node-role.html#create-worker-node-role. You also need to make sure you add the following policies to your IAM role:

eks node group policies

This step is very important.

Creating the EKS Cluster

In your AWS Console, search for the EKS service and create a new cluster. For this cluster, you can use the default VPC and Security Groups. The only options you have to provide are:

  • The Cluster Name

  • And the EKS Cluster role we created earlier

eks cluster name and service role name

All the other options can be left as default and you can go ahead and create the cluster.

Installing the EBS CSI Driver

We will be using an EBS volume to persist the data in our MongoDB database, and for this we need to install the EBS CSI driver.

In your cluster, head over to Add-ons

eks add ons section, eks

You should see three add-ons that come by default with our cluster. Click on "Get more add-ons"

eks default add ons

Now select Amazon EBS CSI driver and create it.

ebs default csi driver

Create Node Group

The node group consists of EC2 instances that will be running our container workloads. In the EKS dashboard, navigate to the compute area and create a nodegroup.

eks add node group

When creating the nodegroup, you have to select the EKS nodegroup role we created earlier.

Add EKS Cluster to kubectl context

To run commands against your EKS cluster, you have to add the cluster to your kubectl context. Use the follwing command will do so:

aws eks update-kubeconfig --name {eks_cluster_name} --region {aws_region}

This command uses the AWS CLI to automatically set up the context for you. In the command, you have to replace {eks_cluster_name} with name of your EKS cluster and {aws_region} with the region you deployed the cluster to. e.g us-east-1.

Use this command to test if you can access your cluster.

kubectl get nodes

If you don't get any errors, then you are all set to go.

Download the Kubernetes Manifest Files

You can get the manifest files from my github repository: https://github.com/ajimbong/k8s-aws-beginner-project.git

or use this git command to clone the repository to your pc:

git clone https://github.com/ajimbong/k8s-aws-beginner-project.git

Once you clone the repository, open the folder in your text editor of choice.

Setup the MongoDB Database

Open a terminal in the project folder and run the following command to setup the database pods:

kubectl apply -f mongo-statefulset.yaml

setup the service

kubectl apply -f mongo-service.yaml

Initialize the MongoDB replica set on the mongo-0 pod. Make sure you wait for this command to finish running

cat << EOF | kubectl exec -it mongo-0 -- mongo
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); 
EOF

Run this command to confirm everything is okay

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

Now lets populate the database with demo data

cat << EOF | kubectl exec -it mongo-0 -- mongo 
use langdb; 
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}}); 

db.languages.find().pretty(); 
EOF

Create the MongoDB secret

kubectl apply -f mongo-secret.yaml

Setting up the API

Create the API deployment

kubectl apply -f api-deployment.yaml

Expose the Deployment through the service

kubectl expose deploy api \
--name=api \
--type=LoadBalancer \
--port=80 \
--target-port=8080

Set the environment variables

{ 
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 
}

Test and to see if the Service is accessible

curl -s $API_ELB_PUBLIC_FQDN/languages | jq . 
curl -s $API_ELB_PUBLIC_FQDN/languages/go | jq . 
curl -s $API_ELB_PUBLIC_FQDN/languages/java | jq . 
curl -s $API_ELB_PUBLIC_FQDN/languages/nodejs | jq .
{ 
API_ELB_PUBLIC_FQDN=$(kubectl get svc api -ojsonpath="
{.status.loadBalancer.ingress[0].hostname}") 
echo API_ELB_PUBLIC_FQDN=$API_ELB_PUBLIC_FQDN 
}

Setting Up the Front End

Apply the front end deployment

kubectl apply -f frontend-deployment.yaml

Create the front end service

kubectl expose deploy frontend \
--name=frontend \
--type=LoadBalancer \
--port=80 \
--target-port=8080

Confirm that the front end can receiver requests

{
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
}

Test Your Deployement

Run this command to get the public URL for your voting app

echo http://$FRONTEND_ELB_PUBLIC_FQDN

Open the link in your browser and if you see a page like this, that means your deployment was successful.

pragramming language voting app output

You can go ahead and start voting for your favorite programming langauge and once you're done, make sure you delete all your kubernetes resources i.e the nodegroup and cluster because you will get charged for that.

Summary

In this project, we learned how to deploy a containerized application on Amazon EKS using kubctl and the AWS console. If you followed along, I hope you were able to get your deployment running successfully and learned a lot in process. Feel free to leave a comment if you have any questions or feedback.

{{ I noticed some errors in this article which I have to rectify in the future }}