beginner~4h

Networking I: Services, Service Discovery & DNS

Pod IPs are inherently unstable -- Pods are created and destroyed constantly. A Service is a stable virtual IP and DNS name that load-balances traffic across a dynamically-changing set of backing Pods

Pod IPs are inherently unstable -- Pods are created and destroyed constantly. A Service is a stable virtual IP and DNS name that load-balances traffic across a dynamically-changing set of backing Pods. Four types: ClusterIP (internal, default), NodePort, LoadBalancer, and ExternalName.

  • Every Service gets a stable ClusterIP (virtual IP) and a DNS name (..svc.cluster.local) from CoreDNS.

  • An EndpointSlice controller continuously watches matching Pods and updates EndpointSlice objects with their current IPs.

  • kube-proxy programs forwarding rules on every node so traffic to the ClusterIP is DNAT'd to a healthy backing Pod.

  • NodePort builds on ClusterIP -- additionally opens a port (30000-32767) on every node.

  • LoadBalancer builds on NodePort -- a cloud controller provisions a real external load balancer.

  • Label your application Pods consistently (e.g. app: payments) -- a Service's spec.selector matches on these labels, nothing else.

  • Create a Service (default type ClusterIP) with that selector and the containerPort your Pods actually listen on as targetPort.

  • CoreDNS immediately registers ..svc.cluster.local resolving to the Service's ClusterIP; other Pods can now reach it by name instead of IP.

  • The EndpointSlice controller populates the Service's backing Pod IPs automatically as matching Pods become Ready -- no manual wiring.

  • For access from outside the cluster, change type to NodePort (opens a port on every node) or LoadBalancer (provisions a cloud load balancer that forwards to the NodePort).

  • For referencing an external (non-Kubernetes) system by a stable in-cluster DNS name, use type: ExternalName instead of a selector.

  • A frontend Deployment calling a backend API purely by its Service DNS name (e.g. http://orders-api.default.svc.cluster.local) instead of hardcoded Pod IPs.

  • Exposing a public-facing web app via a LoadBalancer Service so cloud infrastructure provisions a real external IP/ALB.

  • Using NodePort for a quick debug/dev-only path to a service during troubleshooting, without waiting on a cloud load balancer.

  • Using ExternalName to give an in-cluster DNS name to a managed database or external SaaS API endpoint that lives outside the cluster.

  • Multiple replicas of a stateless API sitting behind a single ClusterIP Service so callers never need to track individual Pod churn.

  • Always name containerPort in the Pod spec and reference it by name in the Service's targetPort -- avoids silent breakage when the container's actual port changes.

  • Prefer ClusterIP + Ingress over NodePort for HTTP services that need external access -- NodePort's high port range and per-node exposure are awkward for real ingress traffic.

  • Use readiness probes on backing Pods -- the EndpointSlice controller only includes Ready Pods, so a bad readiness probe either sends traffic too early or needlessly excludes healthy Pods.

  • Keep Service selectors narrow and specific to avoid accidentally matching Pods from an unrelated Deployment sharing an overlapping label.

  • For headless use cases (direct Pod addressing, e.g. peer discovery), use clusterIP: None deliberately rather than a normal ClusterIP.

  • Assuming a Service load-balances at the connection level with even distribution -- kube-proxy's iptables mode is effectively random selection per connection, not round-robin per request.

  • Forgetting that a Service selector matching zero Pods creates a Service with no endpoints -- requests hang or fail with no obvious error unless you check kubectl get endpointslices.

  • Using a Pod's IP directly in application config instead of the Service DNS name -- Pod IPs change on every restart/reschedule.

  • Not aligning targetPort with the container's actual listening port after a code change -- the Service silently keeps forwarding to the old port.

  • Exposing internal-only services as LoadBalancer out of habit, unnecessarily creating public cloud load balancers and cost.

  • Confusing NodePort's port range (30000-32767) with the Service's own port, causing connection attempts on the wrong port from outside the cluster.

  • kube-proxy in iptables mode adds per-Service rule-matching overhead that grows with Service count; ipvs mode scales better on clusters with thousands of Services.

  • DNS lookups for Service names go through CoreDNS -- cache-miss latency on cold lookups can matter for latency-sensitive request paths; tune CoreDNS's cache TTL and replica count for high-QPS clusters.

  • LoadBalancer Services provision a real cloud load balancer per Service by default -- consolidating multiple HTTP backends behind a single Ingress Controller instead of one LoadBalancer Service per app reduces both cost and per-hop latency variance.

  • EndpointSlice (vs the older Endpoints object) caps entries per slice, which materially reduces control-plane churn/broadcast cost for Services with very large numbers of backing Pods.

  • Monitor kube-proxy and CoreDNS themselves as critical infrastructure -- a CoreDNS outage silently breaks in-cluster service discovery cluster-wide even though every individual Pod is healthy.

  • Set explicit readiness probes on every Service-backed Deployment; without one, a Pod can receive traffic before its app is actually ready to serve.

  • For public LoadBalancer Services, restrict source IP ranges (loadBalancerSourceRanges) where the traffic source is known, rather than leaving them open to the internet by default.

  • Audit Service types periodically -- LoadBalancer Services that should have been ClusterIP-behind-Ingress accumulate as real, ongoing cloud cost.

  • Deploy two replicas of a simple web app behind a ClusterIP Service, then curl the Service DNS name repeatedly from another Pod and observe requests landing on different backing Pods.

  • Change the backing Pods' labels temporarily so they no longer match the Service selector and run kubectl get endpointslices to see the Service drop to zero endpoints.

  • Change a Service from ClusterIP to NodePort and connect to it from outside the cluster on the assigned node port.

  • Create a headless Service (clusterIP: None) and use nslookup from inside a Pod to see individual Pod IPs returned instead of a single virtual IP.

  • Services provide stable virtual IP and DNS name across an inherently unstable set of backing Pods.

  • ClusterIP, NodePort, and LoadBalancer build in layers -- each a superset of network exposure.

  • EndpointSlices track current backing Pod IPs; kube-proxy uses them to program forwarding rules.

  • A Service with zero matching Ready Pods is a silent failure -- EndpointSlice shows the empty set.

Want a visual for this concept?

Generate a diagram tailored to “Networking I: Services, Service Discovery & DNS” — the AI picks whichever visual (flowchart, comparison, sequence, etc.) best fits.

Sign in to generate a visual →

Practice quiz

Next Step

Continue to Networking II: Ingress & Network Policies← Back to all Kubernetes chapters