Ingress
The NodePort from the previous lesson works, but picture twenty applications: twenty random ports to document, no centralized TLS, no routes by name. Kubernetes's answer to incoming HTTP traffic is the Ingress: an object where you declare rules like "requests for host X go to Service Y", and an Ingress controller that makes them real.
The important detail: the Ingress object does nothing on its own, it needs a controller to interpret it. Here the playground plays in your favor: k3s ships Traefik as its Ingress controller out of the box, listening on port 80 of every node. Check it from the dev-machine tab:
kubectl get pods -n kube-system -l app.kubernetes.io/name=traefik
kubectl get ingressclass
That IngressClass called traefik (marked as default) is what will connect your rules with the controller.
Step 1: Backend and Service
An Ingress routes to Services, so first bring up the base. Again, with no guidance: create a Deployment web (1 replica of ghcr.io/iximiuz/labs/nginx:alpine, label app: web, port 80) and a ClusterIP Service web that selects it on port 80.
Hint: neither of the two pieces needs YAML
kubectl create knows how to create a Deployment and a Service directly, with the image, the port and the replicas as flags. And for the Service there is an even better shortcut: kubectl expose starts from an object that already exists and inherits its selector, which is exactly what you have to get right here.
Step 2: The Ingress
You can find the file at manifests/ingress.yaml:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web
spec:
rules:
- host: app.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80
The YAML, explained in questions and answers
Yet another API group?
Yes: networking.k8s.io/v1, the group of the networking resources (Ingress, NetworkPolicy). With this one you have seen the big four: core, apps, batch and networking.
What role does host play?
It is the condition of the rule: only HTTP requests whose Host header is app.local trigger it. That way a single entry point on port 80 can serve dozens of different applications, one per name.
What does pathType: Prefix mean?
How to compare the path with the URL of the request. Prefix matches / and everything hanging from it; the alternative, Exact, demands a literal match. Declaring it is mandatory.
Why is the backend a Service and not my Pods?
Because the Ingress delegates to the machinery you already know: the controller routes to the Service, and the Service spreads among its endpoints. Each piece does a single job.
Isn't it missing which controller it uses?
There is the ingressClassName field to choose it explicitly. We leave it out because k3s marks the traefik class as default; in a cluster with several controllers you would always declare it.
Apply it:
kubectl apply -f manifests/ingress.yaml
kubectl get ingress
Step 3: The test from outside
The host app.local does not exist in any DNS, so simulate what the browser would do by sending the header by hand. From dev-machine:
curl -H "Host: app.local" http://cplane-01/
And the negative test, which is where the Ingress makes sense: the same request without the right header.
curl -i http://cplane-01/
A 404 from Traefik. The controller receives everything that arrives on port 80, but only routes what matches some rule.
And to see it as a user would, open the app.local tab:
there is your application. That tab points at port 80 of cplane-01 and adds the
Host: app.local header for you, which is exactly what you just did by hand with curl -H.
Without that rewrite it would get the same 404 as the negative test.
Summary
- The Ingress declares HTTP rules (host and path toward a Service); the Ingress controller executes them.
- Without a controller, an Ingress is just a decorative object. k3s solves this by shipping Traefik out of the box.
- A single entry point on 80/443 replaces one NodePort per application, and it is where TLS gets centralized.
- The book also covers the evolution of this idea, the Gateway API; with what you learned here, reading it will feel natural.
- Previous lesson
- Exposing the application with a Service
- Next lesson
- Gateway API