ak-127 / sai-prj2
☸ AWS EKS
⎈ Helm v3
20+ Releases

Kubernetes CI/CD
on AWS EKS

Production-grade Django deployment pipeline using AWS EKS, Helm, and GitHub Actions — keyless OIDC auth, automated rollbacks, zero-downtime releases on every git push.

☸️ Kubernetes EKS
🔧 GitHub Actions CI/CD
☁️ AWS ECR + IAM OIDC
🐍 Django Application
⎈ Helm Charts
🐳 Docker
🔐 Keyless Auth
📜 Cert Manager
⚖️ AWS Load Balancer
3
Pipeline Jobs
5
K8s Add-ons
20+
Releases
0
Static Keys
Zero Downtime
Automated Workflow

GitHub Actions CI/CD Pipeline

Every git push to main triggers the full pipeline — from code checkout to live deployment on AWS EKS via Helm, with zero manual steps.

👨‍💻
Git Push
main branch
Developer
🐙
GitHub
Actions triggered
SCM Trigger
🔐
OIDC Auth
AWS role assumed
No Static Keys
🐳
Docker Build
Tag + Push ECR
Containerize
Helm Deploy
upgrade --install
K8s Package
☸️
AWS EKS
Rolling update
Kubernetes
🌐
Live on ALB
HTTPS · TLS
Deployed ✓
.github/workflows/deploy.yml
● GitHub Actions · OIDC Auth
name: Deploy Django to EKS
on:
  push:
    branches: [main]

permissions:
  id-token: write   # Required for OIDC
  contents: read

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

      - name: Configure AWS via OIDC  # ← No static keys!
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
          aws-region: ${{ secrets.AWS_REGION }}

      - name: Login to ECR
        run: |
          aws ecr get-login-password | docker login \
            --username AWS --password-stdin \
            ${{ secrets.ECR_REGISTRY }}

      - name: Build & Push Docker Image
        run: |
          IMAGE_TAG=${{ github.sha }}
          docker build -t $ECR_REGISTRY/$ECR_REPO:$IMAGE_TAG .
          docker push $ECR_REGISTRY/$ECR_REPO:$IMAGE_TAG

      - name: Deploy to EKS via Helm
        run: |
          aws eks update-kubeconfig \
            --name ${{ secrets.EKS_CLUSTER_NAME }}
          helm upgrade --install django-app ./django-chart \
            --set image.tag=$IMAGE_TAG \
            --set image.repository=$ECR_REGISTRY/$ECR_REPO \
            --namespace production --create-namespace --wait
System Design

EKS Infrastructure Architecture

Full Kubernetes architecture on AWS EKS — AWS Load Balancer routes traffic to Django pods managed by Helm. EBS CSI provides persistent storage. Cert Manager handles TLS certificates automatically.

🔧 GitHub Actions Build · Push · Deploy OIDC · No Static Keys 📦 Amazon ECR Container Registry Tagged by git SHA docker push AWS EKS CLUSTER ☸️ EKS Control Plane Managed by AWS · v1.28+ API Server · OIDC · etcd ⚖️ AWS Load Balancer Application LB (ALB) :80 · :443 · SSL term. 🌐 Client / Internet HTTPS Request Let's Encrypt TLS HTTPS WORKER NODE GROUP (t3.small × 2, autoscale 1→4) 🐍 Django Pod Gunicorn · :8000 STATUS: Running ✓ Liveness ✓ Readiness ✓ cpu: 100m · mem: 256Mi 🐍 Django Pod Gunicorn · :8000 STATUS: Running ✓ Liveness ✓ Readiness ✓ cpu: 100m · mem: 256Mi 🔗 K8s Service (ClusterIP) selector: app=django-app port 80 → targetPort 8000 📈 HPA — Autoscaler min:1 · max:4 · cpu 70% EKS ADD-ONS 💾 EBS CSI Driver Persistent Volumes EBS gp2/gp3 · IRSA DB data persistence addon: aws-ebs-csi-driver ⚖️ LB Controller AWS ALB Provisioner Helm: eks/alb-ctrl Ingress → ALB auto v2.12.0 · IRSA 🔒 Cert Manager TLS Auto-provisioning Let's Encrypt · ACME Auto cert renewal jetstack/cert-manager route helm deploy docker pull
Kubernetes Packaging

Helm Chart Structure

The django-chart/ Helm chart defines all Kubernetes resources as templates — parameterized, reusable, and version-controlled. One command deploys or upgrades everything.

django-chart/ — Helm Chart Layout
📁django-chart/
├── Chart.yaml— chart metadata & version
├── values.yaml— default config values
└── templates/
├── deployment.yaml— K8s Deployment
├── service.yaml— ClusterIP Service
├── ingress.yaml— ALB Ingress
├── hpa.yaml— HorizontalPodAutoscaler
├── configmap.yaml— App config
├── secret.yaml— K8s Secrets
├── pvc.yaml— PersistentVolumeClaim
└── certificate.yaml— TLS cert (cert-manager)
values.yaml — Key Config
replicaCount: 2

