12 - Services & Networking

Why Services?

Pods are ephemeral -- they get new IPs when recreated. A Service provides:

  • Stable IP (ClusterIP) that never changes
  • DNS name (my-service.default.svc.cluster.local)
  • Load balancing across pod replicas
  • Service discovery for other pods
Without Service:                    With Service:
Pod A → 10.244.1.5 (Pod B)        Pod A → my-service (stable)
        ↓ Pod B dies + restarts              ↓
Pod A → 10.244.1.5 ✗ (wrong IP!)  Pod A → my-service → any healthy Pod B

Service Types

1. ClusterIP (Default)

Internal-only access within the cluster:

yaml
apiVersion: v1 kind: Service metadata: name: api-service spec: type: ClusterIP # Default, can omit selector: app: api # Routes to pods with this label ports: - name: http port: 80 # Service port (what clients use) targetPort: 8080 # Container port (where app listens) protocol: TCP
┌─── Cluster ──────────────────────────────────┐
│                                              │
│  Client Pod                                  │
│    curl http://api-service:80                │
│         │                                    │
│         ▼                                    │
│  api-service (ClusterIP: 10.96.45.12)        │
│         │                                    │
│    ┌────┼────┐                               │
│    ▼    ▼    ▼                               │
│  Pod1  Pod2  Pod3   (label: app=api)         │
│  :8080 :8080 :8080                           │
│                                              │
│  NOT accessible from outside cluster         │
└──────────────────────────────────────────────┘

2. NodePort

Exposes service on every node's IP at a static port:

yaml
apiVersion: v1 kind: Service metadata: name: web-service spec: type: NodePort selector: app: web ports: - port: 80 targetPort: 8080 nodePort: 30080 # Range: 30000-32767 (optional, auto-assigned)
External Client
     │
     ▼
Node1:30080  or  Node2:30080  or  Node3:30080
     │                │                │
     └────────────────┼────────────────┘
                      │
              web-service (ClusterIP)
                      │
                ┌─────┼─────┐
                ▼     ▼     ▼
              Pod1  Pod2  Pod3

3. LoadBalancer

Creates an external load balancer (cloud provider only):

yaml
apiVersion: v1 kind: Service metadata: name: public-web annotations: # AWS-specific service.beta.kubernetes.io/aws-load-balancer-type: nlb spec: type: LoadBalancer selector: app: web ports: - port: 80 targetPort: 8080
Internet
   │
   ▼
Cloud Load Balancer (e.g., AWS NLB)
   │ External IP: 54.23.45.67
   │
   ├── Node1:NodePort
   ├── Node2:NodePort
   └── Node3:NodePort
         │
     web-service (ClusterIP)
         │
   ┌─────┼─────┐
   Pod1  Pod2  Pod3

4. ExternalName

Maps a service to an external DNS name:

yaml
apiVersion: v1 kind: Service metadata: name: external-db spec: type: ExternalName externalName: db.example.com
bash
# Inside the cluster: # nslookup external-db → CNAME db.example.com # Useful for referencing external services with a K8s-native name

5. Headless Service

No ClusterIP -- returns individual pod IPs:

yaml
apiVersion: v1 kind: Service metadata: name: db-headless spec: clusterIP: None # This makes it headless selector: app: database ports: - port: 5432
bash
# DNS returns ALL pod IPs (not a single VIP) nslookup db-headless # db-headless.default.svc.cluster.local → 10.244.1.5 # → 10.244.2.3 # → 10.244.3.7 # Individual pod DNS (when used with StatefulSet): # db-0.db-headless.default.svc.cluster.local → 10.244.1.5 # db-1.db-headless.default.svc.cluster.local → 10.244.2.3

Service Comparison

FeatureClusterIPNodePortLoadBalancerExternalName
Internal accessYesYesYesYes (DNS)
External accessNoYes (node:port)Yes (LB IP)N/A
Static IPClusterIPNode IPsExternal IPDNS alias
Cloud requiredNoNoYesNo
Use caseInternal servicesDev/testingProduction externalExternal service alias

Kubernetes DNS

Every Service gets a DNS entry:

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

Examples:
api-service.default.svc.cluster.local     # Full FQDN
api-service.default.svc                    # Short form
api-service.default                        # Shorter
api-service                                # Same namespace
bash
# Inside a pod: curl http://api-service # Same namespace curl http://api-service.production # Different namespace curl http://api-service.production.svc.cluster.local # Full FQDN

