Devops on Three tier architecture.

Kasturi NithinKasturi Nithin
6 min read

This article is explanation of my recent project on Continuous Integration, Continuous Deployment. I have utilized various kinds of tools like Github, Github actions, Docker, ArgoCD, Kubernetes[Minikube].

Pre-requisite :

  • Github, Github actions.

  • Docker.

  • Helm chart installation and commands.

  • Kubernetes and yaml files.

  • Minikube configuration.

Table of contents :

  • Checking out code from Github repository.

  • Build and Update Docker image.

  • Deep diving Kubernetes manifests.

  • ArgoCD configuration.

  • Github actions.

1 . Checking out code from Github repository.

I have forked a repository which consists code of MERN stack application [Chat-app], created by Md. Afzal Hassan Ehsani. Then started adding helm chart and other required files and folders.

2 . Building and Updating Docker image.

Docker files for both frontend and backend are already written by owner, so I have utilized the same. First and foremost step is to build and push image to Docker Hub.

Frontend Dockerfile

The Dockerfile of frontend is stored in frontend folder, open command prompt, move to frontend folder and build the frontend image using the following command :

docker build -t <Name-of-your-docker-hub-rep>:<Name-of-the image> <Path-to-Dockerfile>

In my case the command was something like this :

docker build -t nithin8/chat-app-frontend .

Push it to Docker Hub using the below command :

docker push <Name-of-your-docker-hub-rep>:<Name-of-the-image>:<Version>

In my case the command was something like this :

docker push nithin8:chat-app-frontend:latest

Backend Dockerfile

The Dockerfile of backend is stored in backend folder, open command prompt, move to backend folder and build the backend image using the following command :

docker build -t <Name-of-your-docker-hub-rep>:<Name-of-the image> <Path-to-Dockerfile>

In my case the command was something like this :

docker build -t nithin8/chat-app-backend.

Push it to Docker Hub using the below command :

docker push <Name-of-your-docker-hub-rep>:<Name-of-the-image>:<Version>

In my case the command was something like this :

docker push nithin8:chat-app-backend:latest

In case you have queries regarding Docker refer to this blog .

2 . Deep diving Kubernetes manifests.

Kubernetes manifests are being included in the existing forked repository, but I have used helm chart for configuring and maintenance of manifest files, refer my repository for more information.

Each micro-service has their own deployment, service yaml files, lets deep dive into each file to understand the concept behind them.

frontend-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
  namespace: chat-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
      - name: frontend
        image: nithin8/chat-app-frontend:{{.Values.image.tag}} 
        ports:
        - containerPort: 80

Explanation :

  1. apiVersion: refers which version of kubenetes API is used to create the resource of kind deployment.

  2. kind: refers to the type of resource created.

  3. metadata: name here points to name of the deployment, name space is where the deployment will reside.

  4. spec: defines the desired state of the deployment.

  5. replicas: are the number of pods that the deployment has to maintain.

  6. mathcLables: app helps in identification of pods that belong to this deployment.

  7. labels: inside the template is used for labeling the pod created by deployment.

  8. containers: list of containers that run inside the pod.

  9. name: is name of the container.

  10. image: is the path of image of container in docker hub.

  11. ports: specifies the port where container listens.

The same is for backend-deployment and mongo-deployment also.

service.yaml

apiVersion: v1
kind: Service
metadata:
  name: backend 
  namespace: chat-app
spec:
  selector:
    app: backend  
  ports:
    - protocol: TCP
      port: 5001  
      targetPort: 5001

Explanation:

  • name: refers to the name of the resource being created.

  • app in selector: is the name of pods to which service routes the traffic.

  • ports: lists the ports that service exposes.

  • protocol: is the type of protocol that service uses.

  • port: specifies the port that service listens in the cluster.

  • targetPort: specifies where the traffic is forwarded to.

The same is for frontend-service and mongo-service as well.

namespace.yaml

apiVersion: v1
kind: Namespace
metadata:
  name: chat-app
  • name: is the name of the name space where the pods, nodes and services reside.

I have used minikube cluster to experiment on the project.

Commands used to create and manage helm chart :

  1. helm install <name-of-chart> <path-of-chart> → helm install chat-app-helm chat-app-helm/

Then you can see your services, pods and cluster up and running.

3 . ArgoCD configuration.

Install argocd inside the kubernetes cluster using the documentation.

Steps after entering into the argocd UI :

  • Username is admin.

  • Get password by running the command kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d.

  • Click on New app on left corner of the web page.

  • Enter application name, select project name as default, select sync policy to automatic.

  • Use your repository url which consists of k8 manifests or my repository.

  • It automatically detects the helm path.

  • Fill the name space field with “chat-app“.

  • And click on create.

And you are done integrating ArgoCD with kubernetes cluster, verify it by clicking on the app in the home of ArgoCD UI.

ArgoCD believes github as single source of truth and gets to desired state whenever changes are made to github repo.

4 . Github actions.

I had configured Githb actions to trigger the pipeline job when ever the push event is made to the repository. The ci.yaml file is located in .github/workflows folder inside my repository.

name: CI/CD
on:
  push:
    branches:
      - main
    paths-ignore:
      - 'chat-app-helm/**'
      - ''

jobs:
  docker:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v2

      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Build and Push frontend Docker image
        uses: docker/build-push-action@v6
        with:
          context: ./frontend  # Set context to the frontend directory
          file: ./frontend/Dockerfile  # Path to Dockerfile inside the frontend directory
          push: true
          tags: ${{ secrets.DOCKERHUB_USERNAME }}/chat-app-frontend:${{ github.run_id }}

      - name: Build and Push backend Docker image
        uses: docker/build-push-action@v6
        with:
          context: ./backend  # Use the root of the repo as context for backend
          file: ./backend/Dockerfile  # Path to Dockerfile inside the backend directory
          push: true
          tags: ${{ secrets.DOCKERHUB_USERNAME }}/chat-app-backend:${{ github.run_id }}
  Update-NewTag-in-Helm-Chart:
    runs-on: ubuntu-latest
    needs: docker
    steps:
    - name: Checkout repository
      uses: actions/checkout@v4
      with:
        token: ${{secrets.TOKEN}}
    - name: Update tag in helm chart
      run: |
        sed -i 's/tag: .*/tag: "${{github.run_id}}"/' chat-app-helm/values.yaml
    - name: Commit and push changes
      run: |
        git config --global user.email "nithinkasturi8@gmail.com"
        git config --global user.name "Nithin kasturi"
        git add chat-app-helm/values.yaml
        git commit -m "Update tag in helm chart"
        git push

Explanation:

  • name: specifies the name of the pipeline.

  • on: specifies when to trigger the pipeline.

  • jobs: defines the jobs that are to be performed on tiggering.

  • docker: is the name of the stage inside the jobs, it can be any thing.

  • runs-on: is the operating system where this stage runs on.

  • steps: are used to build and push the docker image.

  • Set the secrets by navigating to settings→secrets and variables→actions.

  • Update-NewTag-in-Helm-Chart: is another job which updates the image tag in values.yaml and pushes the changes to github.

So, whenever push event occurs on github source code other than the chat-app-helm folder the jobs are triggered.

Finally we have successfully implemented the Continuous Integration and Continuous deployment of a three tier architecture.

0
Subscribe to my newsletter

Read articles from Kasturi Nithin directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Kasturi Nithin
Kasturi Nithin

Exploring something new.