Crisp answer: A Service is a stable virtual IP and DNS name that load-balances traffic to a set of pods selected by labels, regardless of where those pods are running or how many there are.
Why Services exist:
Pods are ephemeral. Their IPs change every time they are replaced. A Service provides a stable endpoint that other pods and external clients can use regardless of pod churn.
The four Service types:
ClusterIP (default):
Creates a virtual IP (VIP) that only works within the cluster. kube-proxy programs iptables/IPVS rules on every node to NAT packets destined for the ClusterIP to a real pod IP.
apiVersion: v1
kind: Service
metadata:
name: api
spec:
type: ClusterIP # default if omitted
selector:
app: api
ports:
- port: 80
targetPort: 8080
# DNS: api.default.svc.cluster.local → ClusterIP
# From any pod: curl http://api or curl http://api.default.svc.cluster.local
kubectl get svc api
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# api ClusterIP 10.96.45.12 <none> 80/TCP 5m
NodePort:
Opens a static port (30000-32767) on every node. Traffic to <any-node-IP>:<nodePort>
is forwarded to the Service. Useful for testing. Not for production — you'd
need to manage your own load balancer in front of the nodes.
LoadBalancer:
On EKS/AWS, this provisions an AWS Network Load Balancer (NLB) or Classic ELB automatically. The external IP of the LB is exposed as the Service's EXTERNAL-IP.
kubectl get svc my-service
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
# my-service LoadBalancer 10.96.1.1 a1b2c3d4.eu-west-2.elb.amazonaws.com 80:32456/TCP
This is what most production workloads on EKS use for external exposure. Each LoadBalancer Service creates a new AWS load balancer — expensive if you have many services. Use an Ingress controller (one LB for many services) instead.
ExternalName:
Maps a Service to a DNS name outside the cluster. No proxying — just a CNAME.
spec:
type: ExternalName
externalName: rds.eu-west-2.amazonaws.com
Headless Service:
clusterIP: None — no VIP, DNS returns individual pod IPs. Used by
StatefulSets and service meshes for peer discovery.
How kube-proxy implements Services:
# See iptables rules for a Service:
iptables -t nat -L KUBE-SERVICES -n | grep <service-IP>
# The chain KUBE-SVC-XXXX contains rules to probabilistically forward
# to each endpoint (KUBE-SEP-XXXX chains)
# On EKS with IPVS mode:
ipvsadm -ln
Endpoints:
A Service has an Endpoints (or EndpointSlice) object that lists the pod IPs. When pods are added/removed, kube-proxy updates the iptables rules.
kubectl get endpoints api
# NAME ENDPOINTS AGE
# api 10.244.1.5:8080,10.244.2.3:8080 5m
# If Endpoints shows <none>, the selector doesn't match any pods:
kubectl get pods --show-labels | grep app=api
What to say in the interview:
"A Service gives a stable ClusterIP and DNS name that survives pod churn. kube-proxy programs iptables rules on every node to DNAT packets to the ClusterIP into real pod IPs. The four types: ClusterIP for internal traffic, NodePort for opening a port on every node, LoadBalancer for provisioning an AWS NLB on EKS, and ExternalName for a DNS alias to something outside the cluster. If a Service isn't routing, the first thing I check is the Endpoints object — if it's empty, the selector doesn't match any pods. If Endpoints look right, I check iptables rules or whether kube-proxy is running."