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


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.
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:
- Clone the Repository: First, clone this repo to your local machine:
git clone https://github.com/mooazsayyed/Production-Grade-DevOps-Application-Deployment
cd terraform
- 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.
- Private key permission: Change your private key permission:
chmod 400 terra-key
- Initialize Terraform: Initialize the Terraform working directory to download required providers:
terraform init
- Review the Execution Plan: Before applying changes, always check the execution plan:
terraform plan
- Apply the Configuration: Now, apply the changes and create the infrastructure:
terraform apply
Confirm with
yes
when prompted.
- 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 namedtws-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)
👉 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.
- 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
- 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
PressOkey
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
Settings
→Webhooks
Add a new webhook pointing to your Jenkins URL
Select:
GitHub hook trigger for GITScm polling
in Jenkins jobTrigger 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
Get the ArgoCD Server Pod Name: First, find the name of the ArgoCD server pod:
bash
kubectl get pods -n argocd
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.
- 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
- save and upgrade the helm chart.
helm upgrade my-argo-cd argo/argo-cd -n argocd -f my-values.yaml
add the record in route53 or your dns provider like netlify or cloudflare “argocd.mooazsayyed.live” with load balancer dns.
access it in browser.
Retrive the secret for Argocd
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
login to argocd “admin” and retrieved password
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:
Cluster URL: https://kubernetes.default.svc (usually shown as "default")
Namespace: tws-e-commerce-app (or your desired namespace)
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.
create an app “alertmanager”
go to incoming webhook
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.
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