09

Kubernetes Services: ClusterIP vs NodePort vs LoadBalancer vs ExternalName

Video: Day 9/40 — Kubernetes Services Explained • https://www.youtube.com/watch?v=tHAQWLKMTB0 • Duration: ~46 min

Key terms

TermMeaning
ServiceStable network endpoint for a set of pods
ClusterIPInternal-only virtual IP (default type)
NodePortExposes a port on every node
LoadBalancerProvisions an external cloud load balancer
ExternalNameMaps a Service to an external DNS CNAME
SelectorLabels picking the backend pods
EndpointsThe pod IPs currently behind a Service
port/targetPort/nodePortService / container / node port numbers

Problem & solution

Pods are ephemeral and get a new IP each time they are recreated, so nothing can reliably address them. We need a stable virtual IP and DNS name that load-balances across a constantly changing set of matching pods.

Solution: Put a Service (ClusterIP/NodePort/LoadBalancer) in front of pods for a stable IP/DNS that load-balances to healthy pods.

The analogy

At our port, the actual ships come and go constantly, different vessels tie up at different spots every day. If you told a customer "your goods are on the ship currently at the third buoy," they'd be lost the moment it sailed. So the port gives each route a permanent pier number: customers always go to "Pier 7," and the port quietly directs them to whichever ships are berthed there right now. A Kubernetes Service is that permanent pier number, a stable IP and DNS name, while the pods behind it are the ever-changing ships. The Service's selector is the rule "all ships flying the app=web flag dock at this pier."

Where this fits in the cluster

The same cluster entities appear in every day's notes; the diagram below shows where this day's topic fits.

Why Services exist

Pods are ephemeral — they die and get recreated with new IPs. You can't rely on a pod IP. A Service gives a stable virtual IP + DNS name and load-balances across the matching pods.

Pods run on nodes. Each pod gets its own IP; the container listens on a containerPort (e.g. 8080). Nodes have their own IPs too.

Graph legend — a real nginx Deployment fronted by a Service vs hit directly:

Graph nodeMaps toWhat it does
nginx pod 10.1.1.5 port 80a bare nginx pod's podIPThe ephemeral IP that changes on every recreate
Service nginx ClusterIP 10.96.0.5Service.spec (type ClusterIP)Stable virtual IP + DNS in front of the nginx pods
nginx pod ...port 80 (Node A/B)the nginx Deployment's podsReal backends selected by the Service, on any node
load-balanced across nodeskube-proxy / EndpointsSpreads requests over the healthy nginx pods

How a Service finds its pods: labels & selectors

A Service has no hard-coded pod list — its selector matches pod labels, and the matching pod IPs become the Service's Endpoints.

Graph legend — how the nginx Service discovers its pods:

Graph nodeMaps toWhat it does
Service nginx spec.selector app=nginxService.spec.selectorLabel query that picks the backend nginx pods
nginx pod app=nginxthe nginx pods' metadata.labelsPods carrying app=nginx are matched
Endpoints = podIP, podIP, podIPthe auto-created Endpoints/EndpointSliceThe live list of matching nginx pod IPs
kubectl get endpoints nginx     # the actual nginx pod IPs behind the service

The 4 Service types

A Service's type field decides who can reach it and how. There are four types, from most internal to most external: ClusterIP (inside the cluster only), NodePort (reachable on each node's IP), LoadBalancer (a public cloud IP), and ExternalName (a DNS alias to something outside the cluster). The next four subsections take them one at a time.

1) ClusterIP (default) — internal only

Gives a stable virtual IP reachable only inside the cluster.

Use for pod-to-pod / internal microservice traffic.

Graph legend — ClusterIP keeps nginx internal-only:

Graph nodeMaps toWhat it does
Service nginx type=ClusterIPService.spec.type: ClusterIPInternal-only virtual IP, the default Service type
nginx podthe nginx Deployment's podsIn-cluster backends the ClusterIP load-balances to
client podanother in-cluster workloadReaches nginx by the Service name/ClusterIP, never from outside

2) NodePort — expose on every node's IP at a high port

Opens the same high port on every node, forwarding outside traffic to the pods.

Graph legend — NodePort exposes nginx on every node:

Graph nodeMaps toWhat it does
Service nginx type=NodePortService.spec.type: NodePortOpens the same high port on every node for nginx
Node A/B port 30007Service.spec.ports[].nodePortThe 30000-32767 port each node forwards inward
nginx podthe nginx Deployment's podsReal backends the NodePort routes to via ClusterIP

3) LoadBalancer — cloud external LB

Asks the cloud provider for a public IP and front-ends the service with a managed load balancer.

Graph legend — LoadBalancer fronts nginx with a cloud LB:

