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! ๐ŸŽ‰