Ravindra BagaleCourses & study guides मराठी

Chapter 62: Kubernetes Services, Networking and Load Balancing: CNI, ClusterIP, NodePort, LoadBalancer, DNS and NetworkPolicy

62.4 LoadBalancer Services: MetalLB on kind and the AWS Load Balancer Controller

Why: the problem

NodePort made the storefront reachable, but testers had to know node IPs and a strange port. Real users of an app like Swiggy want one stable address on port 80 or 443. A LoadBalancer Service gives exactly that: Kubernetes asks someone outside the cluster to create a load balancer and puts its address in the Service's EXTERNAL-IP column. Who is that someone? On a cloud, it is a controller. On EKS today the recommended one is the AWS Load Balancer Controller, which creates a Network Load Balancer (NLB) for a Service of type LoadBalancer, and an Application Load Balancer (ALB) for an Ingress. On bare metal or kind nobody answers, so EXTERNAL-IP stays forever. MetalLB fills that gap: it hands out IPs from a pool you give it and announces them on the local network. Under the hood a LoadBalancer Service is still a NodePort plus a ClusterIP. The load balancer either sends traffic to the node ports (instance mode) or, with the AWS controller in ip mode, straight to pod IPs. Each LoadBalancer Service usually means one cloud load balancer and one bill, which is why many teams expose only an Ingress controller this way and route many apps behind it. In this lesson we see , install MetalLB on kind, get a real external IP, and then read an EKS NLB manifest with the right annotations.

What: every term in plain English

  • type: LoadBalancer — Asks an external controller for a load balancer.
  • EXTERNAL-IP — The address the controller assigned; until then.
  • MetalLB — A load balancer implementation for bare metal and kind.
  • IPAddressPool — MetalLB resource listing IPs it may hand out.
  • L2Advertisement — Tells MetalLB to answer ARP for those IPs on the local network.
  • AWS Load Balancer Controller — Creates NLBs for Services and ALBs for Ingresses on EKS.
  • NLB — AWS Network Load Balancer, layer 4 (TCP/UDP).
  • Target type ip vs instance — ip sends straight to pod IPs; instance goes through node ports.
  • loadBalancerClass — Chooses which controller handles the Service.
  • loadBalancerSourceRanges — Limits which client CIDRs may connect.
Change store to type LoadBalancer. Install MetalLB. Give MetalLB an IP pool from the kind network. Watch EXTERNAL-IP appear and curl it. Read the EKS NLB manifest. Limit sources with loadBalancerSourceRanges. Clean up.
LoadBalancer: an external IP from MetalLB on kind or an NLB on EKS

How: step by step

  1. Change store to type LoadBalancer. See .
  2. Install MetalLB. Native manifests.
  3. Give MetalLB an IP pool from the kind network. IPAddressPool and L2Advertisement.
  4. Watch EXTERNAL-IP appear and curl it. From a container on the kind network.
  5. Read the EKS NLB manifest. Annotations for the AWS controller.
  6. Limit sources with loadBalancerSourceRanges. Basic protection.
  7. Clean up. Keep MetalLB for later lessons.

Example 1: See why EXTERNAL-IP stays on kind

Goal. See why EXTERNAL-IP stays on kind.

store-lb.yaml

apiVersion: v1
kind: Service
metadata:
  name: store-lb
  namespace: net
spec:
  type: LoadBalancer
  selector:
    app: store
  ports:
    - name: http
      port: 80
      targetPort: http

What each line does

  • type: LoadBalancer — Requests an external load balancer.
  • No nodePort set — Kubernetes still allocates one automatically.
  • <pending> — No controller in kind fulfils the request.
  • Still usable — ClusterIP and NodePort work meanwhile.
  • Events — Nothing is logged, because nobody is watching.

Green output

$ kubectl apply -f store-lb.yaml
$ kubectl get svc -n net store-lb
NAME       TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)
store-lb   LoadBalancer   10.96.183.77    <pending>     80:31402/TCP
$ sleep 60; kubectl get svc -n net store-lb -o jsonpath='{.status.loadBalancer}{"\n"}'
{}

