Services & Networking
Pods are ephemeral β they come and go with new IPs each time. A Service gives you a stable endpoint to reach them.
Why Services Existβ
Before Service:
Pod A β connects to Pod B at 10.244.1.5
Pod B crashes β recreated at 10.244.2.7
Pod A β ??? (IP changed, connection broken)
With Service:
Service "my-api" β stable ClusterIP: 10.96.0.42
Pod A β connects to 10.96.0.42 (always works)
Service load-balances to healthy backing Pods automatically
Service Typesβ
1. ClusterIP (Default)β
Internal-only. Accessible only within the cluster.
apiVersion: v1
kind: Service
metadata:
name: my-api
spec:
type: ClusterIP # Default β can omit
selector:
app: my-api # Route to Pods with this label
ports:
- name: http
port: 80 # Service port (what clients connect to)
targetPort: 8080 # Container port (what the Pod listens on)
protocol: TCP
- name: management
port: 9090
targetPort: 9090
Inside cluster:
curl http://my-api # Resolves via DNS β ClusterIP
curl http://my-api:80 # Explicit port
curl http://my-api.default.svc.cluster.local # Fully qualified
2. NodePortβ
Exposes service on each Node's IP at a static port (30000β32767).
apiVersion: v1
kind: Service
metadata:
name: my-api-nodeport
spec:
type: NodePort
selector:
app: my-api
ports:
- port: 80 # ClusterIP port
targetPort: 8080 # Container port
nodePort: 30080 # Port on every Node (omit for random)
Traffic flow:
External β NodeIP:30080 β ClusterIP:80 β Pod:8080
Use case: Development, on-premise without cloud load balancer. Rarely used in production cloud deployments.
3. LoadBalancerβ
Provisions a cloud load balancer. Most common for production.
apiVersion: v1
kind: Service
metadata:
name: my-api-lb
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: "nlb" # AWS NLB
service.beta.kubernetes.io/aws-load-balancer-internal: "false"
spec:
type: LoadBalancer
selector:
app: my-api
ports:
- port: 80
targetPort: 8080
Traffic flow:
Internet β AWS ALB/NLB (external IP) β NodePort β Pod:8080
Problem with LoadBalancer: One external IP / load balancer per service = expensive and complex. Use Ingress for HTTP traffic instead.
4. ExternalNameβ
Maps a service to an external DNS name. No proxying β just DNS CNAME.
apiVersion: v1
kind: Service
metadata:
name: my-database
spec:
type: ExternalName
externalName: mydb.rds.amazonaws.com # External hostname
Inside cluster:
jdbc:postgresql://my-database:5432/mydb
β resolves to mydb.rds.amazonaws.com via DNS
Use case: Reference external services (RDS, ElastiCache) from within the cluster using Kubernetes DNS names.
5. Headless Serviceβ
No ClusterIP β direct Pod IPs returned by DNS. Used by StatefulSets.
apiVersion: v1
kind: Service
metadata:
name: postgres-headless
spec:
clusterIP: None # β headless
selector:
app: postgres
ports:
- port: 5432
DNS lookup for postgres-headless returns ALL Pod IPs:
nslookup postgres-headless β 10.244.1.1, 10.244.2.1, 10.244.3.1
StatefulSet Pods get individual DNS:
postgres-0.postgres-headless.default.svc.cluster.local β 10.244.1.1
Ingressβ
HTTP/HTTPS routing into the cluster from outside. One LoadBalancer handles all HTTP services.
Without Ingress: With Ingress:
/api β LoadBalancer #1 One LoadBalancer
/web β LoadBalancer #2 β Ingress Controller
/docs β LoadBalancer #3 β Routes by path/host
Cost: 3Γ load balancers Cost: 1 load balancer
Ingress Resourceβ
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "true"
cert-manager.io/cluster-issuer: "letsencrypt-prod" # Auto TLS cert
spec:
ingressClassName: nginx # Which Ingress Controller to use
# TLS
tls:
- hosts:
- api.example.com
- www.example.com
secretName: my-tls-secret # Certificate stored as Secret
# Routing rules
rules:
# Route by host
- host: api.example.com
http:
paths:
- path: /v1
pathType: Prefix
backend:
service:
name: api-v1-svc
port:
number: 80
- path: /v2
pathType: Prefix
backend:
service:
name: api-v2-svc
port:
number: 80
- host: www.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: frontend-svc
port:
number: 80
Path Typesβ
| Type | Behaviour |
|---|---|
Exact | Exactly matches /foo β not /foo/ or /foo/bar |
Prefix | Matches /foo and /foo/bar and /foo/baz |
ImplementationSpecific | Controller-defined |
Popular Ingress Controllersβ
| Controller | Notes |
|---|---|
| nginx | Most common. kubectl apply -f ingress-nginx.yaml |
| Traefik | Dynamic config, auto-discovers services |
| AWS ALB | Native AWS integration (aws-load-balancer-controller) |
| GCE | Native GKE integration |
| Istio | Full service mesh with gateway |
# Install nginx ingress controller
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.10.0/deploy/static/provider/cloud/deploy.yaml
# Check Ingress status
kubectl get ingress
kubectl describe ingress my-ingress
Gateway API (The Modern Standard)β
Ingress is legacy. The modern standard for Kubernetes traffic routing is the Gateway API. It separates concerns so infrastructure providers manage the Gateway, while application developers manage the Routes.
Ingress (Legacy):
Single object mixing L4/L7 routing, TLS, and infrastructure setup.
Gateway API (Modern):
GatewayClass β Describes infrastructure (e.g., AWS ALB, Istio)
Gateway β Provisions the Loadbalancer / IP
HTTPRoute β Developer specifies routing logic
HTTPRoute Exampleβ
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: api-route
namespace: production
spec:
parentRefs:
- name: my-gateway # Attach to infrastructure gateway
rules:
- matches:
- path:
type: PathPrefix
value: /api/v2
backendRefs:
- name: api-v2-svc
port: 8080
weight: 90 # Canary rollouts natively supported!
- name: api-v2-canary
port: 8080
weight: 10
Why it's better: Supports header matching, traffic splitting (canary deploys), cross-namespace routing, and generic L4/L7 routing natively without using non-standard annotations.
Kubernetes DNSβ
Every Service gets a DNS entry: <service>.<namespace>.svc.cluster.local
Service: my-api in namespace: production
Full name: my-api.production.svc.cluster.local
Short name: my-api.production (from other namespaces)
Shortest: my-api (within same namespace)
# Test DNS from inside a Pod
kubectl run -it --rm dns-test --image=busybox --restart=Never -- sh
$ nslookup my-api
$ nslookup my-api.production.svc.cluster.local
$ nslookup kubernetes.default.svc.cluster.local
CoreDNSβ
Kubernetes uses CoreDNS as its cluster DNS server. Runs as a Deployment in kube-system.
kubectl get pods -n kube-system -l k8s-app=kube-dns
kubectl get configmap coredns -n kube-system -o yaml # CoreDNS config
Network Policyβ
Controls which Pods can communicate with each other. Default: all traffic allowed.
# Deny ALL ingress to "api" Pods β then explicitly allow needed traffic
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-network-policy
spec:
podSelector:
matchLabels:
app: my-api # Apply to Pods labeled app=my-api
policyTypes:
- Ingress
- Egress
ingress:
# Allow traffic from nginx ingress controller
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-nginx
ports:
- protocol: TCP
port: 8080
# Allow monitoring from Prometheus
- from:
- namespaceSelector:
matchLabels:
name: monitoring
podSelector:
matchLabels:
app: prometheus
ports:
- port: 9090
egress:
# Allow outbound to postgres
- to:
- podSelector:
matchLabels:
app: postgres
ports:
- port: 5432
# Allow DNS
- to:
- namespaceSelector: {}
ports:
- port: 53
protocol: UDP
- port: 53
protocol: TCP
β NetworkPolicy requires a CNI plugin that supports it: Calico, Cilium, WeaveNet. Flannel does NOT support NetworkPolicy.
Traffic Flow: Internet to Podβ
1. User β DNS β myapp.example.com β External IP
2. External IP β Cloud Load Balancer (AWS ALB)
3. ALB β Kubernetes NodePort (30080) on any Node
4. Node iptables/IPVS β kube-proxy rules β Service ClusterIP
5. ClusterIP β load balanced to one of the backing Pods
6. Pod β Container listening on port 8080
In full:
User β ALB:443 β NodePort:30443 β Service:443 β Pod:8080
Service Discovery Pattern for Spring Bootβ
# application.yml in Kubernetes
spring:
datasource:
url: jdbc:postgresql://postgres-svc.production.svc.cluster.local:5432/mydb
# OR short name (same namespace):
url: jdbc:postgresql://postgres-svc:5432/mydb
data:
redis:
host: redis-svc # K8s Service name as hostname
port: 6379
Interview Questionsβ
- What is a Kubernetes Service and why is it needed?
- What are the four Service types? When would you use each?
- What is the difference between a ClusterIP and a LoadBalancer service?
- What is an Ingress and why is it preferable to multiple LoadBalancer services?
- How does Kubernetes DNS work? What is the format of a fully qualified service name?
- What is a headless Service and why do StatefulSets use them?
- What is a NetworkPolicy? What is the default behaviour without one?
- Trace the path of an HTTP request from a user's browser to a container running in Kubernetes.
- What is an Ingress Controller and how does it relate to the Ingress resource?
- What CNI plugin do you need for NetworkPolicy to work?