Pod IPs change constantly; Services are how anything reliably reaches your app.
A Service selects pods by label and gives them a stable address. Clients connect to the Service; traffic is spread over the matching pods that are ready. The list of backends is kept in EndpointSlice objects, updated as pods come and go.
Types:
ClusterIP(default): a virtual IP reachable only inside the cluster. The workhorse for service-to-service calls.NodePort: also opens a port (30000-32767) on every node, forwarding to the Service.LoadBalancer: also asks the cloud for an external load balancer pointing at the nodes.ExternalName: a DNS CNAME to an outside name; no proxying.
Ports: port is what clients use on the Service, targetPort is the container port (a number or a named port), and nodePort the node port for NodePort/LoadBalancer.
A headless Service (clusterIP: None) has no virtual IP; its DNS name returns the pod IPs directly, which StatefulSets and client-side load balancers use.
For HTTP from outside the cluster, you usually don't give every service a LoadBalancer: you put an Ingress or Gateway in front of ClusterIP Services.
kubectl port-forward svc/web 8080:80 tunnels to a Service from your laptop, perfect for testing.
Example
apiVersion: v1
kind: Service
metadata:
name: web
spec:
type: ClusterIP
selector:
app: web
ports:
- name: http
port: 80 # clients use web:80
targetPort: 8080 # the containers listen on 8080
# kubectl apply -f svc.yaml
# kubectl get endpointslices -l kubernetes.io/service-name=web
# kubectl run tmp --rm -it --image=busybox:1.37 -- wget -qO- http://web
# kubectl port-forward svc/web 8080:80 # then open http://localhost:8080Remember
- A Service = stable virtual IP + DNS name over label-selected ready pods
- ClusterIP inside, NodePort/LoadBalancer to expose
- port is the Service's port, targetPort the container's
Watch out for
- A selector that doesn't match the pod labels: the Service exists but has no endpoints.