Skip to content
Foxipa

Suggest an edit

Tell us what should be corrected, clarified, or improved. Submission will be connected to Foxipa’s contribution service later.

No information is sent anywhere yet. Your suggestion is only prepared in your browser.

Learning

14.3 The Ingress Model

Kubernetes didn’t invent new routing concepts — it renamed the ones you already know. The translation table: every Ingress object you’ll ever read, as the NGINX config it becomes.

Levels: Advanced ~30 min read

Advanced

What you will learn

  1. The cast: Service, Endpoints, pods, Ingress, controller.

  2. The translation table: k8s objects → NGINX directives.

  3. Readiness as membership: why "pod NotReady" is Lesson 13's endpoint removal, automated.

  4. Annotations as policy, and their honest limits.


1. The cast

A request entering a Kubernetes cluster meets this chain:

text
internet -> ingress controller's NGINX
              ^
              | watches & renders
           [ Ingress resource ]  <- your routing intent (host/path rules)
                    |
              points at a Service (a stable name)
                    |
              Endpoints/EndpointSlices (the CURRENTLY READY pod IPs)
                    |
              pods (your actual processes)

The magic — and it is the Module 13 story with new names — is the middle: the controller watches the Service's EndpointSlices and renders NGINX config whose upstream contains exactly the ready pod addresses. Nothing you learned changes; the pipeline is industrialized:

Kubernetes says

You learned it as

Service (ClusterIP/headless)

the stable name of Module 13's discovery contract

Endpoints / EndpointSlices

the resolved-address list behind the name

Ingress resource

a server{} + location{} policy, written as YAML

ingress controller

the manifest→config→reload loop of Lesson 14.4

readiness probe

the health gate that edits membership (Module 13's "DNS says exists" vs "actually serving", answered)

2. The translation table

An Ingress resource, annotated with its NGINX meaning:

yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: shop-routes
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"        # http->https server (Module 6)
    nginx.ingress.kubernetes.io/limit-rps: "100"            # limit_req zone (Module 7)
    nginx.ingress.kubernetes.io/proxy-body-size: "10m"      # client_max_body_size
spec:
  ingressClassName: nginx                                    # which controller owns this
  tls:
  - hosts: [shop.example.com]                               # ssl_certificate pair (Module 6)
    secretName: shop-tls                                    #   (cert = a k8s Secret, mounted)
  rules:
  - host: shop.example.com                                  # server_name (Module 2)
    http:
      paths:
      - path: /api/                                         # location (Module 2)
        pathType: Prefix
        backend:
          service:
            name: svc-api                                   # upstream svc-api (Module 5)
            port: { number: 8080 }
      - path: /                                             # location /
        pathType: Prefix
        backend:
          service:
            name: svc-web
            port: { number: 80 }

Read that top to bottom and you have written it before — TLS termination, host routing, path locations, upstream pools — every line a Module number. The controller's rendered config would contain, in order: a redirect server, the TLS server with ssl_certificate from the Secret, two locations proxying to two upstreams whose server lines are the current ready pods. Even the details translate: pathType: Prefix vs ImplementationSpecific is Module 2's prefix-vs-regex location choice in YAML clothing.

3. Readiness is membership

The single most important operational fact of the ingress model — and it is Module 13's failure-honesty lesson with a health layer on top:

A pod that flips NotReady disappears from EndpointSlices, the controller re-renders, NGINX reloads, and traffic stops reaching it — all within the controller's sync loop (seconds), with no human, no DNS TTL, and no stale cache.

Compare the shapes:

Module 13 (DNS)

k8s ingress

Who notices a dead pod

DNS record removal (external)

kubelet readiness → API → controller

Staleness bound

valid= / TTL

sync loop latency (~seconds)

Health semantics

none ("exists")

readiness ("serving")

Drain story

record disappears (hard cut)

terminationGracePeriod + preStop (real drain)

This is why Lesson 13.1 deferred the controller story to this module: they bolt health-gated membership onto the runtime-upstream pattern. (And why the mini-controller in Lesson 14.4 models readiness as editing the manifest — that edit IS the EndpointSlice update, by hand.)

The same table exposes k8s' version of the classic trap: readiness flapping. A pod bouncing Ready/NotReady re-renders config on every flip — reload storms are the controller-era echo of Module 13's churn warning. Mitigations live at the probe level (initial delays, failure thresholds) and at the controller level (change-detection that ignores no-op diffs) — but the concept is one you've held since Module 1: reloads are cheap, not free.

4. Annotations as policy — the honest limits

Annotations ( nginx.ingress.kubernetes.io/*) are the escape hatch from YAML-standard into NGINX-specific policy — rate limits, timeouts, body sizes, snippets. Their power is real (self-service routing for teams that shouldn't touch the edge config); their limits are architectural:

  1. Annotation drift is config drift. A typo'd annotation silently does nothing on some controllers, crashes renders on others. (Lesson 14.4's gen-config.sh refusing an invalid render is the discipline that catches this class of bug — in CI, not in prod.)

  2. Snippets ( configuration-snippet ) are arbitrary config injection — powerful, and a governance hole you can drive a rewrite-shaped truck through. Many platforms disable them; the ones that don't should.

  3. Portability is aspirational. Annotations bind you to a controller family — which is fine if chosen deliberately (Lesson 14.5's decision) and expensive if discovered later.

The trajectory of the ecosystem — Gateway API — is exactly this lesson's conclusion: policy that outgrew annotations deserves a first-class object (HTTPRoute, with proper fields, proper validation, proper portability).

Try it yourself

  • Translation practice, no k8s required: open the services.json (Lesson 14.4's manifest) next to the YAML above and map each field to its NGINX rendering — then run gen-config.sh and check yourself against the output.

  • The readiness story, by hand: remove an endpoint from services.json, re-render, reload — you have just performed an EndpointSlice update with a text editor.

Q&A

Q: Do I need a Service if I have an Ingress? A: Yes — the Ingress points at Services; the controller reads their Endpoints. (Headless Services + your own NGINX via resolver — Lesson 13.1's backdoor — is the no-controller alternative.)

Q: Who terminates TLS in the cluster? A: Typically the controller's NGINX (certs from Secrets, auto renewed by cert-manager) — then HTTP or mTLS inward, per Lesson 14.1's Shape B. Passthrough (Lesson 10.5's SNI routing) is also supported by ingress-nginx for the can't-decrypt-here compliance shapes.

Q: pathType: Prefix vs Exact vs ImplementationSpecific ? A: Prefix = location /api/ (longest-prefix), Exact = location = /api, ImplementationSpecific = "ask your controller" — often regex locations under ingress-nginx. Module 2's matching algorithm, resurfaced as an enum.

Q: What happens to my Module 7 WAF/limits at the edge? A: They ride annotations or the controller's global config ( limit-rps, snippets, custom templates) — the directives are identical; only their packaging changes.

Takeaways

  • Kubernetes renamed your toolkit: Service = stable name, Endpoints = address list, Ingress = server/location policy, controller = the render-and-reload loop, readiness = health- gated membership.

  • The translation table is mechanical — every Ingress field maps to a directive you've written; read YAML as config and it stops being alien.

  • Readiness-as-membership is Module 13 with a health layer: seconds-scale, health-gated, drained — but flap-happy probes can reload-storm you just like churny DNS could TTL-tax you.

  • Annotations are policy escape hatches with real governance costs; the ecosystem's answer (Gateway API) is the lesson's own conclusion wearing a nicer type system.

Was this page helpful?

Mark this learning resource complete to track your learning progress.