DateJul 04, 2021

Deploying a NuxtJS application to a Kubernetes Cluster in Cloud

If you have been following my series on creating a Personal Website, you might think this is insane! We haven't even written a single line of code for our website, and we are already deploying it to internet!! Hold your horses!

I know this is uncommon. But believe me, this will help you to develop a more reliable and stable codebase. In addition, this article will help you deploy a web application(whether it's a full-fledged Portfolio website or just a shabby page with Grandma quotes) to a Kubernetes Cluster in Cloud(In this case, GCP).

Also, in case you haven't setup a starter project, you can check out this article here.

Some of the advantages of deploying a barebone base application to the internet:

  1. Cross-Device Compatibility checking: Even after using containerization of application, you might still face issues where the web app works perfectly in Dev environment but has issues in prod. These issues might accumulate over numerous commits. Detecting them early makes our life easier.
  2. Easier to ask for Reviews: It's impossible to share your code with your peers for review. But they can always suggest improvements by visiting your deployed website. I have received reviews and incorporated them to improve the user experience.
  3. Gives an Agile spirit to your Project: Large Enterprises don't deploy a 100% developed product to production. They deploy the most miniature usable product and then build upon it. You can follow the same. Use versioning to track changes to your website. You can even create a Changelog.
  4. Source of Motivation: A visible product will motivate you to work than a codebase running on Sandbox in your development machine. It feels nice to open your website now and then. ?

What is Kubernetes Platform?

I won't go into many details here, but Kubernetes is a container orchestration framework. According to an official quote

Kubernetes is a portable, extensible, open-source platform for managing containerized workloads and services that facilitates both declarative configuration and automation. It has a large, rapidly growing ecosystem. Kubernetes services, support, and tools are widely available.

If you want to learn more, visit these links:

Setting up a Cluster

Here we'll use the native Google Kubernetes Engine (GKE) Provided by Google Cloud Platform (GCP)  to deploy our nuxt application.

Visit this link for steps on how to setup GKE on GCP.

You can create a standard cluster with the following configuration:

  • Name: Give any to your liking
  • Location Type: Depends on your requirements and budget. If you are low on budget, select zone instead of the region. You are trading redundancy for cost.
  • The number of nodes: Two should be more than enough for our use case.
  • Machine configuration: You can choose E2-Medium (2 vCPU, 4 GB Ram). This gives you a total of 4vCPU and 8 GB Ram.
  • Leave the rest as default.

Setting up Persistent Volumes

Containers, by definition, are stateless and ephemeral. Hence, to store persistent data, you need persistent storage, which the containers can then connect to store data. You can create a standard PV(persistent volume). But here, I'll create a better, solid-state drive for improved performance.

1. First, you need to create a Volume class

# ssd-storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: solid-state
provisioner: kubernetes.io/gce-pd
parameters:
  type: pd-ssd

2. Then, you can create a PV Claim.

# PersistentVolumeClaim.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: limosyn-com-pv-claim
spec:
  storageClassName: solid-state
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi

Great! We need to setup Github Actions to directly build and deploy our application from the Repository(CI/CD).

Setting up GitHub Actions

If you are using GitHub as your version control, you can use GitHub actions to setup a CI/CD workflow that will directly deploy your application to production. GitHub Actions has good integration with the GKE platform.

Some Prerequisites

Make sure you have a GCP account and have already setup a cluster following the steps in the previous section.

  1. Get a service account with proper access to GKE. Visit this link to create one.
  2. Keep your cluster, Information Handy.

Adding required files

Now you need to add four important files. You can add them manually, but I suggest using GitHub itself to add them. These files are:

  1. .github/workflows/google.yml : Main configuration used by GitHub to access your Kubernetes cluster and deploy. It also publishes the generated Image to google's Container Registry.
  2. .github/deployment.yml : Has deployment information such as the number of replicas, deployment strategy, to name a few.
  3. .github/service.yml : Has information required to expose your deployed service to the internet or within your cluster.
  4. kustomization.yml : Keeps a record of all the deployment and yml files.
  5. Dockerfile: For creating your application container image

First of all, let's setup a Dockerfile which will be used by GA to generate the application image. This image would then be pushed to Google Container Registry.

#Dockerfile

FROM node:latest as build-stage
RUN apt update -y && apt upgrade -y
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install
COPY ./ .
EXPOSE 3000
ENV HOST=0.0.0.0
ENV PORT=3000
RUN yarn build
CMD ["yarn", "start"]

To setup GA, visit the actions tab on your application Repository:

Scrolling down, you can find a Build and Deploy to GKE workflow among many others. You'll get something similar to this:

Make changes to the google.yml file, such as:

  • PROJECT_ID : The GCP Project ID.
  • GKE_CLUSTER : Name of the GKE Cluster
  • GKE_ZONE : Zone where the cluster exists
  • DEPLOYMENT_NAME : Give any to your liking.

For adding Secrets, go to Repository Settings > Secrets > New Repository Secret

GKE_SA_KEY is the content of your service account credentials.json file.

Now add deployment.ymlkustomization.yml and service.yml files. For reference, they can be:

# deployment.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nuxt-frontend
  namespace: "default"
spec:
  replicas: 2
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: nuxt-frontend
  template:
    metadata:
      labels:
        app: nuxt-frontend
    spec:
      containers:
      - name: app
        image: gcr.io/PROJECT_ID/IMAGE:TAG
        imagePullPolicy: Always
        ports:
        - containerPort: 3000
        livenessProbe:
          tcpSocket:
            port: 3000
          initialDelaySeconds: 10
          timeoutSeconds: 5
          periodSeconds: 30
        readinessProbe:
          tcpSocket:
            port: 3000
          initialDelaySeconds: 30
          timeoutSeconds: 5
          periodSeconds: 30
# service.yml

apiVersion: v1
kind: Service
metadata:
  name: nuxt-frontend
  namespace: default
  labels:
    app: nuxt-frontend
    tier: frontend
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 3000
  selector:
    app: nuxt-frontend
  type: LoadBalancer
# kustomization.yml

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - .github/service.yaml
  - .github/deployment.yaml

Once added, commit your changes!! ??

Provided you did everything right, the next time you push changes to your repository, GitHub actions will deploy your application to your Kubernetes Cluster.

You should be able to see the created deployments.
You should be able to see the created deployments.

Also, your application should be accessible via the Load balancer IP address.

Summing it up...

Following this article, you:

  1. Created a GCP account with a free trial.
  2. Set up a GKE cluster to deploy your application.
  3. Created files critical to GitHub Actions integration.
  4. Created a GitHub actions workflow.

This guide can be used to deploy all types of basic applications. Of course, there will be adaptations, but the soul would remain the same.✌?