Production-Grade 3-Tier E-Commerce Deployment on Amazon EKS with Helm, Ingress, Domain Load Balancing, SSL/TLS, and Autoscaling

Mooaz SayyedMooaz Sayyed
10 min read

EasyShop is a modern, full-stack e-commerce platform built with Next.js 14, TypeScript, and MongoDB. It features a beautiful UI with Tailwind CSS, secure authentication, real-time cart updates, and a seamless shopping experience.

✨ Features

  • 🎨 Modern and responsive UI with dark mode support

  • 🔐 Secure JWT-based authentication

  • 🛒 Real-time cart management with Redux

  • 📱 Mobile-first design approach

  • 🔍 Advanced product search and filtering

  • 💳 Secure checkout process

  • 📦 Multiple product categories

  • 👤 User profiles and order history

  • 🌙 Dark/Light theme support

🏗️ Architecture

EasyShop follows a three-tier architecture pattern:

1. Presentation Tier (Frontend)

  • Next.js React Components

  • Redux for State Management

  • Tailwind CSS for Styling

  • Client-side Routing

  • Responsive UI Components

2. Application Tier (Backend)

  • Next.js API Routes

  • Business Logic

  • Authentication & Authorization

  • Request Validation

  • Error Handling

  • Data Processing

3. Data Tier (Database)

  • MongoDB Database

  • Mongoose ODM

  • Data Models

  • CRUD Operations

  • Data Validation

Devops Logic

To deploy this e-commerce site, first clone the application's source code from GitHub, and then clone the follow-up GitHub repository.

Ecommerce Platform tool

Project Repo

Creating the infrastructure using terraform

Setup & Initialization

1. Install Terraform

  • Install Terraform

Linux & macOS

curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -
sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"
sudo apt-get update && sudo apt-get install terraform

Verify Installation

terraform -v

Initialize Terraform

terraform init

2. Install AWS CLI

AWS CLI (Command Line Interface) allows you to interact with AWS services directly from the command line.

curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
sudo apt install unzip
unzip awscliv2.zip
sudo ./aws/install

Install AWS CLI in Windows 'powershell'

msiexec.exe /i https://awscli.amazonaws.com/AWSCLIV2.msi

aws configure

This will prompt you to enter:

  • AWS Access Key ID:

  • AWS Secret Access Key:

  • Default region name:

  • Default output format:

Note

Make sure the IAM user you're using has the necessary permissions. You’ll need an AWS IAM Role with programmatic access enabled, along with the Access Key and Secret Key.

Follow the steps below to get your infrastructure up and running using Terraform:

  1. Clone the Repository: First, clone this repo to your local machine:
git clone https://github.com/mooazsayyed/Production-Grade-DevOps-Application-Deployment
cd terraform
  1. Generate SSH Key Pair: Create a new SSH key to access your EC2 instance:
ssh-keygen -f terra-key

This will prompt you to create a new key file named terra-key.

  1. Private key permission: Change your private key permission:
chmod 400 terra-key
  1. Initialize Terraform: Initialize the Terraform working directory to download required providers:
terraform init
  1. Review the Execution Plan: Before applying changes, always check the execution plan:
terraform plan
  1. Apply the Configuration: Now, apply the changes and create the infrastructure:
terraform apply

Confirm with yes when prompted.

  1. Access Your EC2 Instance;
    After deployment, grab the public IP of your EC2 instance from the output or AWS Console, then connect using SSH:
ssh -i terra-key ubuntu@<public-ip>

List of items this will create

  • Elastic IP

    • aws_eip.jenkins_server_ip
  • EC2 Instances

    • aws_instance.bastion_host (t3.medium, tagged "Bastion-Host")

    • aws_instance.testinstance (t3.medium, tagged "Jenkins-Automate")

  • Key Pair

    • aws_key_pair.deployer (terra-automate-key)
  • Security Groups

    • aws_security_group.allow_user_bastion (bastion_host_SG)

    • aws_security_group.allow_user_to_connect (mysecurity)

    • aws_security_group.node_group_remote_access (allow HTTP)

  • EKS Cluster & Related

    • aws_eks_cluster.this (EKS cluster named tws-eks-cluster)

    • aws_cloudwatch_log_group.this (/aws/eks/tws-eks-cluster/cluster)

    • aws_ec2_tag.cluster_primary_security_group

    • aws_eks_access_entry.this

    • aws_eks_access_policy_association.this

    • aws_iam_openid_connect_provider.oidc_provider

    • aws_iam_policy.cluster_encryption

    • aws_iam_policy.custom

  • EKS Add-ons

    • aws_eks_addon.this["coredns"]

    • aws_eks_addon.this["kube-proxy"]

    • aws_eks_addon.this["vpc-cni"]

  • Supporting Data Resources (read during apply)

    • data.aws_instances.eks_nodes

    • data.aws_eks_addon_version.this (for coredns, kube-proxy, vpc-cni)

    • data.tls_certificate.this

