This project demonstrates a complete cloud-native deployment of a Node.js Nyan Cat application on AWS EKS using modern DevOps practices. The application consists of a three-tier architecture with a MySQL database, Node.js API backend, and a web frontend, all containerized and deployed using Kubernetes. The infrastructure is provisioned using Terraform, with multiple deployment options including GitHub Actions CI/CD, Jenkins pipelines, and GitOps with ArgoCD. The project showcases best practices for container orchestration, infrastructure as code, automated testing, security scanning, and continuous deployment in a production-ready environment.
- MySQL - StatefulSet with EBS persistent storage (1Gi gp3), ClusterIP service on port 3306
- API - Node.js backend Deployment with HPA (1-5 replicas, 50% CPU/80% memory targets), connects to MySQL
- Web - Node.js frontend Deployment with HPA (1-5 replicas), communicates with API service
- All services run in
nodejsnamespace with resource limits (64-128Mi memory, 100-250m CPU)
- cluster - EKS 1.28+ with managed node groups, VPC, security groups, OIDC provider, IAM roles
- backend-bucket - S3 bucket for Terraform state with versioning and AES256 encryption
- github-oidc-policy - GitHub Actions OIDC trust policy for Role-GitHubActionsTerraformEKSInfra
- modules/iam-roles - EKS cluster role, node role, EBS CSI driver role, ALB controller role
- modules/helm - Reusable Helm deployment module for application charts
- setup_github_oidc.yaml - Configure GitHub OIDC provider and create Role-GitHubActionsTerraformEKSInfra for workflow authentication
- create_buckets.yaml - Create S3 bucket for Terraform state storage with versioning and encryption
- create_clusters.yaml - Provision EKS cluster with Terraform including VPC, node groups, IAM roles, and OIDC provider
- deploy_applications.yaml - Deploy full stack (MySQL, API, Web) with Metrics Server, EBS CSI driver, and HPA configuration
- deploy_argocd.yaml - Deploy ArgoCD with Helm including IRSA roles for ECR access and automated password configuration
- deploy_jenkins.yaml - Deploy Jenkins with Helm including persistent storage, RBAC, and Kaniko IRSA for ECR builds
- api_app.yaml / web_app.yaml / mysql_app.yaml - ArgoCD Application manifests with Helm values and sync policies
- nodejs-app-stack.yaml - App of Apps pattern managing all three services
- IRSA roles: Role-ArgoCD-Deployment (ECR/EKS/S3), Role-ArgoCD-RepoServer (ECR read), Role-ArgoCD-ImageUpdater (ECR monitoring)
- Auto-sync, self-heal, and prune enabled for continuous deployment
- Jenkinsfile - Multi-stage pipeline: checkout → Kaniko build → ECR push → kubectl deploy → test
- Deployed via Helm chart with 5Gi persistent storage (jenkins-storageclass)
- RBAC: jenkins-cd ClusterRole with full namespace access
- IRSA: Role-JenkinsKanikopushECR for ECR authentication
You need an AWS user that GitHub Actions can use to deploy infrastructure.
-
Go to AWS Console → IAM → Users → Add user
- User name:
user-devopsor any you like - Enable: Programmatic access
- User name:
-
Attach permissions (choose either full access or least privilege):
AmazonS3FullAccess→ needed for Terraform state in S3AdministratorAccess→ full control for EC2, VPC, IAM
(or create your own least-privilege policy for tighter security)
-
After creation, download the credentials file (
.csv) or just copy from AWS.
It contains:AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY
-
Store these in GitHub as secrets:
- Go to GitHub → Your Organization → Settings → Secrets and variables → Actions
- Add two organization secrets (or repo-level if you prefer):
AWS_ACCESS_KEY_ID_DEV→ paste the access keyAWS_SECRET_ACCESS_KEY_DEV→ paste the secret key
If your workflows in a public repo need to use a private repo, configure a token.
-
Generate a token
- Go to GitHub → Organization → Settings → Developer settings → Personal access tokens → Fine-grained tokens
- Click Generate new token
- Under "Resource owner", select your organization
- Limit access only to the private repo(s) you want
-
Set required permissions
- Repository → Contents: Read (mandatory to clone)
- Repository → Metadata: Read (auto-enabled)
- (Optional) Contents: Read & Write if workflows need push/write
- Admin access is not required
-
Save the token as a GitHub secret
- Go to Org → Settings → Secrets and variables → Actions → New organization secret
- Name it:
PRIVATE_REPO_TOKEN - Paste the token value
- Assign to all repositories or only the one hosting workflows
Go to your repo(with project) Settings → Secrets and variables → Actions and add:
AWS_ACCOUNT_ID- Your AWS Account IDAWS_ACCESS_KEY_ID- From IAM user creation on AWSAWS_SECRET_ACCESS_KEY- From IAM user creationDOCKERHUB_USERNAME- Your Docker Hub accountDOCKERHUB_TOKEN- Docker Hub → Account Settings → Security
4. (optional) Deploy Bastion VM on AWS - Install the following tools before deploying the application:
Deploy Ubuntu 22 VM 'Bastion' on your default VPC, and in security group open ports 22, 80 and 8080. Install on VM next applications(optional):
- Run workflow setup_github_oidc.yaml - Configure GitHub OIDC provider and create all needed Roles for workflow authentication
- Run workflow create_buckets.yaml - Create S3 bucket for Terraform state storage with versioning and encryption
- Run workflow create_clusters.yaml - Provision EKS cluster with Terraform including VPC, node groups, IAM roles, and OIDC provider
- Run workflow deploy_applications.yaml - Deploy full stack (MySQL, API, Web) with Metrics Server, EBS CSI driver, and HPA configuration
- Run workflow deploy_argocd.yaml to deploy ArgoCD with LoadBalancer
# Get LoadBalancer URL
kubectl get svc argocd-server -n argocd
# Get admin password
kubectl get secret argocd-secret -n argocd -o jsonpath="{.data.admin\.password}" | base64 --decode
echo ""
# Access via browser: http://<LOADBALANCER-URL>
# Username: admin
# Password: (from command above)If your repository is private, ArgoCD needs credentials:
# Create GitHub Personal Access Token at: https://github.com/settings/tokens
# Select scope: 'repo' (Full control of private repositories)
# Add repository to ArgoCD
argocd repo add https://github.com/K8-Team/project-nodejs-app.git \
--username YOUR_GITHUB_USERNAME \
--password YOUR_GITHUB_TOKEN \
--insecure-skip-server-verification
# Verify
argocd repo list# Apply ArgoCD applications (after configuring repository access)
kubectl apply -f ArgoCD/mysql_app.yaml
kubectl apply -f ArgoCD/api_app.yaml
kubectl apply -f ArgoCD/web_app.yaml
# OR use App of Apps pattern (deploys all three)
kubectl apply -f ArgoCD/nodejs-app-stack.yaml
# Check status
argocd app list
argocd app get api# Port-forward to Jenkins
kubectl port-forward -n jenkins svc/jenkins 8080:8080
# Open browser: http://localhost:8080
# Username: admin
# Get Jenkins password
kubectl get secret -n jenkins jenkins -o jsonpath="{.data.jenkins-admin-password}" | base64 --decode
echo
# Password: (from above command)
- In Jenkins UI, click "New Item"
- Enter name:
CD - Select "Pipeline"
- Under Pipeline, select "Pipeline script from SCM"
- SCM: Git
- Repository URL:
https://github.com/YOUR_USERNAME/project-nodejs-app.git - Script Path:
Jenkins-Deployment/Jenkinsfile - Click "Save"
# Create namespace
kubectl create namespace nodejs
# Deploy MySQL
helm install mysql ./Kubernetes-Helm/mysql \
--namespace nodejs \
--set statefulset.image.tag=8.0
# Deploy API
helm install api ./Kubernetes-Helm/api \
--namespace nodejs \
--set deployment.image.repository=$(aws sts get-caller-identity --query Account --output text).dkr.ecr.us-east-1.amazonaws.com/nodejs-nyan-app/api \
--set deployment.image.tag=latest
# Deploy Web
helm install web ./Kubernetes-Helm/web \
--namespace nodejs \
--set deployment.image.repository=$(aws sts get-caller-identity --query Account --output text).dkr.ecr.us-east-1.amazonaws.com/nodejs-nyan-app/web \
--set deployment.image.tag=latest- Role-GitHubActionsTerraformEKSInfra - Allows GitHub Actions workflows to authenticate with AWS and manage EKS infrastructure.
- Role-cluster-role-{cluster_name} - Provides permissions for the EKS control plane to manage AWS resources.
- Role-node-group-role-{cluster_name} - Grants EC2 instances in EKS node groups permissions to join the cluster.
- Role-aws-load-balancer-controller-{cluster_name} - Enables the AWS Load Balancer Controller to create and manage load balancers.
- Role-CSIdriverECRtoEBS - Allows the EBS CSI driver to create and manage EBS volumes for persistent storage.
- Role-JenkinsKanikopushECR - Grants Jenkins Kaniko pods permission to build and push Docker images to ECR.
- Role-ArgoCD-Deployment - Provides ArgoCD Application Controller with access to ECR, EKS, Secrets Manager, and S3.
- Role-ArgoCD-RepoServer - Allows ArgoCD Repo Server to pull images from ECR and access Helm charts.
- Role-ArgoCD-ImageUpdater - Enables ArgoCD Image Updater to monitor ECR for new image tags.
- jenkins-cd-role - Grants Jenkins service account cluster-wide permissions to deploy applications.
- argocd-manager-role - Provides ArgoCD service account with full cluster access to manage resources.