# 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](https://cdn.hashnode.com/res/hashnode/image/upload/v1696006301278/befe1cb6-a5f4-4867-a13e-89919432bfc5.png align="center")

> 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](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](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](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](https://cdn.hashnode.com/res/hashnode/image/upload/v1696006487740/6cc424db-c1fc-45bd-b8d0-1ae52b9b6809.png align="center")

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](https://cdn.hashnode.com/res/hashnode/image/upload/v1696006550845/d7b7408a-2176-4d53-ac04-a0dd30517378.png align="center")

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](https://cdn.hashnode.com/res/hashnode/image/upload/v1696006658459/3ce2da12-18d5-4572-a690-4ff8fa1deba9.png align="center")

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

![eks default add ons](https://cdn.hashnode.com/res/hashnode/image/upload/v1696006694518/1f83c855-a886-4512-a81d-438a3b30f511.png align="center")

Now select Amazon EBS CSI driver and create it.

![ebs default csi driver](https://cdn.hashnode.com/res/hashnode/image/upload/v1696006749895/c4e29807-272d-4789-851e-79e3d4f2cea6.png align="center")

### 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](https://cdn.hashnode.com/res/hashnode/image/upload/v1696006944935/d713e4d8-7cdf-4346-8d83-4c38ffba5401.png align="center")

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:

```bash
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.

```bash
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](https://github.com/ajimbong/k8s-aws-beginner-project.git)

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

```bash
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:

```bash
kubectl apply -f mongo-statefulset.yaml
```

setup the service

```bash
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

```bash
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

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

Now lets populate the database with demo data

```bash
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

```bash
kubectl apply -f mongo-secret.yaml
```

### Setting up the API

Create the API deployment

```bash
kubectl apply -f api-deployment.yaml
```

Expose the Deployment through the service

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

Set the environment variables

```bash
{ 
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

```bash
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 .
```

```bash
{ 
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

```bash
kubectl apply -f frontend-deployment.yaml
```

Create the front end service

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

Confirm that the front end can receiver requests

```bash
{
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

```bash
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](https://cdn.hashnode.com/res/hashnode/image/upload/v1696007112226/33e5b92f-9489-4b51-9656-f7d388ad4cce.png align="center")

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