Pod DNS (only with Headless Service + StatefulSet):

<pod-name>.<service>.<namespace>.svc.cluster.local
db-0.db-headless.default.svc.cluster.local

Endpoints and EndpointSlices

A Service selects Pods via labels. The list of selected Pod IPs is stored in Endpoints:

bash
# See which pods a service routes to kubectl get endpoints api-service # NAME ENDPOINTS AGE # api-service 10.244.1.5:8080,10.244.2.3:8080,10.244.3.7:8080 # EndpointSlices (modern, more scalable) kubectl get endpointslices -l kubernetes.io/service-name=api-service

Network Policies

By default, all pods can talk to all other pods. Network Policies restrict this:

yaml
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: api-allow namespace: default spec: podSelector: matchLabels: app: api policyTypes: - Ingress - Egress ingress: # Allow traffic FROM web pods on port 8080 - from: - podSelector: matchLabels: app: web - namespaceSelector: matchLabels: name: frontend ports: - port: 8080 protocol: TCP egress: # Allow traffic TO database - to: - podSelector: matchLabels: app: database ports: - port: 5432 # Allow DNS - to: - namespaceSelector: {} ports: - port: 53 protocol: UDP - port: 53 protocol: TCP

Default Policies

yaml
# Deny all ingress (whitelist approach) apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default-deny-ingress spec: podSelector: {} # Applies to ALL pods in namespace policyTypes: - Ingress # No ingress rules = deny all incoming traffic
Note

Note: Network Policies require a CNI plugin that supports them (Calico, Cilium, Weave). The default kubenet does NOT enforce them.

CNI (Container Network Interface)

CNI plugins handle pod networking:

PluginFeaturesUsed By
CalicoNetwork policies, BGP, eBPFMost popular
CiliumeBPF-based, observability, service meshGrowing fast
FlannelSimple overlay, no network policiesBasic clusters
WeaveSimple, encryption, network policiesSmall clusters
AWS VPC CNINative AWS VPC networkingEKS
Azure CNINative Azure networkingAKS

Service Mesh (Preview)

A service mesh adds a proxy sidecar to every pod for advanced networking:

┌─── Pod ─────────────────┐
│  ┌─────┐  ┌───────────┐ │
│  │ App │←→│ Envoy     │ │ ← Sidecar proxy
│  │     │  │ Proxy     │ │
│  └─────┘  └─────┬─────┘ │
└─────────────────┼───────┘
                  │ mTLS, retries, circuit breaking
┌─────────────────┼────────┐
│  ┌─────┐  ┌────┴────┐    │
│  │ App │←→│ Envoy   │    │
│  └─────┘  └─────────┘    │
└──────────────────────────┘

Popular service meshes: Istio, Linkerd, Cilium Service Mesh See 23 - Service Mesh & Advanced Patterns for details.

kubectl Networking Commands

bash
# List services kubectl get svc kubectl get svc -o wide # Describe service (see endpoints) kubectl describe svc api-service # Expose a deployment as a service kubectl expose deployment web --port=80 --target-port=8080 # Port forward (for debugging) kubectl port-forward svc/api-service 8080:80 # Test DNS from inside cluster kubectl run -it --rm debug --image=busybox -- nslookup api-service # List network policies kubectl get networkpolicies # Test connectivity kubectl run -it --rm curl --image=curlimages/curl -- curl api-service:80

FAANG Interview Angle

Common questions:

  1. "Explain the different Service types in Kubernetes"
  2. "How does service discovery work in K8s?"
  3. "What are Network Policies and why are they important?"
  4. "How would you expose an application to the internet?"
  5. "What is a headless service and when would you use it?"

Key answers:

  • ClusterIP (internal), NodePort (node IP:port), LoadBalancer (cloud LB), ExternalName (DNS alias)
  • DNS-based: <service>.<namespace>.svc.cluster.local; also env vars
  • Network Policies restrict pod-to-pod traffic; default is allow-all; requires compatible CNI
  • LoadBalancer Service or Ingress controller for HTTP/HTTPS routing
  • Headless (clusterIP: None) returns pod IPs directly; used with StatefulSets for stable pod DNS

Official Links