NodePort and LoadBalancer

How does traffic from outside the cluster reach a Pod? A NodePort opens the same port on every Node, so a client can dial any Node directly, even one with no backend, which by default DNATs the connection and forwards it across the cluster network to a Pod on another Node. A LoadBalancer puts one address in front: the cloud-controller-manager provisions an external balancer that typically forwards to that Node port, and the chosen Node DNATs to a Pod.

Play the animation 5 steps, animated, best on a wide screen

NodePort and LoadBalancer, Kubernetes diagram

NodePort and LoadBalancer, step by step

  1. Setting type NodePort on Service web makes the control plane allocate one port from the Node port range, here 31000, and every Node proxies that same port into the Service. In its default iptables mode kube-proxy writes a KUBE-NODEPORTS rule for 31000 on each Node, Node-2 included, although it runs no backend Pod.

    Play from step 1
  2. No balancer is needed to use it. A client that can reach a Node dials Node-2 on port 31000 directly. Node-2 has no backend, so under the default externalTrafficPolicy Cluster its rule DNATs the connection to a ready Pod, here 10.244.3.9 on port 8080, and forwards it across the cluster network to Node-3. Node-2 SNATs it as well.

    Play from step 2
  3. Switching the type to LoadBalancer keeps that Node port by default. The cloud-controller-manager provisions an external load balancer, typically pointed at port 31000 on every Node. Creation happens asynchronously, so status.loadBalancer stays empty until 203.0.113.7 is published in status.loadBalancer.ingress.

    Play from step 3
  4. An external client now dials 203.0.113.7 on port 80 and the balancer picks a Node. A balancer that targets Node ports sends the connection to Node-1 on 31000, as drawn. One that preserves the destination, which ipMode VIP declares, would deliver it still addressed to 203.0.113.7:80, and kube-proxy catches that with its load balancer IP rule.

    Play from step 4
  5. On Node-1 the KUBE-NODEPORTS rule for 31000 DNATs the connection to the local Pod 10.244.1.5:8080. The balancer only chose the Node: under the default Cluster policy the rule picks from every ready Pod, so it could as well have picked 10.244.3.9. One external address has reached a private Pod.

    Play from step 5

Sources