TT Lab
Get started
Learn Learning paths Courses

CKAD — Kubernetes Application Developer

Service, Ingress and NetworkPolicy

Continue in TT Lab

Goal

You specify the three kinds of Service ports distinctly, and write named ports, headless Services, StatefulSet DNS, Ingress routing, and a NetworkPolicy allowlist as manifests.

Why it matters

Nearly all the mistakes that come up with Services are one of two: a selector that differs from the Pod labels, and mixing up the three ports. When the selector is off, the endpoints are empty without any error and the client sees only connection refused. The single habit of checking whether kubectl get endpoints is empty saves most of your time.

If you remember ports by direction, you will not mix them up. port is the incoming side (the port a client connects to on the Service), targetPort is the outgoing side (the port the Service connects to on the Pod), and nodePort is the side that comes in from outside (the port opened on the node).

A headless Service is a choice to give up load balancing. It creates no virtual IP and has DNS return the list of Pod IPs as is, and paired with a StatefulSet, each Pod gets a unique name. Use it for workloads where a client has to name a specific instance (a DB primary, a shard).

A NetworkPolicy is summarized in two sentences: allow by default, and an allowlist once a policy applies. That is why in practice you first apply a default deny to the whole namespace and then open only the communication you need. Also remember that policies do not override one another; they add up as a union.

Steps

  1. Create the namespace ckad-net. Create a Deployment frontend (2 replicas, label app=frontend, image nginx:1.27, container port 80 named http) and a Service frontend (ClusterIP, port: 80, targetPort: 80, selector app=frontend).
  2. Create a Deployment backend (2 replicas, label app=backend, image nginx:1.27, container port 8080 named api) and a Service backend (ClusterIP, port: 80, targetPort: api — by name, selector app=backend).
  3. Create a Service frontend-np. Type NodePort, port: 80, targetPort: 80, nodePort: 30080, selector app=frontend.
  4. Create a headless Service cache-hs (clusterIP: None, port: 6379, selector app=cache) and a StatefulSet cache (3 replicas, serviceName: cache-hs, label app=cache, image nginx:1.27). Then write the fully qualified DNS name of Pod 0 on one line in /root/ckad-net/dns.txt.
  5. Create an Ingress shop. ingressClassName: nginx, host shop.ckad.local, path / (pathType Prefix) → Service frontend port 80, path /api (pathType Prefix) → Service backend port 80.
  6. Create a NetworkPolicy backend-allow-frontend. The target is the app=backend Pods, policyTypes: [Ingress], the allowed source is the app=frontend Pods, and the port is TCP 8080.
  7. Create a NetworkPolicy default-deny-ingress. podSelector: {} (the whole namespace), policyTypes: [Ingress], and leave the ingress rules empty. At this point, check with kubectl get pods -n ckad-net -l app=backend that the Service backend's selector actually picks at least 2 Pods.

Notes

Deployment and ClusterIP Service

Create the namespace ckad-net. Create a Deployment frontend (2 replicas, label app=frontend, image nginx:1.27, container port 80 named http) and a Service frontend (ClusterIP, port: 80, targetPort: 80, selector app=frontend).

Use kubectl expose deployment or write the Service directly. The endpoints attach only if the Service's selector exactly matches the Pod labels. If you name the container port now, the next step is easier.

Connecting targetPort with a named port

Create a Deployment backend (2 replicas, label app=backend, image nginx:1.27, container port 8080 named api) and a Service backend (ClusterIP, port: 80, targetPort: api — by name, selector app=backend).

Write the name you gave in the container's ports[].name as a string in the Service's targetPort. The name must be at most 15 characters of lowercase letters, digits, and hyphens.

Opening to the outside with NodePort

Create a Service frontend-np. Type NodePort, port: 80, targetPort: 80, nodePort: 30080, selector app=frontend.

Set the type to NodePort and specify nodePort yourself. The allowed range is 30000–32767, and if you do not specify it, one is assigned automatically. port and targetPort stay as they are.

Headless Service and StatefulSet

Create a headless Service cache-hs (clusterIP: None, port: 6379, selector app=cache) and a StatefulSet cache (3 replicas, serviceName: cache-hs, label app=cache, image nginx:1.27). Then write the fully qualified DNS name of Pod 0 on one line in /root/ckad-net/dns.txt.

A headless Service is clusterIP: None. You have to write that Service's name in the StatefulSet's serviceName for per-Pod DNS names to exist. The naming rule puts dots in the order Pod name, Service name, namespace.

Host and path routing with an Ingress

Create an Ingress shop. ingressClassName: nginx, host shop.ckad.local, path / (pathType Prefix) → Service frontend port 80, path /api (pathType Prefix) → Service backend port 80.

It is networking.k8s.io/v1, and the backend is specified with service.name and service.port.number. pathType is required on each path. The rules have the structure rules[].host and rules[].http.paths[].

Allowing only the frontend with a NetworkPolicy

Create a NetworkPolicy backend-allow-frontend. The target is the app=backend Pods, policyTypes: [Ingress], the allowed source is the app=frontend Pods, and the port is TCP 8080.

podSelector points to the target the policy applies to, and ingress[].from[].podSelector points to the allowed source. It is easy to swap the two, so be careful. Narrow the port as well.

Default deny policy and selector check (comprehensive)

Create a NetworkPolicy default-deny-ingress. podSelector: {} (the whole namespace), policyTypes: [Ingress], and leave the ingress rules empty. At this point, check with kubectl get pods -n ckad-net -l app=backend that the Service backend's selector actually picks at least 2 Pods.

An empty podSelector: {} means all Pods in the namespace. If you put no ingress rules at all on it, it becomes a default deny. And check directly with kubectl get pods -l that the Service selector actually picks real Pods.