Skip to content
CKAD: Certified Kubernetes Application Developer

Services and Networking

Last verified against its sources on 23 September 2026

Services and Networking closes out the CKAD exam at 20%: giving a set of Pods a stable address, routing HTTP traffic to the right one by host and path, and restricting which traffic is allowed at all.

The throughline across all three lessons is the same: a Kubernetes networking object is a declaration of intent, and something else has to actually be running — kube-proxy, an Ingress Controller, a CNI plugin — to turn that declaration into real traffic behavior.

Exposing Applications with Services

  • Choose the right Service type — ClusterIP, NodePort, LoadBalancer or headless — for how an application needs to be reached.
  • Troubleshoot a Service that isn't routing traffic to its Pods.

A Service gives a stable virtual IP and DNS name to a set of Pods selected by label, so clients never track individual Pod addresses that change on every restart or reschedule. The control plane watches for Pods matching a Service's spec.selector and records them — as of Kubernetes v1.33, the classic v1 Endpoints object is officially deprecated in favor of discovery.k8s.io/v1 EndpointSlice, stable since v1.21; the API server now warns on kubectl get endpoints.

Four Service types build on each other. ClusterIP, the default, is reachable only inside the cluster. NodePort builds on ClusterIP by additionally opening the same port on every node. LoadBalancer builds on NodePort by provisioning an external cloud load balancer in front of it. ExternalName is different in kind — no selector, no proxying, just a DNS CNAME alias to an external name.

bash

apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 8080
A ClusterIP Service selecting Pods by label.

A headless Service (clusterIP: None) skips the shared virtual IP entirely: DNS returns each matching Pod's own IP individually instead of one load-balanced address — the fit when clients need to reach a specific replica directly, such as one member of a StatefulSet, rather than whichever Pod a Service happens to route to.

When a Service routes to nothing, the most common cause is a selector that no longer matches any Pod's labels — a typo, or a label that changed on one side without the other. kubectl get endpointslices (or the deprecated kubectl get endpoints) for that Service shows the mismatch directly: zero entries despite Pods visibly Running.

A Service shows zero endpoints even though its Pods are Running. What's the most common cause?Answer it yourself first, then open this.

The Service's selector doesn't match the Pods' current labels — check both sides for a typo or a recent label change.

Routing Traffic with Ingress

  • Write Ingress rules that route HTTP traffic by host and path to the correct Service.
  • Explain why an Ingress resource does nothing on its own without a controller.

An Ingress (networking.k8s.io/v1) describes HTTP(S) routing rules — host and path combinations mapped to backend Services — but on its own it's just data, the same way a CRD is inert without a controller. Nothing in the core API server actually routes traffic for it. An Ingress Controller (nginx, and many others) has to be running in the cluster, watching Ingress objects and provisioning the real reverse proxy or load balancer that does the routing. An IngressClass names which controller a given Ingress should be handled by.

bash

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-ingress
spec:
  ingressClassName: nginx
  rules:
    - host: shop.example.com
      http:
        paths:
          - path: /api
            pathType: Prefix
            backend:
              service:
                name: api
                port:
                  number: 80
          - path: /
            pathType: Prefix
            backend:
              service:
                name: storefront
                port:
                  number: 80
One Ingress, two paths, one Service each.

pathType has three values, since Kubernetes v1.18. Prefix matches a path segment by segment. Exact matches the request path exactly, case-sensitively, with nothing appended. ImplementationSpecific is the default if pathType is left unset — its actual matching behavior depends entirely on whichever controller is running, not on a fixed Kubernetes-wide rule.

One Ingress, behind one controller, can route many different hostnames and paths to many different backend Services — the usual reason to use Ingress at all instead of a separate LoadBalancer Service per application. A tls section can also list which Secret holds the certificate for a given host, so the controller terminates HTTPS at the edge before forwarding plain HTTP internally, and a defaultBackend can catch any request that matches none of the declared rules.

The Ingress object only supplies rules; the controller is what actually routes.
An Ingress object is created, but no Ingress Controller is running in the cluster. Does traffic get routed?Answer it yourself first, then open this.

No — an Ingress only supplies routing rules; without a running controller to read and act on them, nothing actually routes traffic.

Restricting Traffic with NetworkPolicies

  • Write a NetworkPolicy that restricts a Pod's traffic to only what's explicitly allowed.
  • Explain why a NetworkPolicy might have no effect at all in some clusters.

A NetworkPolicy (networking.k8s.io/v1) describes allowed traffic to and from Pods matched by a podSelector. But Kubernetes itself does not enforce it — that's the job of the cluster's CNI network plugin. If the CNI in use doesn't implement NetworkPolicy, every NetworkPolicy object in the cluster is accepted and stored, but silently has no effect at all; traffic keeps flowing as if none existed.

The other default worth knowing: a Pod that no NetworkPolicy selects at all is fully open — Kubernetes' baseline is allow-all, not deny-all, until some policy specifically targets that Pod.

bash

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all-ingress
spec:
  podSelector: {}
  policyTypes: ["Ingress"]
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-from-frontend
spec:
  podSelector:
    matchLabels:
      app: api
  policyTypes: ["Ingress"]
  ingress:
    - from:
        - podSelector:
            matchLabels:
              role: frontend
Default-deny for ingress, then one explicit allow.

policyTypes (Ingress, Egress, or both) decides which directions a policy actually restricts for the Pods it selects. Once any NetworkPolicy selects a Pod for a given direction, that direction becomes default-deny for that Pod — only traffic matching an allow rule in some applicable policy gets through. Multiple policies selecting the same Pod are additive: the union of everything they each allow, never a stricter intersection. An ingress[].from entry can match by podSelector, by namespaceSelector to allow an entire namespace, or by ipBlock for a CIDR range outside the cluster; egress rules use the same building blocks in the outbound direction, including reaching external IPs.

A policy selects Pod X with policyTypes Ingress and an empty ingress list (no rules at all). What happens to incoming traffic to Pod X?Answer it yourself first, then open this.

All of it is denied — selecting a Pod for Ingress with zero allow rules means nothing is permitted in that direction.

Sources