skip to content

LoadBalancer Provisioning

A type=LoadBalancer Service does nothing on its own: the cloud-controller-manager asks the provider for a load balancer, shaped by annotations, while MetalLB fills the gap on bare metal. 'Why is EXTERNAL-IP still pending?' comes straight out of this.

part ofKubernetesoverview, primer and where to startread it →
on this pageshow

questions

4

Why does a Kubernetes LoadBalancer Service show EXTERNAL-IP <pending> in kubectl get svc, and which component is supposed to replace it?

level: juniorimportance: must knowfreq 76%

answer

  1. a request, not a resource
  2. status written by someone else
  3. cloud-controller-manager's service controller
  4. loadBalancerClass nobody watches
  5. describe svc events

basics

~20 s

A type=LoadBalancer Service only records a request. The cloud-controller-manager's service controller, or another load-balancer implementation such as MetalLB, must create the balancer and write its address into status. <pending> means nothing has done that yet.

solid answer

~40 s

Setting `type: LoadBalancer` does not create anything outside the cluster. The API server allocates a ClusterIP and NodePorts, and then an implementation must act on the Service. On a cloud cluster that is the **service controller in the cloud-controller-manager**: it asks the provider for a balancer and writes the address into `status.loadBalancer.ingress`, which `kubectl get svc` prints as EXTERNAL-IP. `<pending>` means that field is still empty. The usual causes: - no implementation is installed at all (bare metal, kind, a plain kubeadm cluster) - `spec.loadBalancerClass` names a class nobody watches - provisioning is failing, which shows up as `SyncLoadBalancerFailed` events - the provider is simply still working Check with `kubectl describe svc`. The Service still works inside the cluster through its ClusterIP the whole time.

code

bash · 2 lines
bash
kubectl -n billing-invoices get svc invoice-renderer
kubectl -n billing-invoices describe svc invoice-renderer | sed -n '/Events:/,$p'

go deeper

for a junior

Remember the chain: the Service asks, the cloud-controller-manager's service controller builds the balancer and writes its address. <pending> means that address was never written. Start with kubectl describe svc.

for a middle

Name the causes separately: no implementation, a loadBalancerClass nobody watches, SyncLoadBalancerFailed from the provider, or no eligible nodes. Each looks different in the Service's events.

for a senior

Show you check the platform before the provider: is a CCM or MetalLB running, is the class correct, what do the events say. Mention that ClusterIP and NodePort already work, so in-cluster callers are unaffected.

for a principal

Frame one balancer per Service as a cost and quota decision, and describe how the platform chooses load-balancer implementations and classes so tenants never end up waiting on an address with no explanation.