👉 In short:
It provisions 2 EC2 instances (bastion + Jenkins), an Elastic IP, a key pair, 3 security groups, an EKS cluster (tws-eks-cluster) with add-ons (CoreDNS, kube-proxy, VPC CNI), IAM policies/roles, OIDC provider, CloudWatch logs, and necessary access entries.

  1. Update your kubeconfig: wherever you want to access your eks wheather it is yur local machine or bastion server this command will help you to interact with your eks.

Caution

you need to configure aws cli first to execute this command:

aws configure
aws eks --region eu-west-1 update-kubeconfig --name tws-eks-cluster
  1. Check your cluster:
kubectl get nodes

Steps to Access Jenkins & Install Plugins

1. Open Jenkins in Browser:

Use your public IP with port 8080: http://<public_IP>:8080

2. Initial Admin password:

Start the service and get the Jenkins initial admin password:

sudo cat /var/lib/jenkins/secrets/initialAdminPassword

3. Start Jenkins (If Not Running):

Get the Jenkins initial admin password:

sudo systemctl enable jenkins
sudo systemctl restart jenkins

4. Install Essential Plugins:

  • Navigate to: Manage Jenkins → Plugins → Available Plugins

  • Search and install the following:

    • Docker Pipeline

    • Pipeline View

5. Set Up Docker & GitHub Credentials in Jenkins (Global Credentials)

  • GitHub Credentials:

    • Go to: Jenkins → Manage Jenkins → Credentials → (Global) → Add Credentials
  • Use:

    • Kind: Username with password

    • ID: github-credentials

  • DockerHub Credentials: Go to the same Global Credentials section

  • Use:

    • Kind: Username with password

    • ID: docker-hub-credentials [Notes:] Use these IDs in your Jenkins pipeline for secure access to GitHub and DockerHub

6. Jenkins Shared Library Setup:

  • Configure Trusted Pipeline Library:

    • Go to: Jenkins → Manage Jenkins → Configure System Scroll to Global Pipeline Libraries section
  • Add a New Shared Library:

  • Name: Shared

  • Default Version: main

  • Project Repository URL: https://github.com/<your user-name/jenkins-shared-libraries.

[Notes:] Make sure the repo contains a proper directory structure eq: vars/

Setup Jenkins Result

7. Setup Pipeline

  • Create New Pipeline Job

    • Name: EasyShop

    • Type: Pipeline
      Press Okey

In General

  • Description: EasyShop

  • Check the box: GitHub project

  • GitHub Repo URL: https://github.com/<your user-name/tws-e-commerce-app

In Trigger

  • Check the box:GitHub hook trigger for GITScm polling

In Pipeline

  • Definition: Pipeline script from SCM

  • SCM: Git

  • Repository URL: https://github.com/<your user-name/tws-e-commerce-app

  • Credentials: github-credentials

  • Branch: master

  • Script Path: Jenkinsfile

Fork Required Repos

Fork App Repo:

  • Open the Jenkinsfile

  • Change the DockerHub username to yours

Fork Shared Library Repo:

  • Edit vars/update_k8s_manifest.groovy

  • Update with your DockerHub username

Setup Webhook
In GitHub:

  • Go to SettingsWebhooks

  • Add a new webhook pointing to your Jenkins URL

  • Select: GitHub hook trigger for GITScm polling in Jenkins job

Trigger the Pipeline
Build Now in Jenkins

8. CD – Continuous Deployment Setup

Prerequisites:
Before configuring CD, make sure the following tools are installed:

  • Installations Required:
    kubectl
    AWS CLI

SSH into Bastion Server

  • Connect to your Bastion EC2 instance via SSH.

