Kubernetes & EKS Deep Dive: From Zero to Production
Jenkins
Source: Dev.to | Jan 05, 2026 (UTC)
Use the table of content to navigate the concepts
Two weeks have passed after the discussion on AWS Container Services . Alex is back at the coffee shop, laptop open, with a slightly worried expression. Sam walks in with two coffees.
Sam:sets down coffee Okay, that text you sent at 2 AM saying “I think I need Kubernetes after all” was concerning. What happened?
Alex:laughs nervously Don’t worry, nothing’s on fire. The ECS setup is working great actually! But… our CTO just announced we’re acquiring a startup. They’re running everything on Kubernetes. And our new lead developer keeps talking about “the K8s ecosystem” and “cloud-native patterns.”
Sam: Ah, the classic acquisition scenario. So now you need to actually understand Kubernetes, not just know it exists.
Alex: Exactly. And honestly, after working with ECS, I’m curious. What am I missing? Why do people love Kubernetes so much they tattoo the logo on their arms?
Sam:spits out coffee Someone actually did that?
Alex: I saw it on Twitter!
Sam:shakes head Okay, well… let’s dive deep. Fair warning: this is going to be a longer conversation. Kubernetes isn’t something you grasp in ten minutes.
Alex: I’ve got all afternoon. Teach me, sensei.
Understanding Kubernetes: The Philosophy
Sam: Before we touch EKS, you need to understand Kubernetes itself. Think back to our last conversation – I said Kubernetes is complex. But there’s a reason for that complexity. It’s not accidental.
Kubernetes was built to solve Google’s problems: running billions of containers across thousands of machines. It’s designed for:
Declarative configuration: You say what you want, K8s makes it happen
Self-healing: Things fail, K8s automatically recovers
Extensibility: You can add functionality without changing core code
API-driven: Everything is an API resource
Alex: You lost me at “declarative configuration.”
Sam: Right, let me explain with an example. Remember ECS?
In ECS (Imperative thinking):
# You tell ECS what to DO aws ecs create-service --service-name my-app --desired-count 3 aws ecs update-service --service-name my-app --desired-count 5
Enter fullscreen mode
Exit fullscreen mode
In Kubernetes (Declarative thinking):
# You tell K8s what you WANT apiVersion: apps/v1 kind: Deployment metadata: name: my-app spec: replicas: 5 # I want 5 replicas, K8s makes it happen # ... rest of config
Enter fullscreen mode
Exit fullscreen mode
You apply this file, and Kubernetes continuously works to maintain that state. If a pod dies, K8s starts a new one. If you change replicas from 5 to 10, K8s smoothly adds 5 more.
Alex: So it’s like… I’m describing my desired state, not giving commands?
Sam: Exactly! That’s the Kubernetes mindset. Everything is about desired state vs. current state.
Each controller watches for changes and acts on them
Example: Deployment controller sees replicas: 5, counts current pods, creates more if needed
5. kubelet (on each worker node):
Ensures containers are running in pods
Talks to container runtime (Docker, containerd)
Reports node and pod status back to API Server
6. kube-proxy (on each worker node):
Handles networking
Implements Service abstraction
Routes traffic to appropriate pods
Alex: So when I run kubectl apply -f deployment.yaml, what happens?
Sam: Perfect question! Let’s trace it:
Step-by-Step: kubectl apply -f deployment.yaml 1. kubectl → API Server "Here's a Deployment resource" 2. API Server → etcd "Store this Deployment spec" 3. Deployment Controller (watching API Server) "New Deployment! I need to create a ReplicaSet" 4. Deployment Controller → API Server "Create this ReplicaSet" 5. ReplicaSet Controller (watching) "New ReplicaSet! I need to create Pods" 6. ReplicaSet Controller → API Server "Create 3 Pods please" 7. Scheduler (watching for unassigned Pods) "These Pods need homes... Node 2 has space!" 8. Scheduler → API Server "Assign these Pods to Node 2" 9. kubelet on Node 2 (watching API Server) "I have new Pods to run! Starting containers..." 10. kubelet → Container Runtime "Pull image, create container, start it" 11. kubelet → API Server (continuous) "Status update: Pod is Running"
Enter fullscreen mode
Exit fullscreen mode
Alex: Wow. That’s… a lot of moving parts for just starting a container.
Sam: It is! But that complexity gives you power. Each piece can be extended, replaced, or customized. That’s why Kubernetes can do things ECS can’t.
Enter Amazon EKS
Alex: Okay, so where does EKS fit into all this?
Sam:Amazon EKS (Elastic Kubernetes Service) is AWS’s managed Kubernetes. Here’s what AWS handles for you:
AWS Manages:
Control Plane (API Server, etcd, scheduler, controller manager)
Control plane HA across multiple AZs
Control plane upgrades
Control plane scaling
etcd backups
API Server endpoint
You Manage:
Worker nodes (or use Fargate)
Applications
Add-ons (though some are managed now)
Networking configuration
Security policies
Alex: So it’s like… they run the control plane, I run the workers?
Sam: Nope! You can’t even see those machines. AWS handles all that. You interact with the cluster through kubectl, which talks to the API Server endpoint AWS provides.
Core Kubernetes Concepts: The Building Blocks
Alex: Alright, I need to understand the actual resources. You mentioned Pods, Deployments, Services… break them all down for me.
Sam:cracks knuckles Here we go. This is where Kubernetes gets rich but also complex.
Pods: The Atomic Unit
Sam: A Pod is the smallest deployable unit in Kubernetes. Think of it as a wrapper around one or more containers that need to run together.
Sam: Correct! Secrets in Kubernetes are just base64 encoded by default. In EKS, you should use AWS Secrets Manager or Parameter Store integration for real security. We’ll cover that.
Networking in EKS: The Complex Beast
Alex: Okay, I need to understand networking because I heard it’s complicated.
Sam:deep breath You heard right. Kubernetes networking is… special. Let me break it down.
The Kubernetes Networking Model has three rules:
Every pod gets its own IP address
All pods can communicate with all other pods without NAT
All nodes can communicate with all pods without NAT
Alex: How does EKS implement this?
Sam: Through the Amazon VPC CNI (Container Network Interface) plugin. This is unique to EKS and actually pretty clever.
Traditional Kubernetes: ┌────────────────────────────┐ │ Node (EC2 Instance) │ │ IP: 10.0.1.50 │ │ │ │ ┌──────────────────────┐ │ │ │ Pod 1: 172.16.1.5 │ │ │ └──────────────────────┘ │ │ ┌──────────────────────┐ │ │ │ Pod 2: 172.16.1.6 │ │ │ └──────────────────────┘ │ │ │ │ Pod IPs are virtual │ │ (overlay network) │ └────────────────────────────┘ EKS with VPC CNI: ┌────────────────────────────┐ │ Node (EC2 Instance) │ │ Primary IP: 10.0.1.50 │ │ │ │ ┌──────────────────────┐ │ │ │ Pod 1: 10.0.1.100 │ │ ← Real VPC IP! │ └──────────────────────┘ │ │ ┌──────────────────────┐ │ │ │ Pod 2: 10.0.1.101 │ │ ← Real VPC IP! │ └──────────────────────┘ │ │ │ │ Pods get real ENI IPs │ │ from your VPC! │ └────────────────────────────┘
Enter fullscreen mode
Exit fullscreen mode
Alex: So pods get actual VPC IP addresses?
Sam: Yes! This means:
Pros:
Pods can directly communicate with VPC resources (RDS, ElastiCache, etc.)
VPC Security Groups can apply to pods
No overlay network performance penalty
VPC Flow Logs work
Cons:
You consume IP addresses quickly
Need to plan VPC CIDR blocks carefully
ENI limits per instance type matter
Alex: How many pods can I run per node?
Sam: It depends on the EC2 instance type and its ENI limits:
t3.small: 3 ENIs × 4 IPs = 12 IP addresses - 1 for node - 11 for pods Maximum pods: 11 m5.large: 3 ENIs × 10 IPs = 30 IP addresses - 1 for node - 29 for pods Maximum pods: 29 m5.xlarge: 4 ENIs × 15 IPs = 60 IP addresses Maximum pods: 58
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: postgres-pvc spec: accessModes: - ReadWriteOnce # Can be mounted by one node at a time storageClassName: gp3 resources: requests: storage: 20Gi
# Already installed if you used addons in eksctl # Or install manually kubectl apply -k "github.com/kubernetes-sigs/aws-ebs-csi-driver/deploy/kubernetes/overlays/stable/?ref=release-1.25"
Scenario: Need more pods than nodes can handle 1. Pods are pending (not enough resources) 2. Cluster Autoscaler sees pending pods 3. Checks node group can scale up 4. Triggers ASG to add nodes 5. New node joins cluster 6. Pods schedule on new node Scenario: Nodes are underutilized 1. Node has <50% utilization for 10 minutes 2. All pods can fit elsewhere 3. Cluster Autoscaler cordons node 4. Drains pods (moves them) 5. Terminates node 6. ASG size decreases
Alex: What about when things go wrong? How do I debug?
Sam:pulls out troubleshooting playbook This is essential! Let me walk you through common issues:
Issue 1: Pods Not Starting
# Check pod status kubectl get pods -n myapp # Output shows: NAME READY STATUS RESTARTS AGE api-7d4b6c9f8-abc123 0/1 ImagePullBackOff 0 5m # Get detailed info kubectl describe pod api-7d4b6c9f8-abc123 -n myapp # Look for: # - Image pull errors (wrong image name, permissions) # - Resource constraints (not enough CPU/memory) # - Volume mount errors
Enter fullscreen mode
Exit fullscreen mode
Common causes and fixes:
# Problem: ImagePullBackOff # Fix 1: Check image name image: 123456789.dkr.ecr.us-east-1.amazonaws.com/myapp:v1.0 # Correct region? # Fix 2: Check IAM permissions for pulling from ECR # Service account needs ecr:GetAuthorizationToken, ecr:BatchCheckLayerAvailability # Problem: CrashLoopBackOff # Check logs kubectl logs api-7d4b6c9f8-abc123 -n myapp --previous # Problem: Pending (not scheduling) # Describe pod to see why Events: Type Reason Message ---- ------ ------- Warning FailedScheduling 0/3 nodes available: insufficient memory # Fix: Either reduce pod requests or add more nodes
Enter fullscreen mode
Exit fullscreen mode
Issue 2: Service Not Reachable
# Check if service exists kubectl get svc -n myapp # Check if endpoints exist (pods backing the service) kubectl get endpoints -n myapp NAME ENDPOINTS AGE api 10.0.1.50:3000,10.0.1.51:3000 10m # If no endpoints, pods might not match service selector kubectl get pods -n myapp --show-labels # Test connectivity from another pod kubectl run test --image=busybox -it --rm -- wget -O- api.myapp.svc.cluster.local
Enter fullscreen mode
Exit fullscreen mode
Issue 3: Ingress Not Working
# Check if ingress exists kubectl get ingress -n myapp # Describe to see ALB creation status kubectl describe ingress myapp-ingress -n myapp # Check AWS Load Balancer Controller logs kubectl logs -n kube-system deployment/aws-load-balancer-controller # Common issues: # - IAM permissions for controller # - Subnet tags missing (kubernetes.io/role/elb=1) # - Security groups blocking traffic
Enter fullscreen mode
Exit fullscreen mode
Issue 4: High Memory/CPU Usage
# Check current usage kubectl top pods -n myapp NAME CPU(cores) MEMORY(bytes) api-7d4b6c9f8-abc123 450m 890Mi # Approaching limits! # Check if pods are being OOMKilled kubectl describe pod api-7d4b6c9f8-abc123 -n myapp | grep -A 5 "Last State" # Fix: Adjust resources resources: requests: memory: "256Mi" cpu: "250m" limits: memory: "1Gi" # Increased cpu: "500m"
Enter fullscreen mode
Exit fullscreen mode
Issue 5: PVC Not Binding
# Check PVC status kubectl get pvc -n myapp NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS postgres-pvc Pending gp3 # Describe for details kubectl describe pvc postgres-pvc -n myapp # Common issues: # - Storage class doesn't exist # - No available volumes # - Zone mismatch (PVC in us-east-1a, nodes in us-east-1b) # Fix: Use WaitForFirstConsumer binding mode volumeBindingMode: WaitForFirstConsumer # Wait until pod is scheduled
Enter fullscreen mode
Exit fullscreen mode
Debugging Toolkit
# Get everything in namespace kubectl get all -n myapp # Watch resources live kubectl get pods -n myapp -w # Exec into running container kubectl exec -it api-7d4b6c9f8-abc123 -n myapp -- /bin/sh # Port-forward for local testing kubectl port-forward svc/api 8080:80 -n myapp # Now access at localhost:8080 # Check events kubectl get events -n myapp --sort-by='.lastTimestamp' # Check resource usage kubectl top nodes kubectl top pods -n myapp # View all resources with labels kubectl get all -n myapp --show-labels # Drain node for maintenance kubectl drain node-name --ignore-daemonsets --delete-emptydir-data # Cordon node (stop scheduling new pods) kubectl cordon node-name
Enter fullscreen mode
Exit fullscreen mode
GitOps and CI/CD
Alex: How do I actually deploy updates? Keep editing YAML files and applying them?
Sam: No way! That’s where GitOps comes in. Let me show you a proper CI/CD pipeline.
The GitOps Workflow with Flux or ArgoCD
Developer Flow: 1. Developer → Git Commit → Push to main branch │ ▼ 2. GitHub Actions (CI) - Runs tests - Builds Docker image - Tags image (git sha) - Pushes to ECR - Updates Kubernetes manifest with new image tag - Commits manifest to GitOps repo │ ▼ 3. Flux/ArgoCD (watching GitOps repo) - Detects change - Pulls new manifests - Applies to cluster │ ▼ 4. Kubernetes - Rolling update deployment - New pods come up - Old pods terminate │ ▼ 5. Developer sees app updated!
4. Implement Pod Disruption Budgets for Safe Downscaling
apiVersion: policy/v1 kind: PodDisruptionBudget metadata: name: api-pdb spec: minAvailable: 1 # Keep at least one running during disruptions selector: matchLabels: app: api
Monthly EKS Costs: Control Plane: $73/month (fixed) 3x t3.medium (x86): - On-Demand: $90/month - Spot: ~$27/month (70% savings!) 3x t4g.medium (ARM): - On-Demand: $72/month (20% savings vs x86!) - Spot: ~$22/month (75% savings!) Best Practice Mix: - 2x t4g.medium on-demand (baseline) $48 - 3x t4g.medium spot (burst) $22 - Control plane $73 ---------------------------------------- Total: ~$143/month for 5-node cluster
Enter fullscreen mode
Exit fullscreen mode
Production Readiness Checklist
Alex: Okay, before we wrap up – give me a checklist. What do I need before going to production?
Sam:pulls out final napkin Here’s my production readiness checklist:
Infrastructure
[ ] Multi-AZ deployment (at least 2 AZs)
[ ] Private subnets for worker nodes
[ ] NAT Gateways for outbound internet
[ ] VPC Flow Logs enabled
[ ] Control plane logging enabled
[ ] Cluster autoscaling configured
[ ] Pod autoscaling (HPA) for services
[ ] Proper instance types selected (right-sized)
Security
[ ] IRSA configured for pod IAM roles
[ ] Pod Security Standards enforced
[ ] Network Policies defined
[ ] Secrets in AWS Secrets Manager (not K8s secrets)
[ ] ECR image scanning enabled
[ ] Security groups properly configured
[ ] IAM least privilege for all roles
[ ] Encryption at rest for EBS volumes
[ ] Encryption in transit (TLS everywhere)
Observability
[ ] CloudWatch Container Insights installed
[ ] Logging to CloudWatch configured
[ ] Metrics collection working
[ ] Distributed tracing (X-Ray) configured
[ ] Dashboards created for key metrics
[ ] Alerts configured for critical issues
[ ] On-call rotation defined
High Availability
[ ] Multiple replicas for all services
[ ] Pod Disruption Budgets defined
[ ] Liveness/readiness probes on all pods
[ ] Resource requests/limits set
[ ] Anti-affinity rules for spreading pods
[ ] Health checks on load balancers
[ ] Graceful shutdown configured
Disaster Recovery
[ ] etcd backups automated (handled by EKS)
[ ] PV snapshots scheduled
[ ] Gitops repo backed up
[ ] Disaster recovery plan documented
[ ] Recovery tested at least once
[ ] RTO/RPO defined and achievable
Operations
[ ] GitOps workflow implemented
[ ] CI/CD pipeline automated
[ ] Rollback procedure tested
[ ] Runbooks for common issues
[ ] Access control (RBAC) configured
[ ] Audit logging enabled
[ ] Change management process defined
Cost Management
[ ] Resource quotas per namespace
[ ] Cost tracking by team/project
[ ] Spot instances for applicable workloads
[ ] Rightsizing reviewed monthly
[ ] Unused resources cleaned up
[ ] Budget alerts configured
Alex:overwhelmed That’s a lot…
Sam: It is! But you don’t need everything day one. Prioritize:
Week 1: Get it running
Basic cluster
Deploy apps
Basic monitoring
Week 2-4: Make it reliable
HA configuration
Proper health checks
Autoscaling
Month 2-3: Make it secure
IRSA
Network policies
Secrets management
Month 3+: Optimize
Cost optimization
Performance tuning
Advanced features
Wrapping Up
Alex:closes laptop Wow. That was… comprehensive. My brain is full.
Sam:laughs I know it’s a lot. Kubernetes and EKS are powerful but complex. Let me leave you with key takeaways:
1. Start Simple
Day 1: Basic EKS cluster with eksctl Day 2: Deploy first app Week 1: Add monitoring Week 2: Add autoscaling Month 1: Implement GitOps Month 2: Advanced features
Enter fullscreen mode
Exit fullscreen mode
2. Use Managed Services
Let AWS manage the control plane
Use managed node groups
Leverage AWS integrations (IAM, ALB, EBS)
3. Embrace Declarative Configuration
Everything in YAML/Git
Let Kubernetes reconcile
Don’t fight the system
4. Focus on Observability
You can’t fix what you can’t see
Logs, metrics, traces
Alert on what matters
5. Security is a Journey
Start with basics (IRSA, network policies)
Add layers over time
Never stop improving
Alex: And if I had to explain EKS to my manager in 30 seconds?
Sam: “EKS is AWS’s managed Kubernetes service. AWS handles the complex control plane, we focus on running our applications. It gives us industry-standard container orchestration, portability, and a massive ecosystem of tools. It’s more complex than ECS but more powerful and portable. Perfect for our growing needs.”
Alex: Perfect. One last question – when should I absolutely NOT use EKS?
Sam: Great question!
Don’t use EKS if:
You have a single, simple application → Use App Runner or ECS
Your team has zero container experience → Start with ECS, migrate later
You need something running TODAY → Kubernetes has a learning curve
Budget is extremely tight → Control plane costs $73/month minimum
You don’t need Kubernetes features → Simpler tools exist
DO use EKS if:
You need Kubernetes for multi-cloud
You have Kubernetes expertise
You need advanced orchestration features
You’re building a platform for multiple teams
You want the K8s ecosystem tools
Alex:stands up Alright, I’m ready. Time to create my first EKS cluster!
Sam: That’s the spirit! Remember:
# Your first cluster eksctl create cluster \ --name my-first-cluster \ --region us-east-1 \ --nodegroup-name workers \ --node-type t3.medium \ --nodes 2 \ --managed # Deploy something kubectl create deployment nginx --image=nginx kubectl expose deployment nginx --port=80 --type=LoadBalancer # Check it kubectl get all
Enter fullscreen mode
Exit fullscreen mode
Alex: And when it inevitably breaks?
Sam:hands over card Text me. Or check the logs, describe the pods, and check events. 90% of issues are:
Wrong image name
Missing permissions
Resource constraints
Configuration errors
Alex:shakes hand Thanks, Sam. Same time next week to discuss service meshes?
Sam:grins Let’s start with getting this working first!
Alex walks out of the coffee shop, laptop bag over shoulder, ready to build something amazing. Sam orders another coffee and opens a laptop – time to help the next person on their Kubernetes journey.
The Beginning of Your EKS Adventure 🚀☸️
For further actions, you may consider blocking this person and/or reporting abuse
Залишити відповідь