Expected result. After a minute the status is still empty. The Service is valid; there is simply no load balancer provider.

Example 2: Install MetalLB and get a real external IP

Goal. Install MetalLB and get a real external IP.

metallb-pool.yaml

apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: kind-pool
  namespace: metallb-system
spec:
  addresses:
    - 172.18.255.200-172.18.255.250
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: kind-l2
  namespace: metallb-system
spec:
  ipAddressPools:
    - kind-pool

What each line does

  • Pool range — Taken from the top of the kind Docker network (docker network inspect kind), away from node IPs.
  • L2Advertisement — Speakers answer ARP for these IPs, so other machines on the network find them.
  • controller — MetalLB pod that assigns IPs.
  • speaker — DaemonSet pod on each node that announces the IP.
  • Result — EXTERNAL-IP is filled in within seconds.

Green output

$ docker network inspect kind -f '{{(index .IPAM.Config 0).Subnet}}'
172.18.0.0/16
$ kubectl apply -f metallb-native-v0.14.8.yaml
$ kubectl wait -n metallb-system --for=condition=Ready pod -l app=metallb --timeout=120s
pod/controller-5f9b8d9c7-xk2lp condition met
pod/speaker-7wq4m condition met
$ kubectl apply -f metallb-pool.yaml
$ kubectl get svc -n net store-lb
NAME       TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)
store-lb   LoadBalancer   10.96.183.77   172.18.255.200   80:31402/TCP
$ docker run --rm --network kind curlimages/curl:8.10.1 -sI http://172.18.255.200 | head -1
HTTP/1.1 200 OK

Expected result. The same Service now has an external IP on port 80, and a client on the kind network reaches it without knowing any node IP.

Example 3: Read an EKS NLB Service for the AWS Load Balancer Controller

Goal. Read an EKS NLB Service for the AWS Load Balancer Controller.

store-nlb.yaml

apiVersion: v1
kind: Service
metadata:
  name: store-nlb
  namespace: net
  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
    service.beta.kubernetes.io/aws-load-balancer-healthcheck-path: /
spec:
  type: LoadBalancer
  loadBalancerClass: service.k8s.aws/nlb
  loadBalancerSourceRanges:
    - 203.0.113.0/24
  selector:
    app: store
  ports:
    - name: http
      port: 80
      targetPort: http

What each line does

  • aws-load-balancer-type: external — Hands the Service to the AWS Load Balancer Controller.
  • nlb-target-type: ip — NLB targets pod IPs directly; no extra node hop.
  • scheme: internet-facing — Public NLB; use internal for private ones.
  • loadBalancerClass — Makes sure only the AWS controller handles it.
  • loadBalancerSourceRanges — Only the office range 203.0.113.0/24 may connect.

Green output

$ kubectl apply -f store-nlb.yaml
$ kubectl get svc -n net store-nlb
NAME        TYPE           EXTERNAL-IP                                                 PORT(S)
store-nlb   LoadBalancer   k8s-net-storenlb-1a2b3c4d5e-0123456789.elb.ap-south-1.amazonaws.com   80:30612/TCP
$ kubectl describe svc -n net store-nlb | grep -A2 Events
  Normal  SuccessfullyReconciled  45s  service  Successfully reconciled

Expected result. On EKS the EXTERNAL-IP is a DNS name of the NLB. It can take a few minutes before the name resolves and targets become healthy. This one needs a real EKS cluster with the controller installed.

