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."