Deploy an Application on EKS with ALB and Autoscaling

EKS Application Deployment Guide


Prerequisites

  • AWS CLI configured with appropriate permissions
  • eksctl, kubectl, docker, helm installed
  • Basic familiarity with YAML and Kubernetes concepts

1. ECR Setup & Docker Image Push

export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
export REGION=ap-south-1

# Create ECR repository
aws ecr create-repository --repository-name myapp --region $REGION

# Authenticate Docker to ECR
aws ecr get-login-password --region $REGION | \\
  docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com

# Tag & push image
docker tag myapp:latest $AWS_ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com/myapp:latest
docker push $AWS_ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com/myapp:latest

2. Create EKS Cluster

eksctl create cluster \\
  --name my-cluster \\
  --region $REGION \\
  --nodegroup-name workers \\
  --node-type t3.medium \\
  --nodes 2 \\
  --managed

AWS manages: Control plane, API server, etcd

You manage: Worker nodes, pods, deployments, services


3. Deploy Application

deployment.yaml

(Note: Added resources block. HPA requires CPU/Memory requests to function.)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  replicas: 2
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
        - name: myapp
          image: $AWS_ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com/myapp:latest
          ports:
            - containerPort: 8080
          resources:
            requests:
              cpu: "100m"
              memory: "128Mi"
            limits:
              cpu: "500m"
              memory: "256Mi"
kubectl apply -f deployment.yaml
kubectl get pods

4. Autoscaling (Pods & Nodes)

4.1 Horizontal Pod Autoscaler (HPA)

EKS includes metrics-server by default. Create an HPA to scale pods based on CPU utilization.

hpa.yaml

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: myapp-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: myapp
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 50
kubectl apply -f hpa.yaml
kubectl get hpa

Test: kubectl run load-generator --image=busybox -- /bin/sh -c "while true; do wget -q -O- <http://myapp-service>; done"

4.2 Cluster Node Autoscaler

EKS managed node groups do not autoscale out-of-the-box. You must deploy the Cluster Autoscaler (CA) or use Karpenter (AWS recommended). Here is the standard CA setup:

Step A: Tag Node Group for Auto-Discovery

CA requires specific tags to manage the node group.

eksctl create nodegroup \\
  --cluster my-cluster \\
  --name workers-autoscale \\
  --node-type t3.medium \\
  --nodes-min 2 \\
  --nodes-max 10 \\
  --tags k8s.io/cluster-autoscaler/enabled=true,k8s.io/cluster-autoscaler/my-cluster=owned

(If updating an existing node group, use aws eks update-nodegroup-config to add tags.)

Step B: Attach IAM Policy

AWS provides a managed policy for CA:

export CA_POLICY_ARN="arn:aws:iam::aws:policy/AWSClusterAutoscalerPolicy"

eksctl create iamserviceaccount \\
  --cluster my-cluster \\
  --namespace kube-system \\
  --name cluster-autoscaler \\
  --attach-policy-arn $CA_POLICY_ARN \\
  --override-existing-serviceaccounts \\
  --approve

Step C: Install via Helm

helm repo add autoscaler <https://kubernetes.github.io/autoscaler>
helm repo update

helm install cluster-autoscaler autoscaler/cluster-autoscaler \\
  -n kube-system \\
  --set autoDiscovery.clusterName=my-cluster \\
  --set awsRegion=$REGION \\
  --set extraArgs.balance-similar-node-groups=true \\
  --set extraArgs.skip-nodes-with-system-pods=false

Verify:

kubectl get pods -n kube-system | grep cluster-autoscaler

5. AWS Load Balancer Controller (ALB) Setup

5.1 Enable OIDC Provider

Required for IAM Roles for Service Accounts (IRSA).

eksctl utils associate-iam-oidc-provider \\
  --region $REGION \\
  --cluster my-cluster \\
  --approve

5.2 Create IAM Policy

curl -O <https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/main/docs/install/iam_policy.json>
aws iam create-policy \\
  --policy-name AWSLoadBalancerControllerIAMPolicy \\
  --policy-document file://iam_policy.json

5.3 Create IAM Service Account (IRSA)

export ALB_POLICY_ARN=$(aws iam list-policies \\
  --query 'Policies[?PolicyName==`AWSLoadBalancerControllerIAMPolicy`].Arn' \\
  --output text)

eksctl create iamserviceaccount \\
  --cluster my-cluster \\
  --namespace kube-system \\
  --name aws-load-balancer-controller \\
  --attach-policy-arn $ALB_POLICY_ARN \\
  --override-existing-serviceaccounts \\
  --approve

5.4 Install Controller via Helm

export VPC_ID=$(aws eks describe-cluster \\
  --name my-cluster \\
  --query "cluster.resourcesVpcConfig.vpcId" \\
  --output text)

helm repo add eks <https://aws.github.io/eks-charts>
helm repo update

helm install aws-load-balancer-controller eks/aws-load-balancer-controller \\
  -n kube-system \\
  --set clusterName=my-cluster \\
  --set serviceAccount.create=false \\
  --set serviceAccount.name=aws-load-balancer-controller \\
  --set region=$REGION \\
  --set vpcId=$VPC_ID

Verify:

kubectl get pods -n kube-system | grep aws-load-balancer-controller

6. Service & Ingress Configuration

6.1 Internal Service (ClusterIP)

ALB handles public routing; services must remain internal. service.yaml

apiVersion: v1
kind: Service
metadata:
  name: myapp-service
spec:
  selector:
    app: myapp
  ports:
    - port: 80
      targetPort: 8080
  type: ClusterIP

6.2 Ingress (ingress.yaml)

Triggers ALB creation via the controller.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myapp-ingress
  annotations:
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
spec:
  ingressClassName: alb
  rules:
    - http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: myapp-service
                port:
                  number: 80

Apply:

kubectl apply -f service.yaml
kubectl apply -f ingress.yaml
kubectl get ingress myapp-ingress

7. Traffic Flow & Verification

Internet → ALB (Created by Ingress) → Ingress Controller → ClusterIP Service → Pods (Scaled by HPA)
                                                                         ↑
                                                                 Nodes (Scaled by CA)
  • Wait 2–3 minutes for ALB provisioning.
  • kubectl get ingress will show an ADDRESS (ALB DNS endpoint).
  • Test access: curl http://<ALB_DNS_ENDPOINT>
  • Monitor scaling: kubectl get hpa and kubectl get nodes