Every command, step by step (kind lab with kubectl)

  1. Create store-lb

    kubectl apply -f store-lb.yaml
    

    What it does: LoadBalancer type.

    service/store-lb created
    
  2. See pending

    kubectl get svc -n net store-lb
    

    What it does: No provider yet.

    store-lb   LoadBalancer   10.96.183.77   <pending>   80:31402/TCP
    
  3. Find the kind subnet

    docker network inspect kind -f '{{(index .IPAM.Config 0).Subnet}}'
    

    What it does: Pick a pool from its top.

    172.18.0.0/16
    
  4. Install MetalLB

    kubectl apply -f metallb-native-v0.14.8.yaml
    

    What it does: Pinned version.

    namespace/metallb-system created
    
  5. Wait

    kubectl wait -n metallb-system --for=condition=Ready pod -l app=metallb --timeout=120s
    

    What it does: Controller and speakers.

    pod/controller-5f9b8d9c7-xk2lp condition met
    
  6. Add the pool

    kubectl apply -f metallb-pool.yaml
    

    What it does: IPAddressPool plus L2Advertisement.

    ipaddresspool.metallb.io/kind-pool created
    l2advertisement.metallb.io/kind-l2 created
    
  7. Read the IP

    kubectl get svc -n net store-lb -o jsonpath='{.status.loadBalancer.ingress[0].ip}{"\n"}'
    

    What it does: Assigned from the pool.

    172.18.255.200
    
  8. Curl it

    docker run --rm --network kind curlimages/curl:8.10.1 -sI http://172.18.255.200 | head -1
    

    What it does: Port 80, no node IP.

    HTTP/1.1 200 OK
    
  9. Request a fixed IP

    kubectl annotate svc -n net store-lb metallb.universe.tf/loadBalancerIPs=172.18.255.210 --overwrite
    

    What it does: Pins the address.

    service/store-lb annotated
    
  10. MetalLB events

    kubectl describe svc -n net store-lb | tail -3
    

    What it does: IPAllocated and nodeAssigned.

    Normal  IPAllocated   5s  metallb-controller  Assigned IP ["172.18.255.210"]
    
  11. Clean up

    kubectl delete svc -n net store-lb
    

    What it does: Keep MetalLB installed.

    service "store-lb" deleted
    

When it is red 1

EXTERNAL-IP <pending> forever

Cause. No load balancer controller, or MetalLB has no pool.

Fix, step by step

  1. Install MetalLB or the AWS controller; check IPAddressPool.

When it is red 2

Failed to allocate IP for "net/store-lb": no available IPs

Cause. Pool is empty or too small.

Fix, step by step

  1. Add more addresses or free unused Services.

When it is red 3

Failed build model due to unable to resolve at least one subnet

Cause. AWS controller cannot find tagged subnets.

Fix, step by step

  1. Tag public subnets kubernetes.io/role/elb=1 and private ones kubernetes.io/role/internal-elb=1.

When it is red 4

NLB targets unhealthy

Cause. Security group blocks the NLB or the health check path fails.

Fix, step by step

  1. Allow traffic from the NLB, fix healthcheck-path.

Practice tasks

Easy Task 1: Pending

Why: See.

What to do: Create a LoadBalancer on bare kind.

How:
  1. <pending>.
Solution
kubectl get svc -n net store-lb

Easy Task 2: MetalLB

Why: Install.

What to do: Install MetalLB.

How:
  1. Native manifest.
Solution
kubectl get pods -n metallb-system

Easy Task 3: Pool

Why: Configure.

What to do: Add a pool.

How:
  1. IPAddressPool.
Solution
kubectl get ipaddresspool -A

Medium Task 4: Curl

Why: Test.

What to do: Curl the external IP.

How:
  1. kind network.
Solution
docker run --rm --network kind curlimages/curl:8.10.1 -sI http://172.18.255.200

Medium Task 5: Fixed IP

Why: Pin.

What to do: Pin an IP.

How:
  1. Annotation.
Solution
kubectl describe svc -n net store-lb

Medium Task 6: Sources

Why: Restrict.

What to do: Add loadBalancerSourceRanges.

How:
  1. CIDR list.
Solution
kubectl get svc -n net store-lb -o yaml

Medium Task 7: Speaker

Why: Read.

What to do: Read speaker logs.

How:
  1. app=metallb.
Solution
kubectl logs -n metallb-system -l component=speaker --tail=5

Medium Task 8: NLB

Why: Read.

What to do: Explain each annotation.

How:
  1. Controller docs.