image:
  repository: .dkr.ecr.us-east-1.amazonaws.com/saiapp
  tag: latest   # overridden with git SHA by CI
  pullPolicy: Always

service:
  type: ClusterIP
  port: 80
  targetPort: 8000

resources:
  requests: {cpu: 100m, memory: 256Mi}
  limits:   {cpu: 500m, memory: 512Mi}

autoscaling:
  enabled: true
  minReplicas: 1
  maxReplicas: 4
  targetCPUUtilizationPercentage: 70

ingress:
  enabled: true
  className: alb
  host: your-domain.com
  tls: true   # cert-manager handles this
🔄
Rolling Updates
Helm's rolling update strategy ensures zero downtime. New pods start before old ones terminate.
↩️
Instant Rollback
helm rollback reverts to any previous revision in seconds. 20+ revision history maintained.
⚙️
Parameterized
Override any value at deploy time using --set. Same chart works for dev and prod.
📋
Version Control
Every helm release is versioned. Full audit trail of what changed and when with helm history.
Helm Commands — Lifecycle
# First deploy
helm upgrade --install django-app ./django-chart \
  --set image.tag=abc1234 \
  --namespace production \
  --create-namespace --wait

# Check release status
helm status django-app -n production

# View all revisions
helm history django-app -n production
# REVISION   STATUS     CHART           DESCRIPTION
# 1          deployed   django-0.1.0    Install
# 2          deployed   django-0.1.0    Upgrade

# Rollback if needed
helm rollback django-app 1 -n production

# List all deployed releases
helm list -A
Zero Static Keys

GitHub OIDC ↔ AWS IAM Trust

Instead of storing long-lived AWS access keys as GitHub secrets, this project uses OIDC federation — GitHub Actions requests a short-lived token from AWS on each run. No credentials can be leaked or rotated.

🔧 GitHub Actions Workflow triggered on push to main id-token: write OIDC token JWT request 🔑 OIDC Provider token.actions. githubusercontent.com Issues JWT token signed & short-lived JWT verify claim match 🏛️ AWS IAM Identity Provider Validates JWT claims: repo: ak-127/sai-prj2 branch: main audience: sts.amazonaws.com Issues temp. credentials temp creds 15min TTL 📦 Amazon ECR Push Docker image saiapp:abc1234 ☸️ AWS EKS helm upgrade --install ✓ Zero Static Credentials Stored AWS access keys never touch GitHub • Tokens expire in 15 minutes
🔐
No Stored AWS Keys
AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are never stored anywhere. OIDC tokens are issued per-run and expire automatically.
📋
Least Privilege IAM
The GitHub IAM role has only ECR push and EKS describe access. No admin privileges. Production cluster uses AmazonEKSEditPolicy.
🛡️
Repo-Scoped Claims
The IAM trust policy validates the GitHub repo, org, and branch in the JWT claim. Other repos cannot assume this role — ever.
🔑
Secrets via GitHub Secrets
App secrets (Django key, DB password, CSRF origins) stored in GitHub encrypted secrets — injected as env vars at runtime, never in code.
🌐
TLS Everywhere
Cert Manager provisions Let's Encrypt certificates automatically. ALB terminates TLS. HTTP redirects to HTTPS enforced by Nginx annotations.
☸️
K8s EKS Access Entries
Uses modern EKS Access Entries API (v1.28+) instead of legacy aws-auth ConfigMap. Fine-grained, auditable cluster permission management.
Step-by-Step Setup

How to Deploy This

Full infrastructure setup guide — provision the EKS cluster once, then all subsequent application deployments happen automatically via GitHub Actions on every git push.

