05: Networking & Storage¶
Service Types & Networking¶
ClusterIP (Default)¶
Internal communication only (between pods).
YAML example
Pods can reach via DNS:
NodePort¶
Expose on every node's IP.
YAML example
Access from outside:
LoadBalancer¶
Cloud provider load balancer (AWS ELB, GCP, Azure).
YAML example
Get external IP:
Service Types - Deep Comparison¶
| Type | Reachability | Typical Use | Pros | Trade-offs |
|---|---|---|---|---|
ClusterIP |
Inside cluster only | Internal microservice-to-microservice calls | Simple, secure default, no external exposure | Not directly reachable from your laptop/browser |
NodePort |
<node-ip>:nodePort |
Lab/dev access, quick external testing | Easy to expose without extra controller | Fixed port range (30000-32767), less flexible for production |
LoadBalancer |
External IP/DNS | Production north-south traffic | Cloud-native external entry point | Requires cloud LB integration or local LB addon |
How Kubernetes Service Routing Actually Works¶
When you call http://api-service, Kubernetes does not send traffic directly to a Pod IP by itself. Several components cooperate:
CoreDNSresolvesapi-service.default.svc.cluster.localto the ServiceClusterIP.kube-proxyprograms node networking rules (iptables/IPVS) for that Service.- The packet is DNATed from
ClusterIP:portto one Pod endpoint (podIP:targetPort). - Endpoint membership comes from
EndpointSliceobjects, which are built from label selectors and pod readiness.
Because of this design:
- Pods can restart and get new IPs without clients changing URLs.
- Unready pods are removed from routing automatically.
- Service names remain stable, while backends can change constantly.
Pod Network Model (Why Pod-to-Pod Works)¶
Kubernetes follows a flat pod networking model:
- Every Pod gets its own IP.
- Every Pod can (by default) reach every other Pod IP.
- No Pod-level NAT is required for pod-to-pod traffic.
This is implemented by the CNI plugin (for Minikube commonly bridge + kube-proxy rules; in other clusters Calico/Cilium/Flannel, etc.).
Request Flow Examples¶
1) In-cluster call via ClusterIP¶
frontend-pod -> CoreDNS -> api-service(ClusterIP) -> kube-proxy rules -> api-pod
2) External call via NodePort¶
curl from laptop -> nodeIP:30000 -> kube-proxy rules -> api-service -> api-pod
3) External call via LoadBalancer¶
client -> external LB -> Service -> api-pod
In local Minikube-on-macOS Docker-driver setups, direct nodeIP:nodePort may not be reachable from host networking. minikube service <name> --url usually provides a reachable local URL.
Ingress (HTTP Load Balancing)¶
Route HTTP/HTTPS to different services based on hostname/path.
Ingress vs Service (Important Concept)¶
- A
Service(L4) forwards traffic to pods by IP/port. - An
Ingress(L7) routes HTTP/HTTPS by host/path and then forwards to Services.
In practice, you still need both:
- Service exposes app internally.
- Ingress gives one smart HTTP entrypoint for many Services.
Ingress Controller Requirement¶
Ingress YAML alone does nothing unless an Ingress controller is running (Nginx, Traefik, HAProxy, etc.).
Quick check:
YAML example
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-ingress
spec:
ingressClassName: nginx # Use nginx ingress controller
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80
- host: web.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-service
port:
number: 80
tls:
- hosts:
- api.example.com
secretName: api-tls-secret
Request to api.example.com → Ingress → api-service → api-pod
Request to web.example.com → Ingress → web-service → web-pod
DNS & Service Discovery¶
K8s DNS automatically creates records:
Example:
# Service: api-service in default namespace
curl http://api-service # From same namespace (short)
curl http://api-service.default # Specify namespace
curl http://api-service.default.svc.cluster.local # FQDN
DNS Resolution Path (Step-by-Step)¶
- App asks resolver for
api-service. - Pod
/etc/resolv.confsearch domains expand it (for example:api-service.default.svc.cluster.local). - Query goes to
CoreDNSService (usuallykube-system). - CoreDNS returns Service
ClusterIP. - Client connects to that IP; kube-proxy handles backend pod selection.
Useful checks:
# Check DNS service and pods
kubectl -n kube-system get svc,pods | grep -E "kube-dns|coredns"
# Launch a temporary debugging shell
kubectl run dns-debug --image=busybox:1.36 --rm -it -- sh
# Inside the pod
nslookup api-service
nslookup api-service.default.svc.cluster.local
cat /etc/resolv.conf
Common DNS and Discovery Mistakes¶
- Wrong namespace in service name (
api-service.prodvsapi-service.default). - Service selector labels do not match pod labels.
- Pods not Ready, so no endpoints are published.
- NetworkPolicy blocks DNS egress to CoreDNS (
53/UDPand sometimes53/TCP).
Networking Troubleshooting Playbook¶
Use this order to isolate issues quickly.
1) Is the app healthy?¶
2) Does Service select pods?¶
kubectl get svc api-service -o yaml
kubectl get pods -l app=api --show-labels
kubectl get endpoints api-service
kubectl get endpointslices -l kubernetes.io/service-name=api-service
3) Is DNS resolving?¶
kubectl run net-debug --image=curlimages/curl --rm -it -- sh
# Inside pod
nslookup api-service
curl -v http://api-service/health
4) Is network policy blocking traffic?¶
5) Is external entry path correct?¶
# NodePort
kubectl get svc api-nodeport
minikube service api-nodeport --url
# Ingress
kubectl get ingress
kubectl describe ingress <ingress-name>
Error Pattern -> Likely Cause¶
| Symptom | Most likely cause |
|---|---|
curl: (6) Could not resolve host |
DNS/CoreDNS issue or wrong service name |
Connection refused |
App not listening on target port or container crash |
Connection timed out |
Network path blocked (policy/firewall/driver networking) |
Service has <none> endpoints |
Selector mismatch or pods not Ready |
| Ingress exists but no routing | Ingress controller missing or wrong class |
Storage (PersistentVolumes & Claims)¶
Problem: Container Storage is Ephemeral¶
Solution: PersistentVolume (PV)¶
Cluster-level storage resource.
YAML example
PersistentVolumeClaim (PVC)¶
Request for storage.
YAML example
Use in Pod¶
YAML example
ConfigMaps & Secrets¶
ConfigMap (Non-Sensitive Configuration)¶
YAML example
Use in Pod:
YAML example
Secret (Sensitive Data)¶
apiVersion: v1
kind: Secret
metadata:
name: app-secret
type: Opaque
data:
DB_PASSWORD: cGFzc3dvcmQxMjM= # base64 encoded
API_KEY: c2VjcmV0LWtleQ==
Create from files:
kubectl create secret generic app-secret \
--from-literal=DB_PASSWORD=password123 \
--from-file=config.json
Use in Pod:
Network Policies¶
Restrict traffic between pods.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all
spec:
podSelector: {} # All pods
policyTypes:
- Ingress
ingress: [] # No ingress allowed by default
Allow specific traffic:
YAML example
Patterns¶
Sidecar Pattern¶
YAML example
Both containers share:
- Network namespace (same IP)
- Storage volumes
- Can communicate via localhost
Use cases:
- Logging sidecar (collects and forwards logs)
- Metrics sidecar (exposes metrics)
- Security sidecar (encryption proxy)
Interview Questions¶
Q: What's the difference between ClusterIP, NodePort, and LoadBalancer?
A: ClusterIP = internal only. NodePort = expose on every node at
Q: Why use Ingress instead of LoadBalancer?
A: Ingress is more efficient (single LB for all services) and supports hostname-based routing. LoadBalancer creates separate LB per service (expensive).
Q: What's ephemeral storage and why use PersistentVolume?
A: Ephemeral = lost when pod restarts. PersistentVolume = survives pod restarts (for databases, caches, etc.).
Key Takeaways¶
✅ Services provide stable endpoints for pods
✅ Ingress for HTTP load balancing and routing
✅ PersistentVolume/Claim for persistent storage
✅ ConfigMap for non-sensitive config, Secret for sensitive data
✅ Sidecar pattern for cross-cutting concerns
✅ Network policies restrict pod-to-pod traffic
Next Steps¶
- Read: Theory 06: Helm
- Do: Lab 04: Services & Discovery