Graph nodeMaps toWhat it does
Cloud LB public IPService.spec.type: LoadBalancerCloud-provisioned external load balancer with a public IP
Service nginx NodePort layerthe auto-allocated nodePortThe LoadBalancer forwards to this node port under the hood
Service nginx ClusterIP layerthe Service's clusterIPThe internal VIP the NodePort forwards to
nginx podsthe nginx Deployment's podsFinal backends serving the request

4) ExternalName — DNS alias to an external host

Maps the service name to an external DNS host via a CNAME — no pods, no proxying.

Graph legend — ExternalName aliases an external managed Postgres:

Graph nodeMaps toWhat it does
Service postgres type=ExternalNameService.spec.type: ExternalNameA Service with no selector/pods, just a DNS CNAME
mydb...rds.amazonaws.comService.spec.externalNameThe real external host (e.g. an AWS RDS Postgres endpoint)
app podan in-cluster clientResolves postgres to the external DB via CoreDNS

Layering (each type builds on the previous)

Kubernetes Services come in three main types, and they are not separate ideas — each one builds on the type beneath it. ClusterIP gives a Service a stable internal-only address; NodePort adds a fixed port on every node on top of that ClusterIP; LoadBalancer adds a cloud load balancer on top of the NodePort. So choosing LoadBalancer automatically gives you the NodePort and ClusterIP underneath — that stacking is the "layering" this section shows.

Graph legend — each Service type wraps the one below:

Graph nodeMaps toWhat it does
LoadBalancer cloud public IPtype: LoadBalancerOutermost layer: cloud LB with a public IP
NodePort NodeIP 30007type: NodePortMiddle layer: a port on every node
ClusterIP internal VIPtype: ClusterIPBase layer: the internal virtual IP
nginx Podsthe nginx Deployment's podsThe real backends at the bottom of the stack

Service YAML examples

Minimal manifests for the two types you create most often.

ClusterIP (in front of a real nginx Deployment, image nginx:1.27, container port 80):

apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  type: ClusterIP
  selector:
    app: nginx
  ports:
    - port: 80          # service port
      targetPort: 80    # nginx container port

NodePort:

spec:
  type: NodePort
  selector:
    app: nginx
  ports:
    - port: 80
      targetPort: 80
      nodePort: 30007   # optional; auto-assigned if omitted

port vs targetPort vs nodePort

These three ports map the hop from node, to service, to container — keep them straight.

Example mapping: 192.168.0.11:30007 (NodeIP:nodePort) to 10.96.0.5:80 (svc:port) to 10.1.1.8:80 (podIP:targetPort).

Graph legend — the three ports for the nginx Service:

Graph nodeMaps toWhat it does
nodePort 30007Service.spec.ports[].nodePortExternal port on each Node IP (NodePort/LoadBalancer only)
port 80 on the Service ClusterIPService.spec.ports[].portThe port the Service VIP listens on in-cluster
targetPort 80 on the nginx PodService.spec.ports[].targetPortnginx's containerPort that actually serves traffic

Commands

Everyday commands to expose a Deployment, inspect the Service, and test it from inside the cluster.

kubectl create deployment nginx --image=nginx:1.27 --replicas=3
kubectl expose deployment nginx --port=80 --target-port=80 --type=NodePort
kubectl get svc
kubectl get svc nginx -o wide
kubectl describe svc nginx

# test internal DNS from another pod
kubectl run tmp --image=busybox -it --rm -- wget -qO- nginx:80

Service DNS name

Every Service gets an in-cluster DNS name built from its name, namespace, and the cluster domain.

   nginx .   default   .   svc   .   cluster.local
   ───       ───────       ───       ─────────────
  service   namespace     type        cluster
   name                   marker      domain

   e.g.  nginx.default.svc.cluster.local

End-to-end flow

A request reaches a stable Service and is load-balanced across the healthy pods picked by its selector.

Graph legend — a request reaching the real nginx Service end to end:

Graph nodeMaps toWhat it does
Client pod: curl nginxan in-cluster callerResolves the Service DNS name and connects
Service nginx ClusterIP + DNSService.spec (ClusterIP)Stable VIP/name for the nginx Deployment
kube-proxy ruleskube-proxy iptables/IPVSPrograms the load-balancing rules on the node
Endpoints from selector app=nginxthe Service's EndpointsLive nginx pod IPs matched by the selector
nginx Pod 1/2/3the nginx Deployment's podsThe healthy backends that serve the response

Key takeaways

  • Services give a stable IP/DNS + load balancing over ephemeral pods.
  • ClusterIP internal, NodePort node IPs, LoadBalancer cloud public, ExternalName DNS alias.
  • Remember the port / targetPort / nodePort trio.

Checklist

  • [ ] Exposed a Deployment as ClusterIP and reached it from another pod
  • [ ] Created a NodePort and hit it on a node IP
  • [ ] Inspected kubectl get endpoints
  • [ ] Can explain port vs targetPort vs nodePort