Solution
kubectl explain service.spec.loadBalancerClass

Hard Task 9: Internal

Why: Main task.

What to do: Write the internal NLB version.

How:
  1. scheme internal.
Solution
kubectl apply --dry-run=client -f store-nlb.yaml

Hard Task 10: Cost

Why: Design.

What to do: Plan one Ingress LB for many apps.

How:
  1. 62 -> Ingress chapter.
Solution
kubectl get svc -A | grep LoadBalancer

Homework: try at home

For Swiggy's staging on kind, expose the storefront with a MetalLB IP fixed at 172.18.255.210, limited to the kind network with loadBalancerSourceRanges, and write the EKS NLB version of the same Service for an internal (not public) load balancer.

Recap

  • type: LoadBalancer asks a controller for an external load balancer.
  • Without one, EXTERNAL-IP stays .
  • MetalLB gives kind and bare metal real IPs from a pool.
  • On EKS, the AWS Load Balancer Controller creates an NLB; ip targets skip node ports.
  • Next: ExternalName and headless Services.

FAQ

Q. What is BGP mode in MetalLB?

Instead of answering ARP, speakers announce the IPs to your routers over BGP, which spreads traffic across nodes and fails over faster. It needs router support.

Q. What happens if the MetalLB speaker node dies in L2 mode?

Another speaker takes over the IP and sends a new ARP announcement. Clients see a short pause of a few seconds.

Q. How much does each NLB cost?

Each NLB has an hourly charge plus capacity units. Ten Services with ten NLBs cost ten times one Ingress setup, which is why teams consolidate.

Q. Does deleting the Service delete the NLB?

Yes, the controller removes it. Be careful in production: the DNS name changes when it is recreated.

Q. Where do I get metallb-native-v0.14.8.yaml?

From the Installation by manifest page of the official MetalLB docs. Save the file, read it, pin the version, and keep it in your repo so installs are repeatable.

Q. Can two Services share one MetalLB IP?

Yes, with the metallb.universe.tf/allow-shared-ip annotation, if their ports do not clash.

Q. Is MetalLB used in production?

Yes, on bare-metal clusters, often in BGP mode.

Q. Why is the EKS EXTERNAL-IP a hostname?

AWS load balancers are addressed by DNS name; IPs can change.

Q. What is the in-tree AWS provider?

The old cloud provider creates Classic or NLB load balancers; the AWS Load Balancer Controller is the recommended way now.

Q. Can I turn off the node port?

Yes, allocateLoadBalancerNodePorts: false, when the LB targets pod IPs.

Q. Does sourceRanges work with MetalLB?

Yes, kube-proxy enforces it.

Q. How do I get HTTPS?

Add a TLS certificate on the NLB, or terminate TLS in an Ingress controller.

Interview questions

  1. What happens when you create a LoadBalancer Service?

    Model answer: A ClusterIP and node port are allocated, then a controller provisions an external LB and writes its address to status.

  2. Why does EXTERNAL-IP stay pending on bare metal?

    Model answer: No cloud controller; you need MetalLB or similar.

  3. Instance vs ip target type?

    Model answer: Instance sends to node ports on nodes; ip sends directly to pod IPs.

  4. How do you limit who can reach a LoadBalancer?

    Model answer: loadBalancerSourceRanges, security groups, or an internal scheme.

  5. Why put many apps behind one Ingress?

    Model answer: One load balancer costs less and centralises TLS and routing.

  6. How would you debug a LoadBalancer Service stuck in pending on EKS?

    Model answer: Check that the AWS Load Balancer Controller pods run, read their logs, confirm the IAM role, check subnet tags, and read the Service events for reconcile errors.

Quick quiz

Q1. What does <pending> mean?

No controller has created a load balancer.

Q2. What does MetalLB need?

An IP pool and an advertisement.

Q3. NLB target type ip means?

Traffic goes straight to pod IPs.

Q4. Does a LoadBalancer Service have a node port?

Yes, by default.

Q5. Why not one LB per app?

Cost; use an Ingress controller instead.

Samajla ka?