Abdul Munaim Dar
← Back to projects

Secure EKS Platform with GitOps

Six-service EKS platform with Multi-AZ networking, IRSA on every workload, one shared ALB, and declarative zero-downtime delivery via Argo CD.

Secure EKS Platform with GitOps architecture diagram

Overview

A reference implementation of a production-shaped EKS platform: six services behind one shared Application Load Balancer, deployed entirely by Argo CD from a single Git repository. The goal was to show the full path from network to running pod — Multi-AZ VPC, EKS cluster, IAM, and GitOps delivery — as a consistent, internally coherent system rather than a collection of disconnected snippets.

Every identifier in the repo is fictional (placeholder account ID, example.com domains), but the manifests are complete and internally consistent — point them at a real AWS account and they apply as-is.

Architecture

Three subnet tiers span three availability zones: public subnets hold the ALB and NAT gateway, private subnets hold the EKS managed node group and VPC interface endpoints (ECR, SSM, CloudWatch, STS), and database subnets — with no route to NAT at all — hold RDS PostgreSQL and ElastiCache Redis. The node group has no public IPs; the ALB reaches pods directly on their private addresses via target-type: ip.

All six services share a single ALB — both Ingress objects declare the same alb.ingress.kubernetes.io/group.name, so the controller merges them into one load balancer instead of six. Every workload runs under its own IAM role via IRSA, scoped to a single ServiceAccount and a single SSM Parameter Store prefix, so a compromised pod can’t read another service’s secrets. Non-secret config lives in a ConfigMap reviewable in a pull request; secrets are read at pod start by the Secrets Store CSI driver, and no long-lived AWS keys exist anywhere in the cluster.

Provisioning (VPC, EKS, EFS, RDS, Redis) is eleven CloudFormation stacks under infrastructure/. Everything above that — namespaces, ingress, storage claims, and all six service deployments — is reconciled by Argo CD from the same repository.

Key decisions & tradeoffs

CloudFormation over Terraform. No state file to store, lock, or corrupt; drift detection and rollback come from the service itself rather than a separately-managed backend.

Immutable image tags, not latest. CI tags each image with its commit SHA and rewrites the manifest. The deployed version is always traceable to a specific commit, and rollback is just git revert.

CI never touches the cluster. The build pipeline authenticates to AWS via OIDC (no stored keys), pushes to ECR, and commits a one-line manifest change. Argo CD, which does hold cluster access, only ever reads from Git — so a compromised CI pipeline can push a bad image but can’t reach the cluster API.

Requests everywhere, CPU limits nowhere. CPU limits add CFS throttling, which hurts tail latency more than a noisy neighbor does. Memory is limited, because unlike CPU it’s incompressible — exceeding it has to kill the pod.

Probes tuned per workload, not copy-pasted. The long-running conversion service tolerates minutes of unresponsiveness before a restart, because a worker mid-job isn’t a failed worker; the web frontend uses a startupProbe so a slow boot doesn’t trip a strict liveness check.

No service mesh, no HPA. Six services with straightforward north-south traffic don’t justify Istio or Linkerd, and autoscaling thresholds should come from observed load rather than invented numbers.

Results

What the finished design demonstrates end-to-end:

  • One load balancer serving six services instead of six — a direct, measurable infra-cost reduction over the naive per-service default.
  • Zero long-lived AWS credentials anywhere in the cluster — every pod-to-AWS call is scoped, short-lived IRSA access.
  • A deploy is a Git commit; a rollback is git revert — no manual kubectl once the cluster is bootstrapped.
  • Full network isolation for stateful services: the database subnets have no route to the internet in either direction.