Kenneth Au

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.medium is 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.