interview-prep

Crisp answer: The AWS Load Balancer Controller is a Kubernetes controller that watches Ingress and Service objects and provisions AWS ALBs and NLBs in response. It replaces the in-tree cloud provider for load balancer provisioning and provides much richer configuration via annotations.

What it does:

Kubernetes Ingress object → AWS ALB (Application Load Balancer)
Kubernetes Service type=LoadBalancer → AWS NLB (Network Load Balancer)

Installation:

# Install via Helm
helm repo add eks https://aws.github.io/eks-charts
helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
  -n kube-system \
  --set clusterName=my-cluster \
  --set serviceAccount.create=true \
  --set serviceAccount.annotations."eks\.amazonaws\.com/role-arn"=arn:aws:iam::123:role/lb-controller-role

Ingress example (ALB):

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: api-ingress
  namespace: production
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing      # or internal
    alb.ingress.kubernetes.io/target-type: ip              # ip or instance
    alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:eu-west-2:123:certificate/xxx
    alb.ingress.kubernetes.io/ssl-redirect: "443"
    alb.ingress.kubernetes.io/healthcheck-path: /healthz
    alb.ingress.kubernetes.io/group.name: shared-alb       # share ALB across Ingresses
spec:
  rules:
  - host: api.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: api
            port:
              number: 80

target-type: ip vs instance:

instance mode: ALB routes to NodePort on each node
               → extra hop: ALB → node → pod
               → does not require VPC CNI pods

ip mode:       ALB routes directly to pod IPs
               → requires AWS VPC CNI (pods have VPC IPs)
               → lower latency, preserves client source IP
               → recommended for EKS with VPC CNI

Sharing an ALB across multiple Ingresses:

# Each Ingress in the same group shares one ALB
annotations:
  alb.ingress.kubernetes.io/group.name: production-shared
  alb.ingress.kubernetes.io/group.order: "10"  # rule priority within the group

This reduces ALB costs significantly in clusters with many services.

NLB for non-HTTP (Service type=LoadBalancer):

apiVersion: v1
kind: Service
metadata:
  name: grpc-api
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: "external"
    service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "ip"
    service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
spec:
  type: LoadBalancer
  ports:
  - port: 443
    targetPort: 8443
    protocol: TCP

Debugging the Load Balancer Controller:

# Check controller logs
kubectl logs -n kube-system -l app.kubernetes.io/name=aws-load-balancer-controller

# Check Ingress events
kubectl describe ingress api-ingress -n production

# Common errors:
# "subnet not tagged correctly" → tag subnets:
#   kubernetes.io/role/internal-elb = 1 (private subnets)
#   kubernetes.io/role/elb = 1 (public subnets)

# Check if the ALB was created in AWS:
aws elbv2 describe-load-balancers --region eu-west-2 | grep -A 5 "k8s-production"

Required subnet tags:

# Public subnets (for internet-facing ALB):
aws ec2 create-tags --resources subnet-xxx \
  --tags Key=kubernetes.io/role/elb,Value=1

# Private subnets (for internal ALB):
aws ec2 create-tags --resources subnet-xxx \
  --tags Key=kubernetes.io/role/internal-elb,Value=1

# Both types need the cluster tag:
aws ec2 create-tags --resources subnet-xxx \
  --tags Key=kubernetes.io/cluster/my-cluster,Value=shared

What to say in the interview:

"The AWS Load Balancer Controller replaces the built-in cloud provider for load balancer provisioning. An Ingress object creates an ALB, a LoadBalancer Service creates an NLB. The key annotation choices are scheme (internet-facing or internal) and target-type (ip or instance). I always use ip mode on EKS with VPC CNI because it routes directly to pod IPs, avoids the extra NodePort hop, and preserves the client source IP. The group.name annotation is important in practice — without it, every Ingress creates a new ALB which gets expensive quickly. With a shared group, all Ingresses in the group share one ALB with separate listener rules."


My notes