## What a LoadBalancer Service actually asks for A Kubernetes **Service** gives a stable address to a set of pods. With `spec.type: LoadBalancer`, the Service also asks for an **external load balancer**: something outside the cluster with its own address that forwards traffic in. The request is only data in the API. The **API server** allocates the parts it owns, a **ClusterIP** and (by default) a **NodePort** for each port. Nothing in the core API server talks to a cloud. The external address goes in `status.loadBalancer.ingress`, a list of entries that each carry an `ip` or a `hostname`. `kubectl get svc` prints that list in the **EXTERNAL-IP** column. When the list is empty and the Service has no `spec.externalIPs`, kubectl prints `<pending>`. ## Who is supposed to fill it in - **cloud-controller-manager (CCM)**: the provider-specific control-plane component. One of its loops is the **service controller** (`service-lb-controller`). It watches Services and acts on those with `type: LoadBalancer` and **no** `spec.loadBalancerClass`. - The controller calls the provider's `EnsureLoadBalancer`. When the balancer exists, it patches the Service status with the balancer's IP or hostname. - In current Kubernetes, **kube-controller-manager contains no cloud-provider code**. Cloud integration lives only in an external CCM, and kubelets are started with `--cloud-provider=external`. - **Other implementations** fill the same field on clusters without a cloud: MetalLB on bare metal, the simple built-in balancer that k3s ships, or `minikube tunnel` on a laptop. - **kube-proxy does not create balancers.** Once the address exists, it only programs node rules so that traffic arriving for it reaches pods. ## Why it stays <pending> 1. **No implementation exists.** A kubeadm cluster on bare metal or a kind cluster has no CCM with load-balancer support. The Service waits forever, and no event says why, because nothing is watching it. 2. **`spec.loadBalancerClass` is set** (for example `example.com/internal-vip`). The default implementation deliberately skips classed Services. If no controller watches that class, nothing happens. 3. **Provisioning fails.** The service controller emits a Warning event with reason `SyncLoadBalancerFailed` and the provider's error text (quota, permissions, an invalid annotation value), then retries with backoff. 4. **No eligible nodes.** The event `UnAvailableLoadBalancer` with the message "There are no available nodes for LoadBalancer" means every node was filtered out, for example by the `node.kubernetes.io/exclude-from-external-load-balancers` label. 5. **It is just slow.** Some providers take a minute or more. `EnsuringLoadBalancer` is logged with no `EnsuredLoadBalancer` yet. | What `kubectl describe svc` shows | Likely cause | |---|---| | No load-balancer events at all | No implementation installed, or a `loadBalancerClass` nobody watches | | `SyncLoadBalancerFailed` repeating | Provider rejected the request; read the error text | | `UnAvailableLoadBalancer` | Every node excluded or ineligible | | `EnsuringLoadBalancer`, nothing after | Provider still creating it | ## Diagnosing it Say the PDF-invoice renderer on a 210-node multi-tenant platform cluster has to move to a new public endpoint before the old one's certificate expires in 18 hours, and its Service stays pending: ```bash kubectl -n billing-invoices get svc invoice-renderer kubectl -n billing-invoices describe svc invoice-renderer kubectl -n billing-invoices get svc invoice-renderer -o jsonpath='{.spec.loadBalancerClass}' kubectl -n kube-system get pods | grep -i cloud-controller ``` If the events are empty, check whether a CCM (or MetalLB) is running at all before you blame the provider. ## What still works while it is pending - The **ClusterIP** and the Service's **DNS name** work normally for in-cluster clients. - Unless `allocateLoadBalancerNodePorts: false` is set, the **NodePort** is already open on every node, so `nodeIP:nodePort` works too. - Pod readiness is a separate question: pods that are not Ready leave the Service without backends, but they do not keep EXTERNAL-IP pending. So `<pending>` is a question about **the load-balancer implementation**, not about your pods.

  • On a laptop kind or minikube cluster, how do you get past <pending> for local testing?
    Neither cluster has a cloud controller, so nothing fills in the status. With minikube, run `minikube tunnel`: it creates routes and writes an address into the Service status. With kind, install a local load-balancer implementation, such as MetalLB with a pool taken from the Docker network's range, or a kind-specific cloud-provider helper. For quick checks, `kubectl port-forward` or the NodePort avoids the question entirely.
  • Why does giving every tenant Service its own LoadBalancer get expensive on a 210-node multi-tenant cluster?
    Each LoadBalancer Service normally becomes a separate cloud balancer, usually with its own billed address and hourly charge. Its backend set also tracks the node list, which is large here. Fifty tenant Services mean fifty balancers. HTTP services usually share one balancer in front of an ingress or gateway controller, and dedicated balancers are kept for non-HTTP protocols or hard isolation needs.
  • If a Service shows an EXTERNAL-IP but external clients still cannot connect, is that still a provisioning problem?
    Usually not. An address in status means the implementation did its part. Next, check whether the Service has ready backends, whether the balancer's health checks pass against the nodes, and whether firewall rules or `loadBalancerSourceRanges` block the client. That is connectivity triage, a different job from provisioning.

Filing a Service of type LoadBalancer is like filing a work order. Nothing gets built until a contractor picks it up, and <pending> means no contractor has signed it yet.

saying these in an interview costs you the question

  • kube-proxy creates the cloud load balancer
  • Pending means the pods behind the Service are not ready
  • The Service is unusable until the external IP appears
  • Kubernetes provisions a load balancer on any cluster automatically
  • kube-controller-manager's built-in cloud code handles it in current releases
open as a page

How do you configure the cloud load balancer behind a Kubernetes LoadBalancer Service, for example internal-only, and what does spec.loadBalancerClass change?

level: middleimportance: should knowfreq 46%

basics

~20 s

The Service spec has only a few portable load-balancer fields, so provider-specific settings such as internal-only, balancer flavour or TLS are set with the provider's annotations on the Service. spec.loadBalancerClass hands the Service to a different load-balancer implementation instead of the default one.

open as a page

What does the cloud-controller-manager's service controller do with a Kubernetes type=LoadBalancer Service, and what role do the NodePorts underneath play?

level: middleimportance: should knowfreq 52%

basics

~20 s

The service controller adds a cleanup finalizer, has the provider build a balancer for the eligible nodes, writes the address into status, and updates or deletes the balancer as things change. Classic balancers forward to each node's NodePort.

open as a page

On a bare-metal Kubernetes cluster, how does MetalLB give LoadBalancer Services an external IP, and how do you choose between its Layer 2 and BGP modes?

level: seniorimportance: nice to knowfreq 34%

basics

~20 s

MetalLB's controller assigns an address from an IPAddressPool to each LoadBalancer Service. Its speakers then attract traffic for that address. In Layer 2 mode one node answers ARP/NDP for it; in BGP mode nodes advertise it to routers, which spread traffic across nodes with ECMP.

open as a page