dora.ed
← Projects
02 / Container Orchestration

Kubernetes Delivery Platform on AWS EKS

A production-grade container orchestration platform deployed on AWS EKS. Terraform provisions the full infrastructure stack: VPC, EKS cluster, EC2 worker nodes, IAM roles, and OIDC provider. A static portfolio app is containerised with Docker/NGINX, packaged with Helm, and deployed automatically via GitHub Actions with IAM OIDC. An AWS Elastic Load Balancer routes external traffic through NGINX Ingress to the pods.

Key Results

Delivered a full container lifecycle — build, package, deploy, probe — as a parallel track to the AWS static path, demonstrating two production-grade deployment approaches in one platform

Packaged releases with Helm to standardise upgrades, rollback workflows, and environment configuration across the cluster

Configured readiness and liveness probes to enforce health-gate behaviour, preventing traffic from reaching containers before they were ready

Architecture
git pushmain branchGitHub Actionsdocker build + pushIAM OIDC → EKShelm upgradeAWS eu-west-2 — VPCELBInternet-facingNGINX Ingressportfoliod-ingressEKS — portfoliod-clusterService (ClusterIP)Pod · Node 1t3.small · Ready ✓Pod · Node 2t3.small · Ready ✓
Problem

Running containers on a single server with no orchestration means no self-healing, no rolling updates, no load balancing, and no production-grade infrastructure. A crash takes the service down permanently.

Solution

AWS EKS manages the Kubernetes control plane. Terraform provisions the full VPC and cluster stack. Helm deploys the app. GitHub Actions automates every build and deployment with IAM OIDC — no stored AWS credentials.

Proof of implementation
GitHub Actions — Build and Deploy to EKS ✅
GitHub Actions green

Both jobs green: Build and Push Docker Image (16s) → Deploy to EKS via Helm (1m 22s). Triggered by push to main. Status: Success. Total duration: 1m 57s.

Live app serving via AWS Elastic Load Balancer
Live app via ELB

portfoliod app live at the ELB DNS name. Traffic flows Browser → ELB → NGINX Ingress → Service → Pod.

EKS nodes — 2× t3.small Ready
EKS nodes

2 nodes in portfoliod-cluster-nodes. Both Status = Ready.

EC2 instances — worker nodes running
EC2 instances

2 t3.small instances Running across eu-west-2a and eu-west-2b.

Terraform outputs
cluster_endpoint = "https://21F972C4047FB6CBB4CDE6F2E67ED923.gr7.eu-west-2.eks.amazonaws.com"
cluster_name     = "portfoliod-cluster"
cluster_region   = "eu-west-2"
github_role_arn  = "arn:aws:iam::127486921697:role/portfoliod-cluster-github-deploy"
vpc_id           = "vpc-0aad5cf237cc566bc"
Infrastructure stack — provisioned by Terraform
VPC

Custom VPC with public + private subnets across 2 AZs. Public subnets for the load balancer, private subnets for worker nodes.

NAT Gateway

Allows private subnet nodes to pull Docker images from DockerHub without being publicly accessible.

EKS Cluster v1.31

AWS-managed Kubernetes control plane. Endpoint access: public + private. No control plane to maintain or patch.

Node Group

2× t3.small EC2 instances across eu-west-2a and eu-west-2b. Managed node group — AWS handles patching.

IAM Roles

Separate roles for EKS cluster, worker nodes, and GitHub Actions OIDC. Least-privilege policies on each.

OIDC Provider

GitHub Actions authenticates to AWS via short-lived tokens. No access keys stored in GitHub Secrets.

Engineering decisions
Why EKS over minikube for this implementation

minikube runs a single-node cluster locally — useful for learning but not representative of production. EKS provisions real EC2 worker nodes inside a VPC, with a managed control plane, IAM integration, and an Elastic Load Balancer for ingress. Every component in this implementation maps directly to what you would run in a production environment.

Why Terraform for the entire infrastructure stack

The VPC, subnets, NAT Gateway, Internet Gateway, EKS cluster, node group, IAM roles, and OIDC provider are all provisioned by Terraform. This means the entire cluster can be created in one command (terraform apply) and destroyed in one command (terraform destroy) — reproducible, version-controlled, and zero manual console setup.

Why three separate health probe endpoints

The NGINX container serves /ready and /live as separate lightweight endpoints returning JSON. Readiness controls whether the pod receives traffic. Liveness controls whether the container is restarted. The startup probe disables liveness during container startup, preventing crash loops on slow-starting containers.

Why IAM OIDC for GitHub Actions instead of stored credentials

The Terraform stack provisions an IAM OIDC provider and a scoped IAM role. GitHub Actions assumes this role via short-lived tokens — no AWS access keys are stored anywhere in the repository. The trust policy is locked to the specific repo and branch.

Why Helm for packaging Kubernetes manifests

Raw kubectl manifests work for a single environment but don't scale. The Helm chart templates the Deployment, Service, and Ingress with configurable values — replica count, image tag, resource limits — so the same chart deploys across environments. helm upgrade --install handles both first-time installs and updates in a single command.

Lessons learned
01

EKS cluster creation takes 10-15 minutes — significantly longer than other AWS resources. The node group takes an additional 3-5 minutes after the cluster is ready.

02

The IAM OIDC provider for GitHub Actions (token.actions.githubusercontent.com) is account-level. Attempting to create it twice fails with EntityAlreadyExists. Import the existing provider into Terraform state with terraform import rather than recreating it.

03

NGINX Ingress on EKS requires the controller service type to be LoadBalancer — this triggers AWS to provision an Elastic Load Balancer automatically. The EXTERNAL-IP shows <pending> until the ELB is fully provisioned (~2 minutes).

04

terraform destroy must be run after helm uninstall to avoid dependency conflicts. Helm removes Kubernetes resources first, which allows Terraform to cleanly destroy the VPC without stuck dependencies on the load balancer.

Tech stack
AWS EKSAWS EC2 (t3.small)AWS VPCTerraformDockerHelmNGINX IngressKubernetesGitHub ActionsIAM OIDCDockerHubElastic Load Balancer
View repository ↗
See the CI/CD pipeline that deploys to this cluster

Project 03 documents the GitHub Actions pipeline — IAM OIDC, dual-track deployments, and keyless AWS authentication.

View CI/CD Pipeline →