Amazon EKS: Managed Kubernetes on AWS
Amazon EKS (Elastic Kubernetes Service) is a managed Kubernetes control plane that runs the upstream Kubernetes distribution — handling control plane availability, updates, and scaling so you focus on your applications.
What You’ll Learn
- Setting up an EKS cluster with managed node groups
- IAM roles for service accounts (IRSA) for fine-grained permissions
- Installing ALB ingress controller for traffic routing
- Cluster Autoscaler vs Karpenter for node scaling
- EBS and EFS CSI drivers for persistent storage
- Cost optimization strategies for EKS
Why EKS Matters
Running Kubernetes yourself means managing the control plane (etcd, API server, scheduler, controller manager) — which requires expertise, dedicated resources, and constant maintenance. EKS manages the control plane for you, providing automatic updates, patching, and 99.95% SLA. DodaTech runs Durga Antivirus Pro’s internal tooling on EKS — data processing jobs, CI/CD runners, and internal dashboards all run on a shared EKS cluster, with each team having their own namespaces and IAM roles.
flowchart LR
A[Kubernetes & AWS] --> B[Amazon EKS]
B --> C[Control Plane]
B --> D[Node Groups]
B --> E[Ingress]
B --> F[Storage]
B --> G[Scaling]
C --> H[Managed by AWS]
D --> I[Managed / Self-Managed / Fargate]
E --> J[ALB Ingress Controller]
G --> K[Cluster Autoscaler / Karpenter]
style B fill:#f90,color:#fff
Cluster Setup
Terraform Configuration
# eks-cluster.tf
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "~> 19.0"
cluster_name = "dodatech-prod"
cluster_version = "1.28"
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnets
cluster_endpoint_public_access = false # Private cluster only
eks_managed_node_groups = {
general = {
desired_size = 3
min_size = 2
max_size = 10
instance_types = ["m5.large", "m5a.large"]
capacity_type = "ON_DEMAND"
}
spot = {
desired_size = 2
min_size = 0
max_size = 20
instance_types = ["c5.2xlarge", "c5a.2xlarge"]
capacity_type = "SPOT"
}
}
tags = {
Environment = "production"
}
}
data "aws_eks_cluster_auth" "this" {
name = module.eks.cluster_name
}# Configure kubectl
aws eks update-kubeconfig --region us-east-1 --name dodatech-prod
# Verify cluster
kubectl get nodes
# Output:
# NAME STATUS ROLES AGE VERSION
# ip-10-0-1-101.ec2.internal Ready <none> 2h v1.28.3
# ip-10-0-2-102.ec2.internal Ready <none> 2h v1.28.3
# ip-10-0-3-103.ec2.internal Ready <none> 2h v1.28.3IAM Roles for Service Accounts (IRSA)
IRSA maps Kubernetes service accounts to IAM roles, giving each pod precise permissions:
# irsa.tf
module "irsa" {
source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks"
role_name = "app-s3-access"
attach_s3_policy = true
s3_bucket_arns = ["arn:aws:s3:::dodatech-app-data/*"]
oidc_providers = {
main = {
provider_arn = module.eks.oidc_provider_arn
namespace_service_accounts = ["default:app-sa"]
}
}
}# app-sa.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-sa
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/app-s3-access
---
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
serviceAccountName: app-sa
containers:
- name: app
image: dodatech/app:latest
# This pod can now read/write to the S3 bucketALB Ingress Controller
Route external traffic to services:
# alb-ingress.tf
resource "helm_release" "aws_lb_controller" {
name = "aws-load-balancer-controller"
repository = "https://aws.github.io/eks-charts"
chart = "aws-load-balancer-controller"
namespace = "kube-system"
set {
name = "clusterName"
value = module.eks.cluster_name
}
}# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: dodatech-api
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]'
alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:us-east-1:123456789012:certificate/abc123
spec:
rules:
- host: api.dodatech.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 8080Scaling: Cluster Autoscaler vs Karpenter
Cluster Autoscaler
Scales node groups based on unschedulable pods:
# cluster-autoscaler-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: cluster-autoscaler-status
namespace: kube-system
data:
# Autoscaler will scale between min and max set in node group
# Works with ASGs — limited to instance types in the ASG
---
# Requires IAM permissions to modify ASG desired countsKarpenter
Karpenter is a newer, more flexible autoscaler that provisions instances directly:
# karpenter-provisioner.yaml
apiVersion: karpenter.sh/v1alpha5
kind: Provisioner
metadata:
name: default
spec:
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["on-demand", "spot"]
- key: kubernetes.io/arch
operator: In
values: ["amd64", "arm64"]
- key: node.kubernetes.io/instance-type
operator: In
values:
- m5.large
- m5.xlarge
- c5.2xlarge
limits:
resources:
cpu: 1000
provider:
subnetSelector:
kubernetes.io/cluster/dodatech-prod: owned
securityGroupSelector:
kubernetes.io/cluster/dodatech-prod: owned
ttlSecondsAfterEmpty: 30Why Karpenter wins: Provisions instances in seconds (vs minutes for ASGs), supports spot and on-demand together, and manages multiple instance types without creating multiple node groups.
Storage with CSI Drivers
# ebs-storage.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ebs-claim
spec:
accessModes:
- ReadWriteOnce
storageClassName: gp3
resources:
requests:
storage: 100Gi
---
# After installing aws-ebs-csi-driver via Helm
# Pods can mount EBS volumes for persistent storage# efs-storage.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: efs-claim
spec:
accessModes:
- ReadWriteMany # Multiple pods can read/write simultaneously
storageClassName: efs-sc
resources:
requests:
storage: 100Gi # EFS is elastic — this is a placeholderCost Optimization
| Strategy | Savings | Notes |
|---|---|---|
| Spot instances | 60-90% | Use spot for batch, stateless, and non-critical workloads |
| Karpenter | 20-40% | Better packing, multi-instance-type selection |
| Fargate for control plane | Fixed cost | No node management for system pods |
| Graviton (ARM) | 20-40% | Better perf/cost ratio for most workloads |
| Cluster rightsizing | 30-50% | Use AWS Compute Optimizer recommendations |
Common Mistakes
Publicly accessible cluster endpoint: Exposing the Kubernetes API server to the internet is a security risk. Set
cluster_endpoint_public_access = falseand use VPN or Direct Connect for access.Not using IRSA: Giving nodes broad IAM permissions means any pod can access any AWS resource. Use IRSA to give each pod exactly the permissions it needs.
Over-provisioning node groups: Multiple large node groups with fixed instance types waste money. Use Karpenter with diverse instance types for better packing and cost.
No pod resource requests/limits: Without resource requests, the scheduler can’t make informed decisions. Without limits, a runaway pod can starve other pods. Always set both.
Ignoring cluster autoscaling: Without autoscaling, you either over-provision (waste money) or under-provision (outages). Always configure at least one autoscaler.
Practice Questions
What is the difference between EKS and self-managed Kubernetes? Answer: EKS manages the control plane (API server, etcd) — AWS handles updates, patching, and HA. Self-managed requires you to run and maintain the control plane on your own infrastructure.
What is IRSA and why is it important? Answer: IAM Roles for Service Accounts maps Kubernetes service accounts to IAM roles. It gives each pod fine-grained AWS permissions without sharing node-level credentials.
How does Karpenter differ from Cluster Autoscaler? Answer: Cluster Autoscaler works with ASGs and takes minutes to provision nodes. Karpenter provisions instances directly in seconds, supports multiple instance types, and handles spot/on-demand together.
When would you use EFS vs EBS for persistent storage? Answer: EBS for single-pod storage (databases) — ReadWriteOnce, low latency. EFS for multi-pod storage (shared configs, logs) — ReadWriteMany, higher latency.
Challenge
Deploy a production EKS cluster: create an EKS cluster with Terraform (private endpoint, managed node groups, IRSA), install ALB ingress controller, deploy a sample API with autoscaling, configure Karpenter for node provisioning, set up EBS CSI driver for database storage, implement spot instances for non-critical workloads, and configure CloudWatch Container Insights for monitoring.
FAQ
Mini Project: EKS Status Checker
#!/bin/bash
# eks-status.sh
CLUSTER="dodatech-prod"
REGION="us-east-1"
echo "=== EKS Cluster Status ==="
aws eks describe-cluster --name $CLUSTER --region $REGION \
--query '{Status: cluster.status, Version: cluster.version, Endpoint: cluster.endpoint, Created: cluster.createdAt}'
echo -e "\n=== Node Groups ==="
aws eks list-nodegroups --cluster-name $CLUSTER --region $REGION \
--query 'nodegroups' --output yaml
echo -e "\n=== Nodes ==="
kubectl get nodes -o wide
echo -e "\n=== System Pods ==="
kubectl get pods -n kube-system
echo -e "\n=== Resource Usage ==="
kubectl top nodesWhat’s Next
| Topic | Description |
|---|---|
| Build and deploy serverless apps | |
| Running containers at scale |
Related topics: Kubernetes, AWS, Terraform, EKS
What’s Next
Congratulations on completing this EKS tutorial! Here’s where to go from here:
- Practice daily — Deploy an app on EKS with a CI/CD pipeline
- Build a project — Set up Karpenter with spot and on-demand nodes
- Explore related topics — Check out the Serverless Framework
Remember: every expert was once a beginner. Keep coding!
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro