Skip to content

Ingresses

A Service exposes one thing. An Ingress routes HTTP traffic to many things by hostname and path, and terminates TLS — which is what you actually want in front of a handful of web applications.

The crucial thing to know first: an Ingress does nothing on its own. It is a set of rules. Something has to read those rules and implement them, and that something is an ingress controller.

Reference

Terminology

  • Ingress controller — The component that watches Ingress objects and configures a real proxy. NGINX, Traefik, HAProxy, Contour, and cloud-specific ones. It runs as a Deployment and usually gets traffic through a Service of type LoadBalancer or NodePort.
  • ingressClassName — Which controller should implement this Ingress. With several installed, this is how you choose. A cluster normally marks one as the default.
  • Rule — A host and a set of paths, each pointing at a Service port. An Ingress with no rules is legal and does nothing useful.
  • pathTypePrefix (matches on path segments), Exact, or ImplementationSpecific. Omit it and behaviour is controller-dependent, which is a bug waiting to happen.
  • Default backend — Where requests go when nothing matches. Without one, unmatched requests get a 404 from the controller.
  • TLS block — Names a Secret containing a certificate and key. The controller terminates TLS; the backend gets plain HTTP.

The Shape of It

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  ingressClassName: nginx
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web
                port:
                  number: 80
  tls:
    - hosts: [app.example.com]
      secretName: web-tls

Note what is not here: no port on the ingress controller, no load balancer configuration, no certificate. All of that belongs to the controller and the annotation on the object.

Exercises

  1. Find out whether your cluster has a controller. Without one, an Ingress is inert:

    kubectl get ingressclass
    kubectl get pods -A | grep -i -E 'ingress|nginx|traefik'

    In your own cluster, you may need to install one:

    helm upgrade --install ingress-nginx ingress-nginx \
      --repo https://kubernetes.github.io/ingress-nginx \
      --namespace ingress-nginx --create-namespace
  2. Route by hostname, and prove it from outside. Create a Deployment, a Service, and an Ingress for web.example.com, then reach it through the controller. On a cluster with no external address, port-forward the controller itself and set the Host header:

    kubectl port-forward -n ingress-nginx svc/ingress-nginx-controller 8080:80
    curl -H 'Host: web.example.com' http://localhost:8080/

    Then delete the host: line and see it match anything. A rule with no host is a catch-all, which is convenient in a lab and dangerous in production.

  3. Compare Prefix and Exact. Create paths for /api with each type and request /api/v1. Prefix matches it; Exact does not. This is the difference between routing working and a confusing 404.

  4. Look at the TLS path. On the shared cluster, the controller is NGINX with cert-manager, and certificates are obtained automatically from a ClusterIssuer:

    kubectl get certificate -A
    kubectl get secret web-tls -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -subject -issuer -dates

    The Secret named in the Ingress is created for you by cert-manager. Nothing in the Ingress manifest contains a certificate.

Ingress or Gateway API?

The Gateway API is the intended successor, and this course uses it for comparison rather than as the default. The difference is mainly about roles:

IngressGateway API
Objectsone, with controller-specific annotationsGatewayClass, Gateway, HTTPRoute — separate objects
Who owns whatone object for everyoneinfrastructure team owns the Gateway, application teams own routes
ProtocolsHTTP and TLS terminationHTTP, TLS, TCP, UDP, gRPC, and traffic weighting
Annotationshow most real behaviour is expressedexpressed in the spec

If your cluster has the Gateway API CRDs installed, create an HTTPRoute for the same backend and compare. The routing rules are more verbose and far more explicit, and there is no reliance on annotations a different controller would ignore.

Gotchas Worth Knowing

  • An Ingress with no controller does nothing, silently. This is the most common cause of “my Ingress is not working” and it produces no events, because the object itself is valid.
  • Annotations are controller-specific. nginx.ingress.kubernetes.io/rewrite-target means nothing to Traefik. Moving clusters means revisiting them.
  • The backend Service must be ClusterIP. The controller is the thing facing the world; pointing at a NodePort or LoadBalancer Service adds a hop and confuses debugging.
  • pathType is not optional in practice. Omitting it makes matching behaviour depend on the controller’s implementation.
  • The Ingress does not create the Service or the certificate. It only references them, and a broken reference shows up as a 503 from the controller, not as an error on the Ingress.

What to Take Away

  • An Ingress is a rule set; the controller is what makes it real.
  • ingressClassName selects the controller, and the rules route by host and path.
  • TLS comes from a Secret that something else — usually cert-manager — maintains.
  • If an Ingress appears to do nothing, check that a controller exists before anything else.