dissected.io
Lesson 4 of 15
Intermediatenetworkingservicesingressdnskube-proxy11 min read

Services and Networking

Kubernetes gives every pod its own IP address. No NAT, no port mapping between pods — container A can talk to container B using B's pod IP directly. This flat networking model is elegant, but pod IPs are ephemeral. Pods die and get replaced constantly, and their IPs change every time. You need a stable way to find and reach your workloads. That's what Services, DNS, and Ingress solve.

Kubernetes Networking — LoadBalancer Traffic Flow

The Networking Model — Every Pod Gets an IP

Kubernetes requires that every pod can communicate with every other pod across nodes without NAT. The Container Network Interface (CNI) plugin you install — Calico, Cilium, Flannel, AWS VPC CNI — implements this requirement. Each plugin does it differently (overlay networks, BGP peering, native VPC routing), but the contract is the same: flat pod-to-pod connectivity.

This means your application code doesn't need to know which node it's running on. A pod on Node 1 talks to a pod on Node 3 the same way it talks to a pod on the same node.

Service Types — Stable Endpoints for Ephemeral Pods

A Service is a stable abstraction over a set of pods selected by labels. When pods come and go, the Service keeps a consistent endpoint.

ClusterIP (default)

Allocates an internal-only virtual IP. Other pods in the cluster can reach the Service, but nothing outside can.

apiVersion: v1
kind: Service
metadata:
  name: my-app-svc
spec:
  type: ClusterIP
  selector:
    app: my-app
  ports:
    - port: 80
      targetPort: 8080

NodePort

Exposes the Service on a static port (30000–32767) on every node's IP. Useful for development or when you're running on bare metal without a cloud load balancer.

spec:
  type: NodePort
  ports:
    - port: 80
      targetPort: 8080
      nodePort: 30080

LoadBalancer

Provisions a cloud load balancer (AWS NLB/ALB, GCP LB, Azure LB) that routes external traffic into the cluster. This is the most common way to expose production services.

ExternalName

A DNS CNAME alias pointing to an external service. No proxying — just DNS resolution. Useful for giving an in-cluster DNS name to an external database or API.

kube-proxy — The Traffic Cop

kube-proxy runs on every node and maintains the networking rules that make Services work. When a pod sends traffic to a Service's ClusterIP, kube-proxy intercepts it and forwards it to one of the backing pods.

Two modes matter:

  • iptables mode (default): Creates iptables rules for each Service/endpoint combination. Fast for small clusters, but rule updates become expensive at scale (thousands of Services mean tens of thousands of iptables rules).
  • IPVS mode: Uses Linux kernel IPVS (IP Virtual Server) for load balancing. Better performance at scale, supports more load balancing algorithms (round-robin, least connections, weighted). Use this if you have 1000+ Services.

CoreDNS — Service Discovery Without Hardcoding

Every Service gets a DNS entry automatically:

<service-name>.<namespace>.svc.cluster.local

So my-app-svc in the default namespace is reachable at my-app-svc.default.svc.cluster.local. Inside the same namespace, just my-app-svc works.

CoreDNS watches the Kubernetes API for Service changes and updates DNS records in real time. Headless Services (those with clusterIP: None) return individual pod A records instead of a single virtual IP — essential for StatefulSets where you need to address specific pods.

Ingress — HTTP Routing at the Edge

An Ingress resource defines HTTP/HTTPS routing rules — host-based routing, path-based routing, TLS termination. But it's just a spec. You need an Ingress controller (NGINX, Traefik, HAProxy, cloud-native options like AWS ALB Ingress Controller) to actually implement it.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
spec:
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /api
            pathType: Prefix
            backend:
              service:
                name: api-svc
                port:
                  number: 80
          - path: /
            pathType: Prefix
            backend:
              service:
                name: frontend-svc
                port:
                  number: 80
  tls:
    - hosts:
        - app.example.com
      secretName: tls-secret

Gateway API — The Future of Ingress

Gateway API is the successor to Ingress, designed to fix its limitations. It separates concerns into three layers:

  • GatewayClass: The infrastructure provider (who runs the load balancer)
  • Gateway: The actual listener configuration (ports, protocols, TLS)
  • HTTPRoute / TCPRoute: The routing rules (what goes where)

This separation means platform teams configure Gateways while application teams define Routes — a much cleaner operational model than Ingress, where everything was lumped into one resource.

The Full Request Path

When an external request hits your cluster, here's the complete chain:

  1. DNS resolves app.example.com to the load balancer's IP
  2. Load balancer forwards to a node (NodePort)
  3. kube-proxy's iptables/IPVS rules rewrite the destination to a pod IP
  4. The request arrives at the pod's container on the target port
  5. Response travels back through the same chain

Every hop is observable. When debugging networking issues, start from the pod and work outward: can the pod reach the Service? Can other pods reach it? Can you curl the NodePort from the node? Is the load balancer health check passing?

Enjoyed this breakdown?

Get notified when new lessons go live.