27.5 AWS Load Balancer Controller: ALB and NLB from Kubernetes Ingress and Service
Alright, let’s talk about getting traffic into your EKS cluster. You’ve got your pods running, your services defined, and now you need the outside world to actually see them. You could manually create an Application Load Balancer (ALB) or Network Load Balancer (NLB) in the AWS console every time you need one, but that would be tedious, error-prone, and frankly, a betrayal of the entire GitOps, declarative ethos we’re living in. Enter the AWS Load Balancer Controller (ALB Controller, for short—its name is a bit of a mouthful, as it handles both ALBs and NLBs).
This brilliant piece of software is the official, AWS-supported way to bridge the gap between the ethereal world of Kubernetes objects and the concrete reality of AWS EC2 Load Balancers. Think of it as a constantly running, highly attentive DevOps engineer who watches your Ingress and Service resources and says, “Ah, I see what you want. Let me just aws elbv2 create-load-balancer that for you.”
Why This, Not That: Ingress Controller vs. Service Type=LoadBalancer
You might be wondering why we need a whole new controller. Can’t we just use the old kubernetes.io/ingress-class annotation or set a Service type to LoadBalancer? Well, you could, but you’d be using the in-tree cloud provider code that’s been deprecated and ripped out of the main Kubernetes project for a reason. It was clunky, limited, and frankly, a bit of a nightmare to maintain.
The AWS Load Balancer Controller is its modern, out-of-tree replacement. It’s more feature-rich, actively developed, and understands a whole new set of annotations that give you fine-grained control over the ALBs and NLBs it creates. The old way is like using a dial-up modem; this is like fiber optic. Don’t be the person still using dial-up.
Installing the Darn Thing: IAM and All That Jazz
First, the non-negotiable prerequisite: IAM permissions. The controller needs serious power to create and manage ALBs, NLBs, target groups, security groups, and more. You’ll need to attach an IAM policy to its service account. The AWS documentation provides the JSON for this policy, and it’s… extensive. Just copy it. Don’t try to be a hero and craft a minimal one yourself; you’ll just end up debugging mysterious Failed create load balancer errors at 2 AM.
Here’s the CliffsNotes version using eksctl. This is the way.
# alb-iam-service-account.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
name: my-awesome-cluster
region: us-west-2
iam:
withOIDC: true
iamServiceAccounts:
- metadata:
name: aws-load-balancer-controller
namespace: kube-system
attachPolicyARNs:
- arn:aws:iam::aws:policy/AWSLoadBalancerControllerIAMPolicy
Apply it with eksctl create iamserviceaccount -f alb-iam-service-account.yaml, then install the Helm chart.
helm repo add eks https://aws.github.io/eks-charts
helm upgrade -i aws-load-balancer-controller eks/aws-load-balancer-controller \
-n kube-system \
--set clusterName=my-awesome-cluster \
--set serviceAccount.create=false \
--set serviceAccount.name=aws-load-balancer-controller
Pro tip: Always check the logs after installation. Run kubectl logs -n kube-system deployment/aws-load-balancer-controller and make sure it cheerfully says something about starting the controller. If it screams about IAM permissions, well, I told you so.
The Magic Trick: Creating an ALB via Ingress
This is where the rubber meets the road. You define what you want in a familiar Kubernetes Ingress manifest, and the controller does the heavy lifting.
Let’s create a simple ingress for a web application.
# basic-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app-ingress
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
spec:
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-web-app-service
port:
number: 80
Apply this, and then watch the magic happen. Run kubectl get ingress my-app-ingress and watch the ADDRESS field. After a minute or two, it will populate with the DNS name of your brand-new AWS ALB.
Let’s break down those annotations:
kubernetes.io/ingress.class: alb: This is the secret handshake. It tells the AWS Load Balancer Controller, “This one’s for you, buddy. Handle it.”alb.ingress.kubernetes.io/scheme: internet-facing: Pretty self-explanatory. Useinternalif you want a private ALB for VPC-internal traffic.alb.ingress.kubernetes.io/target-type: ip: This is crucial. This routes traffic directly to the Pod IPs. The alternative isinstance, which routes to the underlying EC2 instance’s nodePort. You wantip. It’s more efficient, works seamlessly with Windows nodes, and is required for Fargate. The only reason to useinstanceis if you’re running a very specific network setup that requires it.
When You Need Raw Power: Using a Network Load Balancer (NLB)
Sometimes you need speed, not features. ALBs are great for HTTP/S, but if you need ultra-low latency, UDP support, or a static IP address, you need a Network Load Balancer. The controller handles this beautifully using a Service of type LoadBalancer with a special annotation.
# nlb-service.yaml
apiVersion: v1
kind: Service
metadata:
name: my-network-service
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
spec:
selector:
app: my-network-app
ports:
- port: 80
targetPort: 8080
type: LoadBalancer
Note the annotation: service.beta.kubernetes.io/aws-load-balancer-type: "nlb". This is what flips the switch from the old deprecated logic to the new controller. Without it, you’ll get a Classic Load Balancer, which is AWS’s embarrassing old product we don’t talk about at parties.
The Gotchas: Because Nothing is Ever Easy
- Idle Timeouts: ALBs have a default idle timeout of 60 seconds. If you have long-running connections (like WebSockets), they will be unceremoniously dropped. Set
alb.ingress.kubernetes.io/idle-timeout: "300"on your Ingress to fix this. - Deletion Chaos: Delete an Ingress resource and the controller will dutifully delete the ALB and all its related AWS resources. This is good. Unless you accidentally delete it. There’s no “are you sure?” prompt. Be careful with your
kubectl deletecommands. - Security Groups: The controller creates a security group for the ALB by default. It’s permissive by nature (allowing traffic on the ports you define). Your worker node security groups must allow traffic from this ALB security group on the
nodePortrange (if usinginstancemode) or all ports (if usingipmode). The controller tries to manage this for you, but in complex setups, this can be a common source of “I can’t reach my service” issues. - Tags: AWS has limits on the number of tags on resources. The controller tags everything it creates. If you’re already tag-heavy, you might hit a wall. It’s a rare one, but it bites you when you least expect it.
The AWS Load Balancer Controller is one of those tools that, once you get it configured correctly, you simply stop thinking about. It just works. And in the messy world of Kubernetes networking, that’s the highest compliment I can give.