1
Create EKS Cluster
# Provision managed cluster eksctl create cluster \ --name django-cluster \ --region us-east-1 \ --node-type t3.small \ --nodes 2 --nodes-max 4 \ --managed # Enable OIDC provider eksctl utils associate-iam-oidc-provider \ --cluster django-cluster \ --approve
Provision the EKS cluster with managed node groups. Enable OIDC provider — required for IRSA (IAM Roles for Service Accounts) used by EBS CSI and ALB controller.
2
Install EKS Add-ons
# EBS CSI Driver (IRSA) eksctl create iamserviceaccount \ --name ebs-csi-controller-sa \ --namespace kube-system \ --cluster django-cluster \ --role-name AmazonEKS_EBS_CSI_DriverRole \ --attach-policy-arn \ arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy \ --approve aws eks create-addon \ --cluster-name django-cluster \ --addon-name aws-ebs-csi-driver
Install EBS CSI driver, AWS Load Balancer Controller, and Cert Manager. Each uses IRSA for secure, pod-scoped IAM permissions. See DEPLOYMENT.md for full ALB and Cert Manager steps.
3
Configure OIDC Trust
# Register GitHub as IdP in AWS IAM Provider URL: https://token.actions.githubusercontent.com Audience: sts.amazonaws.com # Create IAM Role: Github Type: Web Identity org: ak-127 · repo: sai-prj2 # Grant EKS access (v1.28+) aws eks create-access-entry \ --cluster-name django-cluster \ --principal-arn arn:aws:iam:::role/Github aws eks associate-access-policy \ --policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy
Configure GitHub as an OIDC Identity Provider in AWS IAM. Create an IAM role scoped to your specific repo and branch. Grant EKS access using the modern Access Entries API.
4
Set GitHub Secrets
Settings → Secrets → Actions AWS_ROLE_ARN arn:aws:iam:::role/Github AWS_REGION → us-east-1 ECR_REPOSITORY → saiapp EKS_CLUSTER_NAME → django-cluster DJANGO_SECRET_KEY → CSRF_TRUSTED_ORIGINS → https://domain.com DJANGO_SUPERUSER_PASSWORD → POSTGRES_PASSWORD →
Add these secrets to your GitHub repository. The OIDC role ARN replaces AWS access keys entirely. All other secrets are injected as Helm values into Kubernetes secrets at deploy time.
5
Push & Auto-Deploy
# From now on, every push deploys: git add . git commit -m "feat: new feature" git push origin main # GitHub Actions will automatically: # 1. Authenticate via OIDC # 2. Build & push Docker image to ECR # 3. helm upgrade --install # 4. Rolling update on EKS
After Phases 1–4 are complete, the pipeline is fully automated. Every push to main triggers build, push, and deploy. Watch it run in the GitHub Actions tab — full live logs.
6
Verify & Monitor
# Check pods are running kubectl get pods -n production # Check all K8s resources kubectl get all -n production # View Helm release history helm history django-app -n production # Rollback if needed helm rollback django-app 1 -n production # Watch logs live kubectl logs -l app=django-app -n production -f
Verify pods are healthy, check ingress for your ALB DNS name, confirm TLS certificates are issued by Cert Manager. Use helm history to see all releases.
GitHub Repository Secrets Reference
⚠ Settings → Secrets and variables → Actions
AWS_ROLE_ARN                 # arn:aws:iam:::role/Github
AWS_REGION                   # us-east-1
ECR_REPOSITORY               # saiapp
EKS_CLUSTER_NAME             # django-cluster
DJANGO_SECRET_KEY            # django-insecure-... (generate fresh)
CSRF_TRUSTED_ORIGINS         # https://your-domain.com
DJANGO_SUPERUSER_PASSWORD    # admin user password
POSTGRES_PASSWORD            # database password

# Note: AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are NOT needed.
# OIDC handles AWS authentication without any stored credentials.
Full Technology Overview

Complete Tech Stack

Category
Technology
Role in Project
🐍 App
Python · Django
Web framework serving the blog application. Gunicorn as WSGI server. Entrypoint script handles migrations on startup.
🐳 Container
Docker · .dockerignore
Optimized multi-layer image build. Tagged with Git SHA for traceability. Stored in Amazon ECR private registry.
☸️ Orchestration
Kubernetes · AWS EKS
Managed K8s cluster. Worker node auto-scaling 1→4. Rolling updates, self-healing pods, resource limits enforced.
⎈ Packaging
Helm v3 · django-chart
All K8s resources templated as a Helm chart. Parameterized values, rollback history, same chart for dev and prod.
🔧 CI/CD
GitHub Actions · OIDC
Declarative pipeline. Keyless AWS authentication via OIDC federation. Triggered on push to main. Zero manual steps.
☁️ Cloud
AWS (EKS · ECR · IAM · EBS)
Fully managed infrastructure. IAM OIDC federation for secure CI access. EBS for persistent volume storage.
⚖️ Ingress
AWS Load Balancer Controller
Automatically provisions AWS ALB from Kubernetes Ingress resources. Helm-installed with IRSA service account.
🔒 TLS
Cert Manager · Let's Encrypt
Automatic certificate provisioning and renewal. No manual cert management. ACME DNS challenge via jetstack chart.
📦 Storage
EBS CSI Driver · PVCs
Dynamic EBS volume provisioning via EKS addon. Persistent volume claims for database and static file storage.
🐚 Scripts
Bash · entrypoint.sh
Container startup script runs DB migrations and collects static files before Gunicorn starts. Utility scripts for cluster ops.
What's Next

Future Roadmap

🏗️
Terraform IaC
Move EKS cluster, VPC, IAM roles, and ECR provisioning to Terraform for fully version-controlled, reproducible infrastructure.
In Progress
📊
Prometheus + Grafana Monitoring
Add observability stack — pod metrics, Django app metrics, PostgreSQL dashboards, alerting on HPA scale events.
Planned
🔵
Blue-Green Deployments
Extend Helm chart to support blue-green strategy — run two environments simultaneously and shift ALB traffic on validation.
Planned
🔄
GitOps with ArgoCD
Replace Helm push-deploy with ArgoCD pull-based GitOps model — automatic sync on every change to the Helm chart in git.
Future
🚨
ELK Stack — Centralized Logging
Ship all container and K8s audit logs to Elasticsearch. Kibana dashboards for application error tracking and analysis.
Future
🔐
AWS Secrets Manager Integration
Replace GitHub secrets for app config with AWS Secrets Manager + External Secrets Operator for centralized secret management.
In Progress