Services: ClusterIP, NodePort, LoadBalancer and endpoints
How a Service turns a moving set of Pod IPs into one stable address, what each Service type adds, and how to read EndpointSlices when traffic goes nowhere.
After this lesson
- 01You can expose a Deployment as a ClusterIP or NodePort Service with the right port, targetPort and nodePort in one command.
- 02You can explain what LoadBalancer, ExternalName and headless Services do, and what each looks like on a cluster without a cloud provider.
- 03You can diagnose a Service with no endpoints from its selector, the Pod labels and its EndpointSlices, and fix it without touching the workload.
This lesson covers Use ClusterIP, NodePort, LoadBalancer service types and endpoints. Expect at least one task that says "expose" and at least one troubleshooting task where a Service exists but nothing answers. Both are fast if you know three things: the selector decides which Pods are endpoints, targetPort must match what the container listens on, and EndpointSlices tell you which of those two is wrong.
What a Service is
A Service is a stable virtual IP (the ClusterIP, from the Service CIDR) plus a DNS name, in front of the set of Pods its selector matches. The EndpointSlice controller watches Pods and writes the IPs of the ready matching ones into EndpointSlices. kube-proxy on every node watches those slices and programs rules so that a connection to ClusterIP:port is rewritten to one PodIP:targetPort. No process listens on the ClusterIP, which is why you cannot ping it.
apiVersion: v1
kind: Service
metadata:
name: api
namespace: nw-svc
spec:
type: ClusterIP # default; NodePort and LoadBalancer build on it
selector:
app: api # must match the Pod template labels, not the Deployment's own labels
ports:
- name: http # required once a Service has more than one port
protocol: TCP
port: 80 # what clients connect to on the ClusterIP
targetPort: 8080 # container port; a number or a named containerPort such as "http"
sessionAffinity: None # ClientIP pins each client IP to one Pod (default timeout 10800s)A named targetPort refers to ports[].name in the container spec. That lets different Pods behind one Service listen on different numbers, and lets you change the container port without editing the Service. If targetPort is omitted it defaults to port, which is a classic source of "connection refused".
The Service types
LoadBalancer deserves a sentence of its own. Kubernetes only records the request. A cloud provider's controller (AWS, GCP, Azure, …) or an add-on such as MetalLB or cloud-provider-kind sees the Service, creates the load balancer, points it at the node ports, and fills in the status. Without one, the Service still works as a NodePort, which is exactly what you see for ingress-nginx-controller in this lab.
Imperative shortcuts
# expose reads the Deployment's selector, so the endpoints are right by construction
kubectl -n nw-svc expose deploy api --name=api-svc --port=80 --target-port=8080
kubectl -n nw-svc expose deploy api --name=api-np --type=NodePort --port=80 --target-port=8080
# there is no --node-port flag on expose; set it afterwards:
kubectl -n nw-svc patch svc api-np --type=json -p='[{"op":"replace","path":"/spec/ports/0/nodePort","value":30080}]'
# create service sets the selector to app=<service-name>; correct it if your Pods differ
kubectl -n nw-svc create service clusterip api --tcp=80:8080
kubectl -n nw-svc create service nodeport api --tcp=80:8080 --node-port=30080
kubectl -n nw-svc create service externalname docs --external-name=kubernetes.io
kubectl -n nw-svc create service clusterip db --clusterip=None --tcp=5432:5432 # headless
# A Pod can be exposed too (it uses the Pod's labels as the selector)
kubectl -n nw-svc expose pod web --port=80 --target-port=8080 --name=webEndpoints and EndpointSlices
EndpointSlices (discovery.k8s.io/v1) are the source of truth. Each slice is labeled kubernetes.io/service-name=<service> and lists addresses with ready, serving and terminating conditions, the node and the Pod they belong to. The older v1 Endpoints object is still mirrored, but the API server now prints a deprecation warning when you read it. Use slices in scripts; either works for a quick look.
kubectl -n nw-svc get endpointslices -l kubernetes.io/service-name=api-svc
# NAME ADDRESSTYPE PORTS ENDPOINTS AGE
# api-svc-x7k2p IPv4 8080 192.168.245.41,192.168.220.43 10s
kubectl -n nw-svc get endpointslices -l kubernetes.io/service-name=api-svc \
-o jsonpath='{range .items[*].endpoints[*]}{.addresses[0]}{" ready="}{.conditions.ready}{" "}{.targetRef.name}{"\n"}{end}'
kubectl -n nw-svc describe svc api-svc | grep -E 'Selector|TargetPort|Endpoints'
kubectl -n nw-svc get endpoints api-svc # works, with "v1 Endpoints is deprecated" warningA Service whose selector matches nothing looks perfectly healthy in kubectl get svc. The tells are elsewhere: describe shows an empty Endpoints: line, the EndpointSlice shows <unset> for ports and endpoints, and clients get connection refused immediately because kube-proxy rejects traffic to a Service with no backends. Pods that match but are not Ready show up in the slice with ready=false and receive no traffic.
kubectl -n nw-svc get svc orders -o jsonpath='{.spec.selector}{"\n"}'
kubectl -n nw-svc get pods --show-labels
kubectl -n nw-svc get pods -l app=orders,tier=backend # paste the selector here; empty means mismatch
kubectl -n nw-svc get endpointslices -l kubernetes.io/service-name=orders
# Fix the Service, not the Pods, unless the question says otherwise
kubectl -n nw-svc set selector svc orders app=orders,tier=backend
# or: kubectl -n nw-svc patch svc orders -p '{"spec":{"selector":{"app":"orders"}}}'In the docs · the tab you get on exam day
- Service ↗search: “service nodeport targetport”
- EndpointSlices ↗search: “endpointslice”
- Connecting Applications with Services ↗search: “connecting applications with services”
- kubectl expose ↗search: “kubectl expose”
context · kubectl config use-context kind-cka
Expose a Deployment inside the cluster
nw-svc, the Deployment api serves HTTP on container port 8080. Create a Service named api-svc of type ClusterIP that exposes it on port 80 and forwards to port 8080 of the Pods. A request from Pod client to http://api-svc/hostname must return the name of an api Pod.- 01
Set up the scenario
curl -fsSL https://codewithmuh.com/labs/cka/t/nw-svc-1/setup | bash - 02
Solve it in your terminal
Work the way the exam makes you: kubectl, the docs, nothing else. Put any answer files under
/tmp/cka/. - 03
Check your work
curl -fsSL https://codewithmuh.com/labs/cka/t/nw-svc-1/verify | bashCounts toward your readiness score. - 04
Clean up
curl -fsSL https://codewithmuh.com/labs/cka/t/nw-svc-1/cleanup | bash
context · kubectl config use-context kind-cka
Publish a Deployment on a fixed NodePort
greeter in namespace nw-nodeport reachable from outside the cluster. Create a Service greeter-np of type NodePort with Service port 80, target port 8080 and node port 30080. A request to port 30080 of a node, for example curl -s localhost:30080/hostname from your machine on this lab, must return the name of a greeter Pod.- 01
Set up the scenario
curl -fsSL https://codewithmuh.com/labs/cka/t/nw-svc-2/setup | bash - 02
Solve it in your terminal
Work the way the exam makes you: kubectl, the docs, nothing else. Put any answer files under
/tmp/cka/. - 03
Check your work
curl -fsSL https://codewithmuh.com/labs/cka/t/nw-svc-2/verify | bashCounts toward your readiness score. - 04
Clean up
curl -fsSL https://codewithmuh.com/labs/cka/t/nw-svc-2/cleanup | bash
context · kubectl config use-context kind-cka
A Service that sends traffic nowhere
nw-svcfix, requests from Pod client to http://orders/hostname fail with connection refused, even though both orders Pods are Running and Ready. Find the cause and fix it so the Service orders delivers requests to the orders Pods. Do not modify the Deployment.- 01
Set up the scenario
curl -fsSL https://codewithmuh.com/labs/cka/t/nw-svc-3/setup | bash - 02
Solve it in your terminal
Work the way the exam makes you: kubectl, the docs, nothing else. Put any answer files under
/tmp/cka/. - 03
Check your work
curl -fsSL https://codewithmuh.com/labs/cka/t/nw-svc-3/verify | bashCounts toward your readiness score. - 04
Clean up
curl -fsSL https://codewithmuh.com/labs/cka/t/nw-svc-3/cleanup | bash
Finished reading?
0 of 3 tasks passed. Readiness counts tasks, so run them before you move on.