Deploying to Kubernetes on AWS: A Walkthrough
Deploying containers to Kubernetes on AWS using EKS (Elastic Kubernetes Service) involves several moving parts: the cluster itself, node groups, networking, IAM roles, and your application manifests. This walkthrough covers the essentials.
Prerequisites
# Install tools
brew install kubectl eksctl helm
# Configure AWS credentials
aws configure
Creating the Cluster
eksctl is the easiest way to create an EKS cluster:
# cluster.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
name: myapp-cluster
region: us-east-1
managedNodeGroups:
- name: workers
instanceType: t3.medium
desiredCapacity: 3
minSize: 2
maxSize: 5
iam:
withAddonPolicies:
autoScaler: true
ebs: true
efs: true
Create the cluster:
eksctl create cluster -f cluster.yaml
This takes about 15-20 minutes. It creates a VPC, subnets, security groups, IAM roles, the control plane, and your worker nodes.
Verify the Cluster
# Check nodes
kubectl get nodes
# Check system pods
kubectl get pods -n kube-system
# Update kubeconfig
aws eks update-kubeconfig --name myapp-cluster --region us-east-1
Deploying Your Application
Container Image
Your application needs to be in a container registry. AWS ECR (Elastic Container Registry) works well:
# Create repository
aws ecr create-repository --repository-name myapp-api
# Build and push
docker build -t myapp-api .
docker tag myapp-api:latest 123456789.dkr.ecr.us-east-1.amazonaws.com/myapp-api:latest
docker push 123456789.dkr.ecr.us-east-1.amazonaws.com/myapp-api:latest
Kubernetes Manifests
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
labels:
app: api
spec:
replicas: 3
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: 123456789.dkr.ecr.us-east-1.amazonaws.com/myapp-api:latest
ports:
- containerPort: 3000
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: api-secrets
key: database-url
- name: NODE_ENV
value: 'production'
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
# service.yaml
apiVersion: v1
kind: Service
metadata:
name: api-service
spec:
selector:
app: api
ports:
- port: 80
targetPort: 3000
type: ClusterIP
# secret.yaml (base64 encode your values)
apiVersion: v1
kind: Secret
metadata:
name: api-secrets
type: Opaque
data:
database-url: <base64-encoded-value>
Apply the Manifests
kubectl apply -f secret.yaml
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
# Check deployment status
kubectl rollout status deployment/api
kubectl get pods -l app=api
Exposing to the Internet
Option 1: AWS Load Balancer Controller
Install the AWS Load Balancer Controller with Helm:
helm repo add eks-charts https://aws.github.io/eks-charts
helm install aws-load-balancer-controller eks-charts/aws-load-balancer-controller \
-n kube-system \
--set clusterName=myapp-cluster
Then create an Ingress:
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-ingress
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
spec:
ingressClassName: alb
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80
This creates an AWS Application Load Balancer automatically.
Option 2: NGINX Ingress Controller
helm install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx \
--create-namespace
Database Considerations
Don’t run your database in Kubernetes. Use a managed service:
# Create an RDS PostgreSQL instance
aws rds create-db-instance \
--db-instance-identifier myapp-db \
--db-instance-class db.t3.micro \
--engine postgres \
--master-username myapp \
--master-user-password <password> \
--allocated-storage 20
Connect your application to RDS through the VPC. The EKS cluster and RDS instance should be in the same VPC, and security groups should allow traffic from the Kubernetes node security group to the database on port 5432.
Autoscaling
Horizontal Pod Autoscaler
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
Cluster Autoscaler
The node group in cluster.yaml already has auto-scaling enabled (minSize: 2, maxSize: 5). The cluster autoscaler adjusts the number of nodes based on pending pods.
Monitoring
Install Prometheus and Grafana:
helm install prometheus prometheus-community/kube-prometheus-stack \
--namespace monitoring \
--create-namespace
This gives you:
- Prometheus: Metrics collection
- Grafana: Dashboards and visualization
- Alertmanager: Alert routing
Cleanup
EKS clusters cost money when idle. Clean up when you’re done:
# Delete resources first
kubectl delete -f ingress.yaml
kubectl delete -f service.yaml
kubectl delete -f deployment.yaml
# Delete the cluster
eksctl delete cluster -f cluster.yaml
Cost Tips
- Use spot instances for non-critical workloads (70-90% cheaper)
- Right-size your instances —
t3.mediumis often enough for development - Set up auto-scaling to scale down during low traffic
- Use AWS Budgets to get alerts when spending exceeds a threshold
EKS abstracts the Kubernetes control plane while giving you full control over your worker nodes and workloads. The initial setup has many moving pieces, but once the infrastructure is in place, deploying new services is just a matter of applying manifests.