Ingress controllers and Ingress resources

How an Ingress routes web traffic by host and path, how pathTypes match, and how to serve HTTPS with a Secret.

40 min 2 hands-on tasksNetworking · 20% of the exam

Key ideas in this lesson · tap one for a simple explanation

What you will be able to do

  • Tell the Ingress controller, the IngressClass and the Ingress apart, and find all three on a cluster.
  • Write host and path rules with the right pathType, with kubectl or in YAML, and test them with curl.
  • Turn on HTTPS for an Ingress with a kubernetes.io/tls Secret, and check which certificate is served.

A works inside the cluster. An lets web traffic in from outside, and sends each request to the right Service by its host name and path. For example, shop.local/api goes to Service shop-api, and every other path to shop-web. The exam topic is Know how to use Ingress controllers and Ingress resources.

A typical task gives you two Services and asks for one Ingress, sometimes with HTTPS. The is short. Points are lost on small details:

  • a missing ingressClassName
  • Exact where Prefix was meant
  • a backend port that the Service does not have

Controller, class and Ingress: three names, three jobs

Three things work together here, and their names look alike:

NameWhat it isIn this lab
Ingress controllerA program that receives the web traffic and does the routing. It runs as a , with its own Service., in ingress-nginx
IngressClassA small object that names a controller, in spec.controller. It links Ingresses to that controller.nginx
IngressYour routing rules: which host and path go to which Service. It is only configuration.shop, in the first task below

An Ingress picks its class in spec.ingressClassName. Without a running controller, an Ingress routes nothing at all. Other controllers you may meet are Traefik, HAProxy and the cloud providers' own.

A request through an Ingress

Client

asks for shop.local/api

Ingress controller

ingress-nginx; checks the host and path against the Ingress rules

Service

shop-api:80

Pods

a shop-api Pod answers

The Ingress object only holds the rules. The controller reads them, and sends each request to the matching Service's Pods.

Find the controller on any cluster
kubectl get ingressclass
# NAME    CONTROLLER             PARAMETERS   AGE
# nginx   k8s.io/ingress-nginx   <none>       1h
kubectl get ingressclass -o yaml | grep is-default-class     # a default class is used when ingressClassName is omitted
kubectl -n ingress-nginx get deploy,pods,svc -o wide

# Lab: the controller runs on cka-control-plane with hostPorts 80/443,
# which kind maps to localhost:8080 and localhost:8443 on your machine.
curl -s -o /dev/null -w '%{http_code}\n' localhost:8080/      # 404 from the controller's default backend

# No default class and no ingressClassName: no controller claims the Ingress.
# It just sits there with an empty ADDRESS column and no events.

What an Ingress looks like

This Ingress sends shop.local/api to shop-api, and every other path on shop.local to shop-web:

One host, two paths
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: shop
  namespace: nw-ing
spec:
  ingressClassName: nginx
  # defaultBackend: (optional) a Service for requests that match no rule
  rules:
    - host: shop.local         # omit host to match every host; "*.shop.local" matches one label
      http:
        paths:
          - path: /api
            pathType: Prefix
            backend:
              service:
                name: shop-api
                port:
                  number: 80   # or  name: http  for a named Service port
          - path: /
            pathType: Prefix
            backend:
              service:
                name: shop-web
                port:
                  number: 80
pathTypeMatchesExample: path /api
ExactOnly this exact path. Upper and lower case count./api only. Not /api/ or /api/v1.
PrefixThis path and anything below it, compared one /-separated part at a time/api, /api/, /api/v1. Not /apiv1.
ImplementationSpecificWhatever the controller decidesAvoid it unless the question asks for it

When several paths match, the longest one wins. At equal length, Exact beats Prefix. That is why / with Prefix works as a catch-all next to /api. Also, the backend Service must be in the same namespace as the Ingress. Its port is the Service's port, not the 's targetPort.

kubectl create ingress

kubectl create ingress builds an Ingress in one line. Each --rule reads host/path=service:port.

Terminal
# --rule="host/path=service:port[,tls=secret]"; a trailing * means Prefix, no * means Exact
kubectl -n nw-ing create ingress shop --class=nginx \
  --rule="shop.local/*=shop-web:80" \
  --rule="shop.local/api*=shop-api:80"

kubectl -n nw-ing create ingress shop --class=nginx --rule="shop.local/api=shop-api:80"   # Exact /api only!
kubectl -n nw-ing create ingress secure --class=nginx --rule="secure.shop.local/*=shop-web:80,tls=secure-tls"

# Always look at what you generated before applying it
kubectl -n nw-ing create ingress shop --class=nginx --rule="shop.local/*=shop-web:80" --dry-run=client -o yaml

TLS: serving HTTPS

With TLS, the controller serves HTTPS for a host. It needs that host's certificate and private key. You store both in a of type kubernetes.io/tls, and the Ingress names that Secret in a tls list:

The tls part of an Ingress
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - secure.shop.local
      secretName: secure-tls   # type kubernetes.io/tls, same namespace as the Ingress
  rules:
    - host: secure.shop.local  # the same host name as under tls

