DEV Community

Haripriya Veluchamy
Haripriya Veluchamy

Posted on

πŸš€ End-to-End CI/CD for a MERN Blog App using GitHub Actions, Docker & Kubernetes (AKS & EKS)

⚠️ Disclaimer: The Blog app is not my application. The MERN blog app was cloned from GitHub. My goal here was to practice DevOps with a full CI/CD workflow code quality checks, security audits, containerization, Kubernetes deployment, and monitoring.


🧱 App Overview

  • Frontend: React (served via Nginx)
  • Backend: Node.js + Express
  • Database: MongoDB
  • CI/CD: GitHub Actions
  • Container Registry: DockerHub
  • Deployment: Kubernetes (AKS/EKS)
  • Monitoring: Prometheus + Grafana
  • Security/Code Quality: SonarQube + Trivy + ESLint

πŸ”§ Goals

βœ… Set up CI/CD pipelines
βœ… Apply security scanning tools
βœ… Push images to DockerHub
βœ… Deploy automatically to Kubernetes
βœ… Monitor workloads
βœ… Do all this with free-tier constraints (Azure/AWS)


πŸ› οΈ CI Workflow – GitHub Actions

βœ… What We Did

  1. Lint both frontend & backend with ESLint
  2. Code Quality Check with self-hosted SonarQube
  3. Vulnerability Scanning with Trivy
  4. Build & Push Docker Images to DockerHub

☁️ SonarQube Setup (Azure & AWS)

  1. Launch a Linux VM
  2. Allow port 9000 in inbound firewall rules
  3. Add your user to Docker group:
   sudo usermod -aG docker $USER
Enter fullscreen mode Exit fullscreen mode
  1. Start SonarQube container:
   docker run -d --name sonarqube -p 9000:9000 sonarqube
Enter fullscreen mode Exit fullscreen mode
  1. Access: http://<VM_PUBLIC_IP>:9000
  2. Generate token β†’ Save in GitHub Secrets:
  • SONAR_TOKEN
  • SONAR_HOST_URL

βš™οΈ CD Workflow – GitHub Actions

We created a second GitHub Actions workflow that triggers only after CI succeeds using workflow_run.

πŸ§‘β€πŸ’» AKS Setup

  1. Use az login, create AKS via CLI or portal
  2. Create a Service Principal
  3. Store creds in GitHub Secret: AZURE_CREDENTIALS
  4. In GitHub Action:
   az aks get-credentials --name blog-cluster --resource-group blogdeploy
   kubectl apply -f deploy/base/
Enter fullscreen mode Exit fullscreen mode

πŸ§‘β€πŸ’» EKS Setup (Alternative)

  1. Create EKS with eksctl:
   eksctl create cluster --name blog-cluster ...
Enter fullscreen mode Exit fullscreen mode
  1. Configure AWS access:
  • Store AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY
  • Use:

     - uses: aws-actions/configure-aws-credentials@v1
       with:
         aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
         aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
         region: us-east-1
    
  1. Add kubeconfig for EKS:
   aws eks update-kubeconfig --name blog-cluster
Enter fullscreen mode Exit fullscreen mode
  1. Then apply:
   kubectl apply -f deploy/base/
Enter fullscreen mode Exit fullscreen mode

βœ… Full CD YAML Snippet

on:
  workflow_run:
    workflows: ["CI-Ffor blog"]
    types: [completed]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Azure Login
        uses: azure/login@v1
        with:
          creds: ${{ secrets.AZURE_CREDENTIALS }}

      - name: Set AKS Context
        run: |
          az aks get-credentials --resource-group blogdeploy --name blog-cluster --overwrite-existing

      - name: Deploy
        run: kubectl apply -f deploy/base/
Enter fullscreen mode Exit fullscreen mode

πŸ“ Kubernetes Setup

We used manifest-based deployment instead of Helm.

  • MongoDB
  • blog-server (Node)
  • blog-client (React + Nginx)
  • Services (ClusterIP + LoadBalancer)

For full YAMLs:
πŸ‘‰ GitHub Repo: https://github.com/Harivelu0/Blog-App-using-MERN-stack


πŸ“¦ Docker & Image Strategy

Both client and server had custom Dockerfile.
In CI, we built and pushed them:

docker build -t <username>/blog-client ./client
docker push <username>/blog-client
Enter fullscreen mode Exit fullscreen mode

Great catch! Here's the ArgoCD-specific flow you can insert into your diagram and blog under the "CD & GitOps" section.


πŸ” GitOps Deployment with ArgoCD

Once the CI pipeline builds and pushes Docker images and updates the manifests in the GitHub repo (deploy/base/), ArgoCD handles the automated syncing and deployment into your Kubernetes cluster.

πŸ”Ή Flow:

  1. ArgoCD App Definition
  • Points to your GitHub repo and deploy/base/ folder.
  • Deployed using:

     kubectl apply -f argocd-app.yaml
    
  1. ArgoCD Syncs Automatically
  • Detects changes in the GitHub repo (new images, manifest edits).
  • Applies updates to the AKS/EKS cluster without manual kubectl apply.
  1. ArgoCD UI
  • Accessed via port-forward:

     kubectl port-forward svc/argocd-server -n argocd 8080:443
    
  • Login with:

    • Username: admin
    • Password: from initial pod logs or custom setup
  1. Health Monitoring
  • ArgoCD continuously monitors health and sync status of the app.
  • If app is OutOfSync or Degraded, it provides logs and status for debugging.

πŸ“Š Monitoring

Installed Prometheus + Grafana via Helm:

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm install prometheus prometheus-community/kube-prometheus-stack \
  --namespace monitoring --create-namespace
Enter fullscreen mode Exit fullscreen mode

Port-forward locally:

kubectl port-forward svc/grafana -n monitoring 3001:80
kubectl port-forward svc/prometheus-server -n monitoring 9090:80
Enter fullscreen mode Exit fullscreen mode

⚠️ No alerts configured in this project due to focus/time β€” will cover alerting in a future one.


Final DAeployed App in K8

πŸ§ͺ Common Issues We Solved

Issue Fix
App blank on root Removed homepage from package.json and rebuilt
ErrImageNeverPull Set imagePullPolicy: Always
SonarQube not reachable Opened port 9000 on VM
Port limitations on Azure Used port-forwarding instead of multiple LoadBalancers
Kustomization error Installed required CRDs for Kustomize

🧹 Cleanup

Azure

az aks delete --name blog-cluster --resource-group blogdeploy --yes
az group delete --name blogdeploy --yes
Enter fullscreen mode Exit fullscreen mode

AWS

eksctl delete cluster --name blog-cluster
Enter fullscreen mode Exit fullscreen mode

Also delete:

  • DockerHub images
  • SonarQube VM
  • GitHub Secrets

βœ… Final Summary

βœ… CI: ESLint + SonarQube + Trivy + Docker
βœ… CD: GitHub Actions to AKS (or EKS)
βœ… Monitoring: Prometheus + Grafana
βœ… K8s: Manifest-based setup
βœ… Port-forwarding to work around public IP limits
❌ Alerting skipped for now (to be covered in future project)


Top comments (1)

Collapse
 
suvrajeet profile image
Suvrajeet Banerjee

Amazing! πŸŽ‰