Note:
This is not the node where Jenkins is running. This is the intermediate EC2 (Bastion Host) used for accessing private resources like your EKS cluster.

8. Configure AWS CLI on Bastion Server Run the AWS configure command:

aws configure

Add your Access Key and Secret Key when prompted.

9. Update Kubeconfig for EKS
Run the following important command:

aws eks update-kubeconfig --region eu-west-1 --name tws-eks-cluster
  • This command maps your EKS cluster with your Bastion server.

  • It helps to communicate with EKS components.

10. Install AWS application load balancer refering the below docs link

https://docs.aws.amazon.com/eks/latest/userguide/lbc-helm.html

11. Install the EBS CSI driver refering the below docs link

https://docs.aws.amazon.com/eks/latest/userguide/ebs-csi.html#eksctl_store_app_data

12. Argo CD Setup
Create a Namespace for Argo CD in bastion host

kubectl create namespace argocd

Install Argo CD using helm
(https://artifacthub.io/packages/helm/argo/argo-cd)

helm repo add argo https://argoproj.github.io/argo-helm
helm install my-argo-cd argo/argo-cd --version 8.0.10

get the values file and save it

helm show values argo/argo-cd > argocd-values.yaml

Optional: If you want Ingress and a certificate, register your domain name in AWS ACM.

Or go with port forwarding

  1. Get the ArgoCD Server Pod Name: First, find the name of the ArgoCD server pod:

    bash

     kubectl get pods -n argocd
    
  2. Port Forward to ArgoCD Server: Use the following command to port forward:

    bash

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

edit the values file, change the below settings.

  1. Copy the acm certificate value
global:
  domain: argocd.example.com

configs:
  params:
    server.insecure: true

server:
  ingress:
    enabled: true
    controller: aws
    ingressClassName: alb
    annotations:
      alb.ingress.kubernetes.io/scheme: internet-facing
      alb.ingress.kubernetes.io/certificate-arn: <your-cert-arn> #Not needed if you dont want
      alb.ingress.kubernetes.io/group.name: easyshop-app-lb
      alb.ingress.kubernetes.io/target-type: ip
      alb.ingress.kubernetes.io/backend-protocol: HTTP
      alb.ingress.kubernetes.io/listen-ports: '[{"HTTP":80}, {"HTTPS":443}]'
      alb.ingress.kubernetes.io/ssl-redirect: '443'
    hostname: argocd.mooazsayyed.live #change only if needed
    aws:
      serviceType: ClusterIP # <- Used with target-type: ip
      backendProtocolVersion: GRPC
  1. save and upgrade the helm chart.
helm upgrade my-argo-cd argo/argo-cd -n argocd -f my-values.yaml
  1. add the record in route53 or your dns provider like netlify or cloudflare “argocd.mooazsayyed.live” with load balancer dns.

  2. access it in browser.

  3. Retrive the secret for Argocd

kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
  1. login to argocd “admin” and retrieved password

  2. Change the password by going to “user info” tab in the UI.

Deploy Your Application in Argo CD GUI

On the Argo CD homepage, click on the “New App” button.

Fill in the following details:

  • Application Name: Enter your desired app name

  • Project Name: Select default from the dropdown.

  • Sync Policy: Choose Automatic.

In the Source section:

  • Repo URL: Add the Git repository URL that contains your Kubernetes manifests.

  • Path: Kubernetes (or the actual path inside the repo where your manifests reside)

In the “Destination” section:

Click on “Create”.

NOTE: before deploying Chnage your ingress settings and image tag in the yamls inside “kubernetes” directory

Ingress Annotations:

annotations:
    alb.ingress.kubernetes.io/group.name: easyshop-app-lb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:ap-south-1:876997124628:certificate/b69bb6e7-cbd1-490b-b765-27574080f48c
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/backend-protocol: HTTP
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP":80}, {"HTTPS":443}]'
    alb.ingress.kubernetes.io/ssl-redirect: '443'
    kubernetes.io/ingress.class: alb
  • add record to route 53 or cname record in dns provider “easyshop.mooazsayyed.live”

  • Access your site now.

    Installing a Metric Server

    • metric server install thru helm chart
    https://artifacthub.io/packages/helm/metrics-server/metrics-server

verify metric server.

    kubectl get pods -w
    kubectl top pods

Monitoring Using kube-prometheus-stack

create a namespace “monitoring”

    kubectl create ns monitoring
    https://artifacthub.io/packages/helm/prometheus-community/kube-prometheus-stack

verify deployment :

    kubectl get pods -n monitoring

get the helm values and save it in a file

    helm show values prometheus-community/kube-prometheus-stack > kube-prom-stack.yaml

edit the file and add the following in the params for prometheus, grafana and alert manger.

Grafana:

    ingressClassName: alb
    annotations:
          alb.ingress.kubernetes.io/group.name: easyshop-app-lb
          alb.ingress.kubernetes.io/scheme: internet-facing
          alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:ap-south-1:876997124628:certificate/b69bb6e7-cbd1-490b-b765-27574080f48c
          alb.ingress.kubernetes.io/target-type: ip
                alb.ingress.kubernetes.io/listen-ports: '[{"HTTP":80}, {"HTTPS":443}]'
          alb.ingress.kubernetes.io/ssl-redirect: '443'

        hosts:
          - grafana.mooazsayyed.live

Prometheus:

    ingressClassName: alb
    annotations:
          alb.ingress.kubernetes.io/group.name: easyshop-app-lb
          alb.ingress.kubernetes.io/scheme: internet-facing
          alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:ap-south-1:876997124628:certificate/b69bb6e7-cbd1-490b-b765-27574080f48c
          alb.ingress.kubernetes.io/target-type: ip
          alb.ingress.kubernetes.io/listen-ports: '[{"HTTP":80}, {"HTTPS":443}]'
          alb.ingress.kubernetes.io/ssl-redirect: '443'
        labels: {}



        hosts: 
          - prometheus.mooazsayyed.live
        paths:
         - /
        pathType: Prefix
  • Alertmanger:

      ingressClassName: alb
      annotations:
            alb.ingress.kubernetes.io/group.name: easyshop-app-lb
            alb.ingress.kubernetes.io/scheme: internet-facing
            alb.ingress.kubernetes.io/target-type: ip
            alb.ingress.kubernetes.io/backend-protocol: HTTP
                  alb.ingress.kubernetes.io/listen-ports: '[{"HTTP":80}, {"HTTPS":443}]'
            alb.ingress.kubernetes.io/ssl-redirect: '443'
    
          hosts: 
            - alertmanager.devopsdock.site
          paths:
          - /
          pathType: Prefix
    

    Alerting to Slack

    Create a new workspace in slack, create a new channel e.g. “#alerts

    go to https://api.slack.com/apps to create the webhook.

    1. create an app “alertmanager

    2. go to incoming webhook

    3. create a webhook and copy it.

modify the helm values.

    config:
        global:
          resolve_timeout: 5m
        route:
          group_by: ['namespace']
          group_wait: 30s
          group_interval: 5m
          repeat_interval: 12h
          receiver: 'slack-notification'
          routes:
          - receiver: 'slack-notification'
            matchers:
              - severity = "critical"
        receivers:
        - name: 'slack-notification'
          slack_configs:
              - api_url: 'https://hooks.slack.com/services/T08ULBZB5UY/B08U0CE3DEG/OivCLYq28gNzz4TabiY5zUj'
                channel: '#alerts'
                send_resolved: true
        templates:
        - '/etc/alertmanager/config/*.tmpl'

Note: you can refer this DOCs for the slack configuration. “https://prometheus.io/docs/alerting/latest/configuration/#slack_config”

upgrade the chart

    helm upgrade my-kube-prometheus-stack prometheus-community/kube-prometheus-stack -f kube-prom-stack.yaml -n monitoring

get grafana secret “user = admin”

    kubectl --namespace monitoring get secrets my-kube-prometheus-stack-grafana -o jsonpath="{.data.admin-password}" | base64 -d ; echo

You would get the notification in the slack’s respective channel.

📌 Grafana Dashboard View

WO! ooo!!! ...Your project is now deployed.

0
Subscribe to my newsletter

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

Written by

Mooaz Sayyed
Mooaz Sayyed

I am a everyday devops and cloud enthusiasts who is creating a community for inspiration and networking benefits.I am showcasing my skills almost every week to potential recruiters to find a perfect role in the corporate world, I am still a student in my 2nd year of BCA. So if you are a recruiter,you can reach me at sayyedmooaz@gmail.com