To try it, make a certificate, store it in a Secret, then check which certificate the controller serves:

Terminal
# A self-signed certificate for one host (use the files you are given on the exam)
openssl req -x509 -nodes -newkey rsa:2048 -days 365 \
  -keyout tls.key -out tls.crt -subj "/CN=secure.shop.local" -addext "subjectAltName=DNS:secure.shop.local"

kubectl -n nw-tls create secret tls secure-tls --cert=tls.crt --key=tls.key   # type kubernetes.io/tls
kubectl -n nw-tls get secret secure-tls -o jsonpath='{.type}{"\n"}'

# Test without touching /etc/hosts: --resolve pins the name to the lab's port
curl -sk --resolve secure.shop.local:8443:127.0.0.1 https://secure.shop.local:8443/hostname
# Which certificate is served? A wrong or missing Secret gives the controller's fake certificate
echo | openssl s_client -connect 127.0.0.1:8443 -servername secure.shop.local 2>/dev/null | openssl x509 -noout -subject
Where HTTPS ends

Client

HTTPS to secure.shop.local

Ingress controller

decrypts with the certificate from Secret secure-tls

Service and Pods

get plain HTTP

"Terminating TLS" means the encryption ends at the controller. Your app never needs the certificate.

  • Once a host has TLS, ingress-nginx answers plain HTTP for it with a 308 redirect to HTTPS. So a curl on port 8080 suddenly returns nothing useful.
  • The Secret must hold tls.crt and tls.key, and live in the Ingress's namespace.
  • If the certificate's names do not cover the host, the controller ignores it and serves its own fake certificate.

When an Ingress does not route

The HTTP status code tells you where to look. A 404 from nginx means no rule matched. A 503 means a rule matched, but the Service has no ready .

When an Ingress does not route
kubectl -n nw-ing describe ingress shop          # rules, backends with their endpoints, events
kubectl -n nw-ing get endpointslices -l kubernetes.io/service-name=shop-api   # no endpoints = 503
curl -s -H 'Host: shop.local' localhost:8080/api/orders                       # Host header selects the rule
kubectl -n ingress-nginx logs deploy/ingress-nginx-controller --tail=20       # one access-log line per request
# 404 from nginx: no rule matched (wrong host, Exact vs Prefix, missing class)
# 503: a rule matched, but the Service has no ready endpoints

In the official docs· open on exam day too

Hands-on task 1 of 2nw-ing-1
~6 min6% weightNetworking

Route one host to two Services by path

In namespace nw-ing, create an Ingress named shop using IngressClass nginx for host shop.local. Requests for /api and every path below it must go to Service shop-api on port 80; all other paths must go to Service shop-web on port 80. On this lab, curl -s -H 'Host: shop.local' localhost:8080/hostname must be answered by a shop-web Pod and curl -s -H 'Host: shop.local' localhost:8080/api/orders by a shop-api Pod.

Cluster:kubectl config use-context kind-cka

  1. 1

    Set up the scenario

    curl -fsSL https://codewithmuh.com/labs/cka/t/nw-ing-1/setup | bash
  2. 2

    Solve it in your terminal

    Use kubectl and the Kubernetes docs, like on exam day. Save any answer files under /tmp/cka/.

  3. 3

    Check your work

    curl -fsSL https://codewithmuh.com/labs/cka/t/nw-ing-1/verify | bash
    Got FAIL? Read which check failed, fix it, run it again.
  4. 4

    Clean up (optional)

    curl -fsSL https://codewithmuh.com/labs/cka/t/nw-ing-1/cleanup | bash
Hands-on task 2 of 2nw-ing-2
~6 min5% weightNetworking

Terminate TLS on an Ingress

A certificate and key for secure.shop.local are in /tmp/cka/nw-tls/tls.crt and /tmp/cka/nw-tls/tls.key. In namespace nw-tls, store them in a TLS Secret named secure-tls. Then create an Ingress named secure with IngressClass nginx that routes all paths of host secure.shop.local to Service secure-web on port 80 and terminates TLS for that host with the Secret. On this lab, https://secure.shop.local:8443 (resolved to 127.0.0.1) must serve that certificate.

Cluster:kubectl config use-context kind-cka

  1. 1

    Set up the scenario

    curl -fsSL https://codewithmuh.com/labs/cka/t/nw-ing-2/setup | bash
  2. 2

    Solve it in your terminal

    Use kubectl and the Kubernetes docs, like on exam day. Save any answer files under /tmp/cka/.

  3. 3

    Check your work

    curl -fsSL https://codewithmuh.com/labs/cka/t/nw-ing-2/verify | bash
    Got FAIL? Read which check failed, fix it, run it again.
  4. 4

    Clean up (optional)

    curl -fsSL https://codewithmuh.com/labs/cka/t/nw-ing-2/cleanup | bash

Finished this lesson?

0 of 2 tasks passed. Your readiness counts tasks, so run them before you move on.