CKA · Módulo 7CKA · Module 7CKA · Modulo 7 K8s v1.34 Services & Networking 20%

Del puerto crudo al routing: Ingress, Gateway API y NetworkPolicies

From a raw port to routing: Ingress, Gateway API and NetworkPolicies

Dalla porta grezza al routing: Ingress, Gateway API e NetworkPolicy

Un Service te da una IP estable. Lo que no te da es enrutar por host y por path, terminar TLS, ni decidir quién puede hablarle a quién. Eso es este módulo: la capa 7 (Ingress y su sucesor, Gateway API) y el firewall del cluster (NetworkPolicy).

A Service gives you a stable IP. What it does not give you is routing by host and path, TLS termination, or deciding who may talk to whom. That is this module: layer 7 (Ingress and its successor, Gateway API) and the cluster's firewall (NetworkPolicy).

Un Service ti dà un IP stabile. Ciò che non ti dà è il routing per host e path, la terminazione TLS, o decidere chi può parlare con chi. Questo è il modulo: il livello 7 (Ingress e il suo successore, Gateway API) e il firewall del cluster (NetworkPolicy).

Duración75 min
Duration75 min
Durata75 min
DominioServices & Networking
DomainServices & Networking
DominioServices & Networking
Peso20% (2 de 2)
Weight20% (2 of 2)
Peso20% (2 di 2)
RequiereM6 (Services, DNS)
RequiresM6 (Services, DNS)
RichiedeM6 (Service, DNS)
El M6 terminó con un NodePort: funciona, pero es un puerto raro en cada nodo y un Service por cada app. Con diez apps son diez puertos y ningún nombre. Ingress invierte eso: una sola entrada al cluster que mira el Host y el path de cada request HTTP y la manda al Service correcto — y de paso termina el TLS. Después viene Gateway API, el reemplazo oficial de Ingress, que ya está en el curriculum del CKA v1.34 y que casi seguro te van a tomar. Y al final, la otra mitad: NetworkPolicy, que no enruta nada sino que prohibe — porque por defecto en Kubernetes todos los pods pueden hablarle a todos.
M6 ended on a NodePort: it works, but it is an odd port on every node and one Service per app. Ten apps means ten ports and no names. Ingress flips that: a single entrance to the cluster that reads each HTTP request's Host and path and sends it to the right Service — terminating TLS along the way. Then comes Gateway API, Ingress's official replacement, already in the CKA v1.34 curriculum and almost certainly on your exam. And finally the other half: NetworkPolicy, which routes nothing and instead forbids — because by default in Kubernetes every pod can talk to every pod.
Il M6 finiva con un NodePort: funziona, ma è una porta strana su ogni nodo e un Service per ogni app. Con dieci app sono dieci porte e nessun nome. Ingress ribalta la cosa: un solo ingresso al cluster che legge Host e path di ogni richiesta HTTP e la manda al Service giusto — terminando il TLS per strada. Poi arriva Gateway API, il sostituto ufficiale di Ingress, già nel curriculum del CKA v1.34 e quasi certamente nel tuo esame. E infine l'altra metà: NetworkPolicy, che non instrada nulla ma proibisce — perché di default in Kubernetes ogni pod può parlare con ogni pod.
Modelo mental del módulo
Module mental model
Modello mentale del modulo
Ingress/Gateway = ENRUTA hacia adentro  ·  NetworkPolicy = PROHIBE adentro
Ingress/Gateway = ROUTES inward  ·  NetworkPolicy = FORBIDS inside
Ingress/Gateway = INSTRADA verso l'interno  ·  NetworkPolicy = PROIBISCE dentro
Son dos ejes independientes que la gente mezcla. El primero es capa 7: HTTP entra desde afuera y se reparte por host/path a Services. El segundo es capa 3/4: qué pods pueden abrir conexiones a qué pods, por IP y puerto. Un Ingress bien configurado no te protege nada; una NetworkPolicy no enruta nada.
Two independent axes that people conflate. The first is layer 7: HTTP arrives from outside and is split by host/path across Services. The second is layer 3/4: which pods may open connections to which pods, by IP and port. A well-configured Ingress protects nothing; a NetworkPolicy routes nothing.
Due assi indipendenti che spesso si confondono. Il primo è il livello 7: l'HTTP arriva da fuori e viene distribuito per host/path fra i Service. Il secondo è il livello 3/4: quali pod possono aprire connessioni verso quali pod, per IP e porta. Un Ingress ben configurato non protegge nulla; una NetworkPolicy non instrada nulla.
7.1
Ingress: recurso + controller
Ingress: resource + controller
Ingress: risorsa + controller
La regla y el que la ejecuta. IngressClass.
The rule and whoever runs it. IngressClass.
La regola e chi la esegue. IngressClass.
7.2
Reglas: host, path, TLS
Rules: host, path, TLS
Regole: host, path, TLS
pathType, backends, secret TLS, default backend.
pathType, backends, TLS secret, default backend.
pathType, backend, secret TLS, default backend.
7.3
Gateway API
GatewayClass, Gateway, HTTPRoute. Lo nuevo del examen.
GatewayClass, Gateway, HTTPRoute. The exam's new material.
GatewayClass, Gateway, HTTPRoute. La novità dell'esame.
7.4
NetworkPolicies
Default deny, ingress/egress, los tres selectores.
Default deny, ingress/egress, the three selectors.
Default deny, ingress/egress, i tre selector.
7.5
CNI
Qué hace el plugin y cómo verificar que está vivo.
What the plugin does and how to check it is alive.
Cosa fa il plugin e come verificare che sia vivo.
★
Práctica · 8 ejercicios
Practice · 8 exercises
Pratica · 8 esercizi
Ingress, TLS, HTTPRoute y las policies que engañan, con manifest listo para aplicar.
Ingress, TLS, HTTPRoute and the policies that deceive, each with a ready-to-apply manifest.
Ingress, TLS, HTTPRoute e le policy che ingannano, ognuno con un manifest pronto da applicare.
7.1
La entrada HTTP · 20 min
The HTTP entrance · 20 min
L'ingresso HTTP · 20 min

Ingress: el recurso y el controller

Ingress: the resource and the controller

Ingress: la risorsa e il controller

El objeto describe el ruteo. Otro proceso lo ejecuta.
The object describes the routing. Another process executes it.
L'oggetto descrive il routing. Un altro processo lo esegue.
Práctica · Crear y leer
Practice · Create and read
Pratica · Creare e leggere

Imperativo: sí, existe

Imperative: yes, it exists

Imperativo: sì, esiste

# create ingress NAME --rule="HOST/PATH=SERVICE:PORT"
k create ingress web --rule="demo.io/*=web:80"

# Con class, TLS y varias reglas (--rule se repite)# With class, TLS and several rules (--rule repeats)# Con class, TLS e più regole (--rule si ripete)
k create ingress tienda --class=nginx \
  --rule="demo.io/api*=api-svc:8080" \
  --rule="demo.io/*=web:80"

# Generar el YAML para editarlo (el patrón de siempre)# Generate the YAML to edit it (the usual pattern)# Generare lo YAML per modificarlo (il solito pattern)
k create ingress web --rule="demo.io/*=web:80" \
  --dry-run=client -o yaml > ing.yaml

El YAML mínimo

The minimal YAML

Lo YAML minimo

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web
spec:
  ingressClassName: nginx   # QUIÉN lo atiende# WHO serves it# CHI lo serve
  rules:
  - host: demo.io
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web      # un Service, no un pod# a Service, not a pod# un Service, non un pod
            port:
              number: 80

Verificar

Verify

Verificare

k get ingress
# NAME  CLASS  HOSTS     ADDRESS        PORTS
# web   nginx  demo.io   192.168.64.11  80

k describe ing web        # reglas + eventos del controller# rules + controller events# regole + eventi del controller
k get ingressclass          # qué classes hay# which classes exist# quali class esistono

# Probarlo sin DNS real: Host header a mano# Test it without real DNS: set the Host header by hand# Provarlo senza DNS reale: header Host a mano
curl -H "Host: demo.io" http://<IP-NODO>:<NODEPORT>/
⚠
Si ADDRESS queda vacío y describe no muestra eventos, casi siempre es que no hay ingress controller instalado o que el ingressClassName no matchea ninguna IngressClass. El objeto Ingress se crea igual: la API lo acepta aunque nadie lo vaya a atender.
If ADDRESS stays empty and describe shows no events, it is nearly always that no ingress controller is installed, or that ingressClassName matches no IngressClass. The Ingress object is created anyway: the API accepts it even when nobody will serve it.
Se ADDRESS resta vuoto e describe non mostra eventi, quasi sempre è perché non c'è un ingress controller installato, o perché ingressClassName non corrisponde ad alcuna IngressClass. L'oggetto Ingress viene creato lo stesso: l'API lo accetta anche se nessuno lo servirà.
Teoría · Dos piezas separadas
Theory · Two separate pieces
Teoria · Due pezzi separati

El recurso no hace nada

The resource does nothing

La risorsa non fa nulla

Un objeto Ingress es declaración pura: "el host demo.io va al Service web". Quien lo hace realidad es el ingress controller: un pod (normalmente nginx, Traefik o HAProxy) que observa los objetos Ingress y se reconfigura a sí mismo. A diferencia de casi todo lo demás, el controller no viene con Kubernetes: lo instalás vos.

An Ingress object is pure declaration: "host demo.io goes to Service web". What makes it real is the ingress controller: a pod (usually nginx, Traefik or HAProxy) that watches Ingress objects and reconfigures itself. Unlike almost everything else, the controller does not ship with Kubernetes: you install it.

Un oggetto Ingress è pura dichiarazione: "l'host demo.io va al Service web". A renderlo reale è l'ingress controller: un pod (di solito nginx, Traefik o HAProxy) che osserva gli oggetti Ingress e si riconfigura da solo. A differenza di quasi tutto il resto, il controller non arriva con Kubernetes: lo installi tu.

Cómo entra el tráfico

How traffic gets in

Come entra il traffico

El controller es un Deployment con su propio Service — normalmente NodePort en bare-metal o LoadBalancer en la nube. O sea: el tráfico entra por el Service del controller, y de ahí el controller decide, mirando el HTTP, a qué Service interno mandarlo. El Ingress no reemplaza a los Services: se apoya en ellos.

The controller is a Deployment with its own Service — usually NodePort on bare metal or LoadBalancer in the cloud. So: traffic enters through the controller's Service, and from there the controller decides, by reading the HTTP, which internal Service to forward to. Ingress does not replace Services: it rests on them.

Il controller è un Deployment con il proprio Service — di solito NodePort su bare metal o LoadBalancer in cloud. Quindi: il traffico entra dal Service del controller, e da lì il controller decide, leggendo l'HTTP, a quale Service interno inoltrarlo. Ingress non sostituisce i Service: si appoggia su di essi.

IngressClass

Si hay varios controllers en el cluster, ingressClassName dice cuál se hace cargo. Una IngressClass puede marcarse como default con la anotación ingressclass.kubernetes.io/is-default-class: "true"; ahí podés omitir el campo.

With several controllers in the cluster, ingressClassName says which one takes charge. An IngressClass can be marked default with the annotation ingressclass.kubernetes.io/is-default-class: "true"; then you can omit the field.

Con più controller nel cluster, ingressClassName dice quale se ne occupa. Una IngressClass può essere marcata come default con l'annotazione ingressclass.kubernetes.io/is-default-class: "true"; allora puoi omettere il campo.

Qué agrega sobre un Service

What it adds over a Service

Cosa aggiunge rispetto a un Service

CapacidadServiceIngress
CapabilityServiceIngress
CapacitàServiceIngress
CapaL4 (TCP/UDP)L7 (HTTP/S)
LayerL4 (TCP/UDP)L7 (HTTP/S)
LivelloL4 (TCP/UDP)L7 (HTTP/S)
Rutea por hostnosí
Routes by hostnoyes
Instrada per hostnosì
Rutea por pathnosí
Routes by pathnoyes
Instrada per pathnosì
Termina TLSnosí
Terminates TLSnoyes
Termina il TLSnosì
Puntos de entradauno por appuno para todas
Entry pointsone per appone for all
Punti d'ingressouno per appuno per tutte
★
Cadena completa, memorizala en este orden: cliente → Service del controller (NodePort/LB) → pod del controller → regla del Ingress → Service de la app → pod. Cuando algo no anda, recordá que hay dos Services en el camino.
The full chain, memorise it in this order: client → the controller's Service (NodePort/LB) → controller pod → Ingress rule → the app's Service → pod. When something breaks, remember there are two Services on the path.
La catena completa, memorizzala in quest'ordine: client → Service del controller (NodePort/LB) → pod del controller → regola dell'Ingress → Service dell'app → pod. Quando qualcosa non va, ricorda che ci sono due Service sul percorso.
7.2
Ruteo fino · 15 min
Fine routing · 15 min
Routing fine · 15 min

Hosts, paths, pathType y TLS

Hosts, paths, pathType and TLS

Host, path, pathType e TLS

Donde se pierden los puntos: pathType y el secret del TLS.
Where the points are lost: pathType and the TLS secret.
Dove si perdono i punti: pathType e il secret del TLS.
Práctica · Reglas reales
Practice · Real rules
Pratica · Regole reali

Varios paths, un host

Several paths, one host

Più path, un host

spec:
  ingressClassName: nginx
  rules:
  - host: demo.io
    http:
      paths:
      - path: /api
        pathType: Prefix
        backend:
          service: {name: api, port: {number: 8080}}
      - path: /
        pathType: Prefix
        backend:
          service: {name: web, port: {number: 80}}

Varios hosts (name-based virtual hosting)

Several hosts (name-based virtual hosting)

Più host (name-based virtual hosting)

  rules:
  - host: tienda.demo.io
    http: {paths: [...]}      # → Service tienda
  - host: blog.demo.io
    http: {paths: [...]}      # → Service blog

# Sin host: la regla matchea CUALQUIER host# With no host: the rule matches ANY host# Senza host: la regola corrisponde a QUALSIASI host

TLS: el secret primero

TLS: the secret first

TLS: prima il secret

# 1. Certificado (en el examen te lo suelen dar)# 1. Certificate (usually given to you in the exam)# 1. Certificato (all'esame di solito te lo danno)
openssl req -x509 -newkey rsa:2048 -nodes -days 365 \
  -keyout tls.key -out tls.crt -subj "/CN=demo.io"

# 2. Secret de tipo tls (nombres de clave FIJOS)# 2. A tls-type Secret (FIXED key names)# 2. Un Secret di tipo tls (nomi delle chiavi FISSI)
k create secret tls demo-tls --cert=tls.crt --key=tls.key

# 3. Referenciarlo en el Ingress# 3. Reference it in the Ingress# 3. Referenziarlo nell'Ingress
spec:
  tls:
  - hosts: [demo.io]
    secretName: demo-tls
  rules: ...

Backend por defecto (catch-all)

Default backend (catch-all)

Backend predefinito (catch-all)

spec:
  defaultBackend:
    service:
      name: fallback
      port: {number: 80}
# Lo que no matchea ninguna regla cae acá# Whatever matches no rule lands here# Ciò che non corrisponde a nessuna regola finisce qui
★
El secret TLS tiene que estar en el mismo namespace que el Ingress. Es el error más repetido del tema: creás el secret en default y el Ingress vive en web, y el controller sirve un certificado auto-firmado genérico sin decir mucho más que un evento.
The TLS secret must live in the same namespace as the Ingress. It is the most repeated mistake here: you create the secret in default while the Ingress lives in web, and the controller serves a generic self-signed certificate, saying little beyond one event.
Il secret TLS deve stare nello stesso namespace dell'Ingress. È l'errore più ripetuto del tema: crei il secret in default mentre l'Ingress vive in web, e il controller serve un certificato autofirmato generico, dicendo poco più di un evento.
Teoría · Los detalles que puntúan
Theory · The details that score
Teoria · I dettagli che danno punti

pathType: obligatorio y con trampa

pathType: mandatory, and a trap

pathType: obbligatorio, e una trappola

pathTypeMatchea
pathTypeMatches
pathTypeCorrisponde a
ExactEl path exacto, sin más (case-sensitive)
ExactThat exact path, nothing else (case-sensitive)
ExactQuel path esatto, nient'altro (case-sensitive)
PrefixPor elementos separados por /
PrefixBy elements separated by /
PrefixPer elementi separati da /
ImplementationSpecificLo que decida el controller
ImplementationSpecificWhatever the controller decides
ImplementationSpecificQuel che decide il controller

Prefix con /foo matchea /foo y /foo/bar, pero no /foobar: compara segmentos completos, no texto. Es la sutileza que hace fallar ejercicios.

Prefix with /foo matches /foo and /foo/bar, but not /foobar: it compares whole segments, not text. That subtlety is what fails exercises.

Prefix con /foo corrisponde a /foo e /foo/bar, ma non a /foobar: confronta segmenti interi, non testo. È la sottigliezza che fa fallire gli esercizi.

Prioridad de matcheo

Match priority

Priorità di corrispondenza

Ante varias reglas que matchean, gana el path más largo. Por eso /api le gana a / sin importar el orden en que los escribiste. Y si dos empatan en largo, gana Exact sobre Prefix. El matcheo además es case-sensitive en los dos tipos: /API no entra por una regla /api.

When several rules match, the longest path wins. That is why /api beats / regardless of the order you wrote them in. And if two tie on length, Exact beats Prefix. Matching is also case-sensitive for both types: /API does not match an /api rule.

Se più regole corrispondono, vince il path più lungo. Per questo /api batte / indipendentemente dall'ordine in cui li hai scritti. E se due pareggiano in lunghezza, vince Exact su Prefix. Il match è inoltre case-sensitive per entrambi i tipi: /API non entra da una regola /api.

TLS: quién termina qué

TLS: who terminates what

TLS: chi termina cosa

El TLS termina en el controller: de ahí hacia el Service el tráfico va en claro dentro del cluster (salvo que configures re-encryption con anotaciones). El secret tiene que ser de tipo kubernetes.io/tls, con las claves tls.crt y tls.key — esos nombres no son negociables.

TLS terminates at the controller: from there to the Service the traffic travels in the clear inside the cluster (unless you configure re-encryption with annotations). The secret must be of type kubernetes.io/tls, with the keys tls.crt and tls.key — those names are not negotiable.

Il TLS termina al controller: da lì al Service il traffico viaggia in chiaro dentro il cluster (a meno che tu non configuri la re-encryption con annotazioni). Il secret deve essere di tipo kubernetes.io/tls, con le chiavi tls.crt e tls.key — quei nomi non sono negoziabili.

Anotaciones: potentes y no portables

Annotations: powerful and not portable

Annotazioni: potenti e non portabili

Rewrites, redirects, timeouts, rate limiting: casi nada de eso está en la spec de Ingress; se hace con anotaciones propias del controller (nginx.ingress.kubernetes.io/rewrite-target, etc.). Esa fragmentación es exactamente la razón por la que existe Gateway API.

Rewrites, redirects, timeouts, rate limiting: almost none of that is in the Ingress spec; it is done with controller-specific annotations (nginx.ingress.kubernetes.io/rewrite-target, etc.). That fragmentation is exactly why Gateway API exists.

Rewrite, redirect, timeout, rate limiting: quasi nulla di questo è nella spec di Ingress; si fa con annotazioni specifiche del controller (nginx.ingress.kubernetes.io/rewrite-target, ecc.). Quella frammentazione è esattamente il motivo per cui esiste Gateway API.

★
Ante la duda en el examen: pathType: Prefix y path: /. Es el caso general y el que menos sorpresas da. Guardá Exact para cuando el enunciado diga literalmente "solo esa ruta".
When in doubt in the exam: pathType: Prefix and path: /. It is the general case and the one with fewest surprises. Save Exact for when the wording literally says "only that route".
Nel dubbio all'esame: pathType: Prefix e path: /. È il caso generale e quello con meno sorprese. Tieni Exact per quando il testo dice letteralmente "solo quella rotta".
VisorExplorerVisore · pathType y la regla que ganaand the winning rulee la regola che vince

Ojo con /apifoo: es el caso que hace fallar ejercicios.

Watch /apifoo: it is the case that fails exercises.

Attenzione a /apifoo: è il caso che fa fallire gli esercizi.

Referencia · todos los campos del Ingress
Reference · every Ingress field
Riferimento · tutti i campi dell'Ingress
CampoQué hace
FieldWhat it does
CampoCosa fa
ingressClassNameQué controller se hace cargo. Si lo omitís, solo lo toma la IngressClass marcada como default; si no hay ninguna, nadie lo atiende y ADDRESS queda vacío.
ingressClassNameWhich controller takes it. If you omit it, only the IngressClass marked as default picks it up; if there is none, nobody serves it and ADDRESS stays empty.
ingressClassNameQuale controller se ne occupa. Se lo ometti, lo prende solo la IngressClass marcata come default; se non ce n'è nessuna, non lo serve nessuno e ADDRESS resta vuoto.
rules[].hostEl Host: que tiene que traer el request. Omitido, matchea cualquier host. Admite un comodín de un solo nivel: *.demo.io cubre a.demo.io, pero no a.b.demo.io ni demo.io.
rules[].hostThe Host: the request must carry. Omitted, it matches any host. It takes a single-level wildcard: *.demo.io covers a.demo.io, but not a.b.demo.io nor demo.io.
rules[].hostL'Host: che la richiesta deve portare. Omesso, corrisponde a qualsiasi host. Accetta un wildcard di un solo livello: *.demo.io copre a.demo.io, ma non a.b.demo.io né demo.io.
pathLa ruta que se compara. Siempre arranca con /. Ante varias reglas que matchean, gana el más largo, no el orden.
pathThe route being compared. Always starts with /. When several rules match, the longest wins, not the order.
pathIl percorso da confrontare. Inizia sempre con /. Quando più regole corrispondono, vince il più lungo, non l'ordine.
pathType: PrefixCompara por elementos separados por /. /api cubre /api y /api/loquesea — pero no /apifoo.
pathType: PrefixCompares by elements split on /. /api covers /api and /api/anything — but not /apifoo.
pathType: PrefixConfronta per elementi separati da /. /api copre /api e /api/qualsiasi — ma non /apifoo.
pathType: ExactSolo esa ruta, literal y sensible a mayúsculas. No cubre subpaths: /salud no incluye /salud/x.
pathType: ExactThat route only, literal and case-sensitive. It does not cover subpaths: /salud does not include /salud/x.
pathType: ExactSolo quel percorso, letterale e case-sensitive. Non copre i subpath: /salud non include /salud/x.
pathType: ImplementationSpecificLo decide el controller. Ahí entran los comodines y las regex de nginx — a cambio, el manifiesto deja de ser portable. En el examen, usá Prefix o Exact salvo que te pidan otra cosa.
pathType: ImplementationSpecificThe controller decides. That is where nginx wildcards and regex live — in exchange, the manifest stops being portable. In the exam use Prefix or Exact unless told otherwise.
pathType: ImplementationSpecificDecide il controller. Lì entrano i wildcard e le regex di nginx — in cambio, il manifest smette di essere portabile. All'esame usa Prefix o Exact salvo diversa richiesta.
backend.service.nameEl Service destino. Tiene que existir en el mismo namespace que el Ingress.
backend.service.nameThe target Service. It must exist in the same namespace as the Ingress.
backend.service.nameIl Service di destinazione. Deve esistere nello stesso namespace dell'Ingress.
backend.service.port.numberEl puerto del Service, no el del container. Si el Service tiene nombre de puerto, se usa port.name en su lugar.
backend.service.port.numberThe Service port, not the container one. If the Service names its port, use port.name instead.
backend.service.port.numberLa porta del Service, non quella del container. Se il Service dà un nome alla porta, si usa port.name.
tls[].secretNameEl Secret con el certificado: tipo kubernetes.io/tls, claves tls.crt y tls.key, y en el namespace del Ingress. Es el error más repetido del tema.
tls[].secretNameThe Secret holding the certificate: type kubernetes.io/tls, keys tls.crt and tls.key, and in the Ingress namespace. The most repeated mistake on this topic.
tls[].secretNameIl Secret con il certificato: tipo kubernetes.io/tls, chiavi tls.crt e tls.key, e nel namespace dell'Ingress. È l'errore più ripetuto del tema.
tls[].hostsPara qué nombres presenta ese certificado (SNI). Sin ningún host en las reglas, el controller no sabe qué servir.
tls[].hostsWhich names that certificate is presented for (SNI). With no host in the rules, the controller does not know what to serve.
tls[].hostsPer quali nomi presenta quel certificato (SNI). Senza nessun host nelle regole, il controller non sa cosa servire.
defaultBackendA dónde va lo que no matchea ninguna regla — un catch-all. Sin él, el controller devuelve 404.
defaultBackendWhere whatever matches no rule goes — a catch-all. Without it, the controller returns 404.
defaultBackendDove va ciò che non corrisponde a nessuna regola — un catch-all. Senza, il controller risponde 404.
Constructor · armá el Ingress campo por campo y mirá el YAML
metadata
agregar una regla
extras
7.3
Lo nuevo · 25 min
The new material · 25 min
La novità · 25 min

Gateway API: el sucesor de Ingress

Gateway API: Ingress's successor

Gateway API: il successore di Ingress

Tres objetos en vez de uno, y un rol distinto para cada uno.
Three objects instead of one, each with a different role.
Tre oggetti invece di uno, ciascuno con un ruolo diverso.
Práctica · Los tres objetos
Practice · The three objects
Pratica · I tre oggetti

Primero: ¿está instalado?

First: is it installed?

Prima: è installato?

# Gateway API son CRDs (M3): no vienen con el cluster# Gateway API are CRDs (M3): they do not ship with the cluster# Gateway API sono CRD (M3): non arrivano col cluster
k get crd | grep gateway.networking.k8s.io
k api-resources | grep -i gateway

k get gatewayclass            # qué implementaciones hay# which implementations exist# quali implementazioni ci sono
k get gateway,httproute -A   # qué hay desplegado# what is deployed# cosa è distribuito

GatewayClass (la instala el admin del cluster)

GatewayClass (installed by the cluster admin)

GatewayClass (la installa l'admin del cluster)

apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: nginx
spec:
  controllerName: gateway.nginx.org/nginx-gateway-controller

Gateway: la escucha

Gateway: the listener

Gateway: l'ascolto

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: prod-gw
  namespace: infra
spec:
  gatewayClassName: nginx
  listeners:
  - name: http
    protocol: HTTP
    port: 80
    allowedRoutes:          # QUIÉN puede engancharse# WHO may attach# CHI può agganciarsi
      namespaces:
        from: All             # All | Same | Selector

HTTPRoute: las reglas (la usa el dev)

HTTPRoute: the rules (used by the dev)

HTTPRoute: le regole (la usa il dev)

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: web-route
spec:
  parentRefs:               # a QUÉ Gateway se engancha# WHICH Gateway it attaches to# a QUALE Gateway si aggancia
  - name: prod-gw
    namespace: infra
  hostnames: ["demo.io"]
  rules:
  - matches:
    - path:
        type: PathPrefix    # ojo: PathPrefix, no Prefix# careful: PathPrefix, not Prefix# attenzione: PathPrefix, non Prefix
        value: /api
    backendRefs:
    - name: api
      port: 8080

Split de tráfico (canary), gratis

Traffic splitting (canary), for free

Split del traffico (canary), gratis

    backendRefs:
    - {name: web-v1, port: 80, weight: 90}
    - {name: web-v2, port: 80, weight: 10}
# Con Ingress esto exigía anotaciones propietarias# With Ingress this required proprietary annotations# Con Ingress questo richiedeva annotazioni proprietarie
⚠
No hay kubectl create httproute: son CRDs, así que se escriben a mano. Practicá encontrarlos rápido en la doc (kubernetes.io → Services, Load Balancing and Networking → Gateway API) y usá k explain httproute.spec --recursive como red de seguridad offline.
There is no kubectl create httproute: they are CRDs, so you write them by hand. Practise finding them fast in the docs (kubernetes.io → Services, Load Balancing and Networking → Gateway API) and use k explain httproute.spec --recursive as your offline safety net.
Non esiste kubectl create httproute: sono CRD, quindi si scrivono a mano. Allenati a trovarli in fretta nella documentazione (kubernetes.io → Services, Load Balancing and Networking → Gateway API) e usa k explain httproute.spec --recursive come rete di sicurezza offline.
Teoría · Por qué existe
Theory · Why it exists
Teoria · Perché esiste

El problema de Ingress

Ingress's problem

Il problema di Ingress

La spec de Ingress es diminuta y todo lo interesante quedó en anotaciones específicas de cada controller. Migrar de nginx a Traefik significaba reescribir todo. Además, un solo objeto mezclaba dos responsabilidades: la infraestructura de entrada (del admin) y las rutas de cada app (del dev).

The Ingress spec is tiny and everything interesting ended up in controller-specific annotations. Migrating from nginx to Traefik meant rewriting everything. On top of that, a single object mixed two responsibilities: the entry infrastructure (the admin's) and each app's routes (the dev's).

La spec di Ingress è minuscola e tutto ciò che è interessante è finito in annotazioni specifiche di ogni controller. Migrare da nginx a Traefik significava riscrivere tutto. In più, un solo oggetto mescolava due responsabilità: l'infrastruttura d'ingresso (dell'admin) e le rotte di ogni app (del dev).

La separación en tres

The split into three

La divisione in tre

ObjetoLo manejaDefine
ObjectOwned byDefines
OggettoGestito daDefinisce
GatewayClassProveedor / adminQué implementación
GatewayClassProvider / adminWhich implementation
GatewayClassProvider / adminQuale implementazione
GatewayAdmin del clusterPuertos, protocolo, TLS
GatewayCluster adminPorts, protocol, TLS
GatewayAdmin del clusterPorte, protocollo, TLS
HTTPRouteDev de la appHosts, paths, backends
HTTPRouteApp developerHosts, paths, backends
HTTPRouteDev dell'appHost, path, backend

Cada rol toca solo lo suyo. El Gateway declara quién puede engancharse con allowedRoutes; la HTTPRoute declara a qué Gateway se engancha con parentRefs. Las dos puntas tienen que consentir — ahí está la seguridad del modelo.

Each role touches only its own. The Gateway declares who may attach with allowedRoutes; the HTTPRoute declares which Gateway it attaches to with parentRefs. Both ends must consent — that is the model's security.

Ogni ruolo tocca solo il proprio. Il Gateway dichiara chi può agganciarsi con allowedRoutes; l'HTTPRoute dichiara a quale Gateway si aggancia con parentRefs. Entrambe le parti devono acconsentire — è lì la sicurezza del modello.

Equivalencias mentales

Mental equivalences

Equivalenze mentali

IngressGateway API
IngressClassGatewayClass
Ingress (listener)Gateway
Ingress (listener)Gateway
Ingress (listener)Gateway
Ingress (rules)HTTPRoute
Ingress (rules)HTTPRoute
Ingress (rules)HTTPRoute
pathType: Prefixtype: PathPrefix
anotacionescampos de la spec
annotationsspec fields
annotazionicampi della spec

Estado en v1.34

Status in v1.34

Stato nella v1.34

Ingress está congelado (no recibe features nuevas, pero no está deprecado ni se va a ir). Gateway API es GA para HTTPRoute/Gateway/GatewayClass, se instala como CRDs, y más allá de HTTP existen GRPCRoute, TLSRoute, TCPRoute y UDPRoute — o sea, sirve también para lo que no es HTTP.

Ingress is frozen (no new features, but not deprecated and not going away). Gateway API is GA for HTTPRoute/Gateway/GatewayClass, installs as CRDs, and beyond HTTP there are GRPCRoute, TLSRoute, TCPRoute and UDPRoute — so it also covers what is not HTTP.

Ingress è congelato (nessuna nuova feature, ma non è deprecato e non sparirà). Gateway API è GA per HTTPRoute/Gateway/GatewayClass, si installa come CRD, e oltre all'HTTP esistono GRPCRoute, TLSRoute, TCPRoute e UDPRoute — quindi copre anche ciò che non è HTTP.

★
Gateway API entró explícitamente al curriculum del CKA. Lo más probable que te pidan: convertir un Ingress existente a HTTPRoute, o crear una HTTPRoute contra un Gateway que ya está. Practicá los dos sentidos de la traducción hasta que salga sin pensar.
Gateway API entered the CKA curriculum explicitly. The likeliest ask: convert an existing Ingress into an HTTPRoute, or create an HTTPRoute against a Gateway that already exists. Drill the translation both ways until it is automatic.
Gateway API è entrato esplicitamente nel curriculum del CKA. La richiesta più probabile: convertire un Ingress esistente in HTTPRoute, o creare un HTTPRoute su un Gateway già presente. Allena la traduzione in entrambi i versi finché non viene automatica.
Referencia · campos del Gateway
Reference · Gateway fields
Riferimento · campi del Gateway
CampoQué hace
FieldWhat it does
CampoCosa fa
gatewayClassNameQué implementación lo hace realidad. Apunta a una GatewayClass, que a su vez nombra al controller. Es el equivalente de ingressClassName.
gatewayClassNameWhich implementation makes it real. Points at a GatewayClass, which in turn names the controller. The equivalent of ingressClassName.
gatewayClassNameQuale implementazione lo realizza. Punta a una GatewayClass, che a sua volta nomina il controller. È l'equivalente di ingressClassName.
listeners[].nameNombre del listener, único dentro del Gateway. Es lo que una route referencia con sectionName.
listeners[].nameListener name, unique within the Gateway. It is what a route references with sectionName.
listeners[].nameNome del listener, unico nel Gateway. È ciò che una route referenzia con sectionName.
listeners[].protocolHTTP, HTTPS, TLS, TCP o UDP. Define qué Route se le puede enganchar: una HTTPRoute solo va a HTTP o HTTPS; para el resto hay TCPRoute, UDPRoute y TLSRoute.
listeners[].protocolHTTP, HTTPS, TLS, TCP or UDP. It decides which Route can attach: an HTTPRoute only goes to HTTP or HTTPS; the rest have TCPRoute, UDPRoute and TLSRoute.
listeners[].protocolHTTP, HTTPS, TLS, TCP o UDP. Decide quale Route può agganciarsi: una HTTPRoute va solo su HTTP o HTTPS; per il resto ci sono TCPRoute, UDPRoute e TLSRoute.
listeners[].portEl puerto donde escucha. Dos listeners pueden compartirlo solo si se distinguen por hostname y comparten protocolo.
listeners[].portThe port it listens on. Two listeners can share it only if they differ by hostname and share the protocol.
listeners[].portLa porta di ascolto. Due listener possono condividerla solo se si distinguono per hostname e condividono il protocollo.
listeners[].hostnamePara qué nombre escucha. Una route se engancha solo si sus hostnames intersecan con este. Vacío = cualquiera.
listeners[].hostnameWhich name it serves. A route attaches only if its hostnames intersect with this one. Empty = any.
listeners[].hostnamePer quale nome ascolta. Una route si aggancia solo se i suoi hostnames si intersecano con questo. Vuoto = qualsiasi.
tls.mode: TerminateEl Gateway descifra: necesita certificateRefs. De ahí al backend el tráfico va en claro dentro del cluster.
tls.mode: TerminateThe Gateway decrypts: it needs certificateRefs. From there to the backend traffic travels in the clear inside the cluster.
tls.mode: TerminateIl Gateway decifra: richiede certificateRefs. Da lì al backend il traffico viaggia in chiaro dentro il cluster.
tls.mode: PassthroughEl Gateway no descifra: el TLS llega entero al backend. No lleva certificateRefs y, como nunca ve HTTP, no admite una HTTPRoute — ese caso va con TLSRoute.
tls.mode: PassthroughThe Gateway does not decrypt: TLS reaches the backend whole. It takes no certificateRefs and, since it never sees HTTP, it accepts no HTTPRoute — that case goes with TLSRoute.
tls.mode: PassthroughIl Gateway non decifra: il TLS arriva intero al backend. Non prende certificateRefs e, non vedendo mai HTTP, non accetta una HTTPRoute — quel caso va con TLSRoute.
tls.certificateRefsLos Secret con el certificado, como el secretName del Ingress pero en lista (podés servir varios nombres).
tls.certificateRefsThe Secrets holding the certificate, like the Ingress secretName but as a list (you can serve several names).
tls.certificateRefsI Secret con il certificato, come il secretName dell'Ingress ma in lista (puoi servire più nomi).
allowedRoutes.namespaces.fromQuién puede engancharse: All (cualquier namespace), Same (solo el del Gateway) o Selector (los namespaces con ciertos labels). Es la mitad del consentimiento — la otra mitad es el parentRefs de la route.
allowedRoutes.namespaces.fromWho may attach: All (any namespace), Same (only the Gateway's) or Selector (namespaces carrying certain labels). It is half of the consent — the other half is the route's parentRefs.
allowedRoutes.namespaces.fromChi può agganciarsi: All (qualsiasi namespace), Same (solo quello del Gateway) o Selector (i namespace con certe label). È metà del consenso — l'altra metà è il parentRefs della route.

Y los de la HTTPRoute

And the HTTPRoute ones

E quelli della HTTPRoute

CampoQué hace
FieldWhat it does
CampoCosa fa
parentRefsA qué Gateway se engancha esta route: nombre y namespace. Es la otra mitad del consentimiento.
parentRefsWhich Gateway this route attaches to: name and namespace. It is the other half of the consent.
parentRefsA quale Gateway si aggancia questa route: nome e namespace. È l'altra metà del consenso.
parentRefs[].sectionNameUn listener concreto del Gateway, por su name. Omitido, la route intenta engancharse a todos los que la acepten.
parentRefs[].sectionNameA specific listener of the Gateway, by its name. Omitted, the route tries to attach to every listener that accepts it.
parentRefs[].sectionNameUn listener specifico del Gateway, per il suo name. Omesso, la route prova ad agganciarsi a tutti quelli che la accettano.
hostnamesPara qué nombres vale esta route. Tienen que intersecar con el hostname del listener, o no recibe tráfico.
hostnamesWhich names this route serves. They must intersect the listener's hostname, or it gets no traffic.
hostnamesPer quali nomi vale questa route. Devono intersecarsi con l'hostname del listener, altrimenti non riceve traffico.
matches.path.type: PathPrefixPor segmentos, igual que el Prefix del Ingress — pero acá se llama distinto. Es el que vas a usar casi siempre.
matches.path.type: PathPrefixBy segments, just like the Ingress Prefix — but here it has a different name. The one you will use almost always.
matches.path.type: PathPrefixPer segmenti, come il Prefix dell'Ingress — ma qui si chiama diversamente. È quello che userai quasi sempre.
matches.path.type: ExactLiteral, sin subpaths. Mismo significado que en Ingress.
matches.path.type: ExactLiteral, no subpaths. Same meaning as in Ingress.
matches.path.type: ExactLetterale, senza subpath. Stesso significato che in Ingress.
matches.path.type: RegularExpressionExpresión regular. Es de soporte opcional en la spec: puede que tu implementación no lo acepte. PathPrefix y Exact son los obligatorios.
matches.path.type: RegularExpressionRegular expression. It is optional support in the spec: your implementation may not accept it. PathPrefix and Exact are the mandatory ones.
matches.path.type: RegularExpressionEspressione regolare. Ha supporto opzionale nella spec: la tua implementazione potrebbe non accettarlo. PathPrefix ed Exact sono obbligatori.
matches.methodLimita la regla a un verbo HTTP (GET, POST…). Con Ingress esto no existía: había que resolverlo con anotaciones.
matches.methodLimits the rule to one HTTP verb (GET, POST…). With Ingress this did not exist: you solved it with annotations.
matches.methodLimita la regola a un verbo HTTP (GET, POST…). Con Ingress non esisteva: si risolveva con annotation.
matches.headersMatchea por cabecera, con type: Exact o RegularExpression. Es lo que permite un canary por header sin tocar el resto del tráfico.
matches.headersMatches on a header, with type: Exact or RegularExpression. This is what enables a header-based canary without touching the rest of the traffic.
matches.headersCorrisponde su un header, con type: Exact o RegularExpression. È ciò che permette un canary per header senza toccare il resto del traffico.
matches.queryParamsLo mismo, pero sobre los parámetros de la query string.
matches.queryParamsThe same, but on query string parameters.
matches.queryParamsLo stesso, ma sui parametri della query string.
filters: RequestRedirectContesta un redirect (scheme, hostname, statusCode) en vez de reenviar. Por eso no puede convivir con backendRefs: la API rechaza esa combinación.
filters: RequestRedirectAnswers a redirect (scheme, hostname, statusCode) instead of forwarding. That is why it cannot coexist with backendRefs: the API rejects that combination.
filters: RequestRedirectRisponde un redirect (scheme, hostname, statusCode) invece di inoltrare. Per questo non può convivere con backendRefs: l'API rifiuta quella combinazione.
filters: URLRewriteReescribe el path o el host antes de reenviar. ReplacePrefixMatch cambia el prefijo que matcheó — el equivalente del rewrite-target de nginx, pero en la spec.
filters: URLRewriteRewrites path or host before forwarding. ReplacePrefixMatch swaps the prefix that matched — the equivalent of nginx's rewrite-target, but in the spec.
filters: URLRewriteRiscrive il path o l'host prima di inoltrare. ReplacePrefixMatch cambia il prefisso che ha corrisposto — l'equivalente del rewrite-target di nginx, ma nella spec.
filters: RequestHeaderModifierAgrega, reemplaza o borra cabeceras del request (set, add, remove) antes de que llegue al backend.
filters: RequestHeaderModifierAdds, replaces or removes request headers (set, add, remove) before it reaches the backend.
filters: RequestHeaderModifierAggiunge, sostituisce o rimuove header della richiesta (set, add, remove) prima che arrivi al backend.
backendRefs[].name / portEl Service destino y su puerto. Pueden ser varios en la misma regla.
backendRefs[].name / portThe target Service and its port. There can be several in the same rule.
backendRefs[].name / portIl Service di destinazione e la sua porta. Possono essere più d'uno nella stessa regola.
backendRefs[].weightProporción, no porcentaje: 90 y 10 reparten 90%/10%, igual que 9 y 1. En 0 ese backend no recibe nada. Esto con Ingress exigía anotaciones propietarias.
backendRefs[].weightProportion, not percentage: 90 and 10 split 90%/10%, same as 9 and 1. At 0 that backend gets nothing. With Ingress this required proprietary annotations.
backendRefs[].weightProporzione, non percentuale: 90 e 10 dividono 90%/10%, come 9 e 1. A 0 quel backend non riceve nulla. Con Ingress serviva un'annotation proprietaria.
Constructor · Gateway + HTTPRoute, y si realmente se enganchan
Gateway · lo arma el admin
agregar un listener
HTTPRoute · la arma el dev
agregar una regla
7.4
Firewall interno · 20 min
Internal firewall · 20 min
Firewall interno · 20 min

NetworkPolicies

Por defecto todo pod habla con todo pod. Esto lo corta.
By default every pod talks to every pod. This cuts that.
Di default ogni pod parla con ogni pod. Questo lo taglia.
Práctica · De cero a aislado
Practice · From zero to isolated
Pratica · Da zero a isolato

Deny all: el punto de partida

Deny all: the starting point

Deny all: il punto di partenza

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny
  namespace: prod
spec:
  podSelector: {}        # {} = TODOS los pods del ns# {} = ALL pods in the ns# {} = TUTTI i pod del ns
  policyTypes: [Ingress, Egress]
# Sin reglas → no se permite nada. Base de todo diseño.# No rules → nothing is allowed. The base of every design.# Nessuna regola → non è permesso nulla. La base di ogni design.

Permitir solo desde el frontend

Allow only from the frontend

Permettere solo dal frontend

spec:
  podSelector:
    matchLabels: {app: db}   # A QUIÉN protege# WHO it protects# CHI protegge
  policyTypes: [Ingress]
  ingress:
  - from:
    - podSelector:
        matchLabels: {app: web}
    ports:
    - protocol: TCP
      port: 5432

Desde otro namespace

From another namespace

Da un altro namespace

  ingress:
  - from:
    - namespaceSelector:
        matchLabels: {kubernetes.io/metadata.name: prod}
      podSelector:                # SIN guión: AND# NO dash: AND# SENZA trattino: AND
        matchLabels: {app: web}
# "pods app=web QUE ESTÉN en el ns prod"# "app=web pods THAT ARE IN the prod ns"# "pod app=web CHE STANNO nel ns prod"

  ingress:
  - from:
    - namespaceSelector: {matchLabels: {env: prod}}
    - podSelector: {matchLabels: {app: web}}   # CON guión: OR# WITH dash: OR# CON trattino: OR
# "cualquier pod del ns prod, O cualquier pod app=web de ESTE ns"# "any pod in the prod ns, OR any app=web pod in THIS ns"# "qualsiasi pod del ns prod, O qualsiasi pod app=web di QUESTO ns"

Egress: dejar salir el DNS

Egress: letting DNS out

Egress: lasciar uscire il DNS

  policyTypes: [Egress]
  egress:
  - to:
    - namespaceSelector:
        matchLabels: {kubernetes.io/metadata.name: kube-system}
    ports:
    - {protocol: UDP, port: 53}
    - {protocol: TCP, port: 53}

Probar el efecto

Testing the effect

Provare l'effetto

k get netpol -n prod
k describe netpol default-deny -n prod

# Antes y después: el mismo curl# Before and after: the same curl# Prima e dopo: lo stesso curl
k run t --rm -it -n prod --image=busybox:1.28 -- \
  wget -qO- --timeout=2 db:5432
⚠
Un deny-all de egress rompe el DNS y con eso rompe todo lo demás, porque los pods resuelven contra CoreDNS en kube-system. Si aplicaste egress y "no resuelve nada", es esto. Abrí el puerto 53 UDP y TCP.
An egress deny-all breaks DNS and with it everything else, because pods resolve against CoreDNS in kube-system. If you applied egress and "nothing resolves", this is it. Open port 53 on UDP and TCP.
Un deny-all di egress rompe il DNS e con esso tutto il resto, perché i pod risolvono contro CoreDNS in kube-system. Se hai applicato egress e "non risolve nulla", è questo. Apri la porta 53 in UDP e TCP.
Teoría · Cómo razonarlas
Theory · How to reason about them
Teoria · Come ragionarle

Las cuatro reglas de oro

The four golden rules

Le quattro regole d'oro

  • Sin ninguna policy: todo permitido. Kubernetes es abierto por defecto.
  • Apenas una policy selecciona un pod, ese pod pasa a estar aislado para los policyTypes declarados: se niega todo salvo lo que las policies permitan.
  • Son aditivas y solo permiten: no existe una regla "deny". Varias policies sobre el mismo pod se suman (unión de lo permitido).
  • Son namespaced: la policy vive en un namespace y su podSelector solo alcanza pods de ese namespace.
  • With no policy at all: everything is allowed. Kubernetes is open by default.
  • The moment one policy selects a pod, that pod becomes isolated for the declared policyTypes: everything is denied except what policies allow.
  • They are additive and only allow: there is no "deny" rule. Several policies on the same pod add up (union of what is allowed).
  • They are namespaced: the policy lives in a namespace and its podSelector reaches only pods in that namespace.
  • Senza alcuna policy: tutto permesso. Kubernetes è aperto di default.
  • Appena una policy seleziona un pod, quel pod diventa isolato per i policyTypes dichiarati: si nega tutto tranne ciò che le policy permettono.
  • Sono additive e permettono soltanto: non esiste una regola "deny". Più policy sullo stesso pod si sommano (unione di ciò che è permesso).
  • Sono namespaced: la policy vive in un namespace e il suo podSelector raggiunge solo i pod di quel namespace.

Los tres selectores del from/to

The three selectors in from/to

I tre selector di from/to

SelectorSelecciona
SelectorSelects
SelectorSeleziona
podSelectorPods (del mismo ns, salvo que lo combines)
podSelectorPods (in the same ns, unless combined)
podSelectorPod (dello stesso ns, salvo combinazione)
namespaceSelectorTodos los pods de los ns que matcheen
namespaceSelectorAll pods in the matching namespaces
namespaceSelectorTutti i pod dei namespace corrispondenti
ipBlockCIDRs — para tráfico externo al cluster
ipBlockCIDRs — for traffic external to the cluster
ipBlockCIDR — per traffico esterno al cluster

El guión que cambia el sentido

The dash that changes the meaning

Il trattino che cambia il senso

Dentro de un ítem de la lista from, varios selectores son AND. Cada guión abre un ítem nuevo, y entre ítems es OR. Es la trampa clásica del tema: un guión de más abre la puerta a un namespace entero.

Within one item of the from list, several selectors are AND. Each dash opens a new item, and between items it is OR. It is the classic trap here: one extra dash opens the door to an entire namespace.

Dentro un elemento della lista from, più selector sono in AND. Ogni trattino apre un nuovo elemento, e fra elementi è OR. È la trappola classica del tema: un trattino di troppo apre la porta a un intero namespace.

Es el CNI quien las aplica

The CNI is what enforces them

È il CNI ad applicarle

El objeto NetworkPolicy lo acepta la API siempre, pero solo tiene efecto si tu CNI lo soporta. Calico y Cilium sí; Flannel puro no. Si creás una policy y el tráfico pasa igual, sospechá del CNI antes que de tu YAML.

The API always accepts a NetworkPolicy object, but it only takes effect if your CNI supports it. Calico and Cilium do; plain Flannel does not. If you create a policy and traffic flows anyway, suspect the CNI before your YAML.

L'API accetta sempre un oggetto NetworkPolicy, ma ha effetto solo se il tuo CNI lo supporta. Calico e Cilium sì; Flannel puro no. Se crei una policy e il traffico passa lo stesso, sospetta del CNI prima che del tuo YAML.

Lo que las policies no ven

What policies do not see

Cosa le policy non vedono

Trabajan sobre IPs y puertos, no sobre Services: filtrás por la IP del pod destino, no por la ClusterIP. Y no aplican a tráfico del propio nodo hacia sus pods (por ejemplo, las probes del kubelet, que nunca se bloquean).

They work on IPs and ports, not Services: you filter by the destination pod's IP, not the ClusterIP. And they do not apply to traffic from a node to its own pods (for example the kubelet's probes, which are never blocked).

Lavorano su IP e porte, non sui Service: filtri per l'IP del pod di destinazione, non per il ClusterIP. E non si applicano al traffico da un nodo verso i propri pod (per esempio le probe del kubelet, che non vengono mai bloccate).

★
Receta de examen para "permití solo X→Y": (1) podSelector = el destino Y, nunca el origen; (2) policyTypes: [Ingress]; (3) from = el origen X; (4) ports = el puerto del container. Leé siempre la dirección del enunciado antes de escribir.
Exam recipe for "allow only X→Y": (1) podSelector = the destination Y, never the source; (2) policyTypes: [Ingress]; (3) from = the source X; (4) ports = the container's port. Always read the direction in the wording before writing.
Ricetta d'esame per "permetti solo X→Y": (1) podSelector = la destinazione Y, mai l'origine; (2) policyTypes: [Ingress]; (3) from = l'origine X; (4) ports = la porta del container. Leggi sempre la direzione nel testo prima di scrivere.
VisorExplorerVisore · quién llega al podwho reaches the podchi raggiunge il pod db

Compará AND y OR: el YAML cambia en un solo guión, y la superficie de acceso cambia entera.

Compare AND and OR: the YAML changes by a single dash, and the whole access surface changes.

Confronta AND e OR: lo YAML cambia di un solo trattino, e l'intera superficie di accesso cambia.

7.5
La base · 10 min
The foundation · 10 min
La base · 10 min

CNI: el plugin que hace posible todo lo anterior

CNI: the plugin that makes all the above possible

CNI: il plugin che rende possibile tutto quanto sopra

Poco que configurar, mucho que verificar cuando se rompe.
Little to configure, a lot to check when it breaks.
Poco da configurare, molto da verificare quando si rompe.
Práctica · Verificar
Practice · Verify
Pratica · Verificare

¿Quién es el CNI de este cluster?

Which CNI does this cluster use?

Qual è il CNI di questo cluster?

# Los pods del plugin corren como DaemonSet# The plugin's pods run as a DaemonSet# I pod del plugin girano come DaemonSet
k get pods -n kube-system -o wide | grep -Ei 'calico|flannel|cilium|weave'
k get ds -A

# La config, en cada nodo# The config, on each node# La config, su ogni nodo
ls /etc/cni/net.d/
cat /etc/cni/net.d/10-calico.conflist
ls /opt/cni/bin/          # los binarios# the binaries# i binari

El síntoma clásico de CNI ausente

The classic symptom of a missing CNI

Il sintomo classico di CNI assente

k get nodes
# NotReady

k describe node worker1
# KubeletNotReady: cni plugin not initialized

k get pods -A
# CoreDNS en Pending → siempre es el CNI# CoreDNS Pending → it is always the CNI# CoreDNS in Pending → è sempre il CNI

Comprobar la red plana

Checking the flat network

Verificare la rete piatta

# Un pod en cada nodo, y que se hablen# A pod on each node, and make them talk# Un pod su ogni nodo, e falli parlare
k run p1 --image=nginx
k get pod p1 -o wide           # anotá la IP# note the IP# annota l'IP
k run p2 --rm -it --image=busybox:1.28 -- \
  wget -qO- --timeout=2 <IP-DE-P1>
★
Regla mnemotécnica: CoreDNS en Pending y nodos NotReady al mismo tiempo = CNI. Es el primer chequeo después de un kubeadm init y una de las causas más repetidas de cluster roto en los labs.
Mnemonic: CoreDNS Pending and nodes NotReady at the same time = CNI. It is the first check after a kubeadm init and one of the most repeated causes of a broken cluster in labs.
Mnemonica: CoreDNS in Pending e nodi NotReady insieme = CNI. È il primo controllo dopo un kubeadm init e una delle cause più ripetute di cluster rotto nei lab.
Teoría · Qué hace
Theory · What it does
Teoria · Cosa fa

El contrato

The contract

Il contratto

CNI (Container Network Interface) es un estándar, no un producto. Cuando el kubelet crea un pod, invoca al plugin y le pide: darle una IP, crear su interfaz de red y programar las rutas para que llegue a los demás pods. Al borrarlo, lo deshace. Kubernetes no trae implementación propia: por eso un cluster recién inicializado tiene los nodos NotReady hasta que instalás una.

CNI (Container Network Interface) is a standard, not a product. When the kubelet creates a pod, it invokes the plugin and asks it to: give it an IP, create its network interface and program the routes so it reaches the other pods. On deletion, it undoes that. Kubernetes ships no implementation of its own: that is why a freshly initialised cluster has NotReady nodes until you install one.

CNI (Container Network Interface) è uno standard, non un prodotto. Quando il kubelet crea un pod, invoca il plugin e gli chiede: dargli un IP, creare la sua interfaccia di rete e programmare le rotte perché raggiunga gli altri pod. Alla cancellazione, disfa tutto. Kubernetes non porta un'implementazione propria: per questo un cluster appena inizializzato ha i nodi NotReady finché non ne installi una.

Quién hace qué (ordenar esto vale oro)

Who does what (getting this straight is gold)

Chi fa cosa (mettere ordine qui vale oro)

ComponenteResponsable de
ComponentResponsible for
ComponenteResponsabile di
CNIIP del pod, red pod-a-pod, NetworkPolicy
CNIPod IP, pod-to-pod network, NetworkPolicy
CNIIP del pod, rete pod-a-pod, NetworkPolicy
kube-proxyServices: ClusterIP → pods (M6)
kube-proxyServices: ClusterIP → pods (M6)
kube-proxyService: ClusterIP → pod (M6)
CoreDNSNombres → IPs (M6)
CoreDNSNames → IPs (M6)
CoreDNSNomi → IP (M6)
Ingress controllerHTTP externo → Services
Ingress controllerExternal HTTP → Services
Ingress controllerHTTP esterno → Service

Las implementaciones que verás

The implementations you will meet

Le implementazioni che incontrerai

  • Calico — el de tu cluster. Ruteo L3, NetworkPolicy completa (y extendida).
  • Flannel — simple, overlay VXLAN, sin soporte de NetworkPolicy.
  • Cilium — eBPF, puede reemplazar a kube-proxy, policies L7.
  • Calico — yours. L3 routing, full (and extended) NetworkPolicy.
  • Flannel — simple, VXLAN overlay, no NetworkPolicy support.
  • Cilium — eBPF, can replace kube-proxy, L7 policies.
  • Calico — il tuo. Routing L3, NetworkPolicy completa (ed estesa).
  • Flannel — semplice, overlay VXLAN, senza supporto NetworkPolicy.
  • Cilium — eBPF, può sostituire kube-proxy, policy L7.

Para el examen

For the exam

Per l'esame

No te van a pedir instalar ni configurar un CNI a mano. Sí te pueden pedir diagnosticar que falta o está caído (M10) y saber que las NetworkPolicies dependen de él.

You will not be asked to install or configure a CNI by hand. You may well be asked to diagnose that one is missing or down (M10), and to know that NetworkPolicies depend on it.

Non ti chiederanno di installare o configurare un CNI a mano. Possono però chiederti di diagnosticare che manca o è caduto (M10), e di sapere che le NetworkPolicy dipendono da lui.

★
Cierre del módulo
Module wrap-up
Chiusura del modulo

Lo que te llevás del Módulo 7

What you take from Module 7

Cosa porti via dal Modulo 7

Resumen · Comandos clave
Summary · Key commands
Riassunto · Comandi chiave
   # Ingress
k create ingress web --rule="demo.io/*=web:80"
k create ingress web --class=nginx --rule="demo.io/api*=api:8080"
k get ingress,ingressclass
k describe ing web

   # TLS
k create secret tls demo-tls --cert=tls.crt --key=tls.key

# Gateway API (CRDs: a mano, pero verificá que estén)# Gateway API (CRDs: by hand, but check they exist)# Gateway API (CRD: a mano, ma verifica che ci siano)
k get crd | grep gateway.networking
k get gatewayclass,gateway,httproute -A
k explain httproute.spec.rules --recursive

   # NetworkPolicy
k get netpol -A
k describe netpol default-deny -n prod

   # CNI
k get pods -n kube-system -o wide
ls /etc/cni/net.d/

# Probar HTTP sin DNS# Test HTTP without DNS# Provare HTTP senza DNS
curl -H "Host: demo.io" http://<IP-NODO>:<NODEPORT>/api
Teoría · Lo esencial
Theory · The essentials
Teoria · L'essenziale

Las 6 ideas que no se negocian

The 6 non-negotiable ideas

Le 6 idee non negoziabili

  • El Ingress no hace nada solo: hace falta un controller instalado, y el tráfico entra por el Service de ese controller.
  • pathType es obligatorio y Prefix compara segmentos, no texto. Gana el path más largo.
  • El secret TLS va en el mismo namespace que el Ingress, tipo kubernetes.io/tls.
  • Gateway API parte Ingress en tres: GatewayClass (impl), Gateway (listener), HTTPRoute (reglas). Las dos puntas consienten: allowedRoutes + parentRefs.
  • NetworkPolicy: sin policy todo pasa; con una policy que te seleccione, solo pasa lo permitido. Son aditivas y nunca niegan explícitamente.
  • El guión en from decide AND vs OR. Y todo esto lo ejecuta el CNI: si no lo soporta, tu policy es decorativa.
  • Ingress does nothing on its own: you need an installed controller, and traffic enters through that controller's Service.
  • pathType is mandatory and Prefix compares segments, not text. The longest path wins.
  • The TLS secret goes in the same namespace as the Ingress, of type kubernetes.io/tls.
  • Gateway API splits Ingress into three: GatewayClass (impl), Gateway (listener), HTTPRoute (rules). Both ends consent: allowedRoutes + parentRefs.
  • NetworkPolicy: with no policy everything passes; with a policy selecting you, only what is allowed passes. They are additive and never deny explicitly.
  • The dash in from decides AND vs OR. And the CNI executes all of it: if it does not support policies, yours are decorative.
  • Ingress non fa nulla da solo: serve un controller installato, e il traffico entra dal Service di quel controller.
  • pathType è obbligatorio e Prefix confronta segmenti, non testo. Vince il path più lungo.
  • Il secret TLS va nello stesso namespace dell'Ingress, di tipo kubernetes.io/tls.
  • Gateway API divide Ingress in tre: GatewayClass (impl), Gateway (listener), HTTPRoute (regole). Entrambe le parti acconsentono: allowedRoutes + parentRefs.
  • NetworkPolicy: senza policy passa tutto; con una policy che ti seleziona, passa solo ciò che è permesso. Sono additive e non negano mai esplicitamente.
  • Il trattino in from decide AND contro OR. E tutto questo lo esegue il CNI: se non lo supporta, la tua policy è decorativa.
★
El hilo del módulo: Kubernetes separa siempre declaración de ejecución. Ingress declara y el controller ejecuta; NetworkPolicy declara y el CNI ejecuta. Por eso el diagnóstico es siempre el mismo: primero ¿el objeto está bien escrito?, después ¿hay alguien que lo esté ejecutando?
The thread of the module: Kubernetes always separates declaration from execution. Ingress declares and the controller executes; NetworkPolicy declares and the CNI executes. That is why the diagnosis is always the same: first, is the object written correctly? then, is anyone actually executing it?
Il filo del modulo: Kubernetes separa sempre la dichiarazione dall'esecuzione. Ingress dichiara e il controller esegue; NetworkPolicy dichiara e il CNI esegue. Per questo la diagnosi è sempre la stessa: prima, l'oggetto è scritto bene? poi, c'è qualcuno che lo sta eseguendo?
Checkpoint Módulo 7: Ingress (reglas, pathType, TLS), Gateway API (los tres objetos y la traducción desde Ingress), NetworkPolicies (default deny, AND vs OR, DNS en egress) y el rol del CNI. Dominio Services & Networking (20%) completo. Siguiente: Módulo 8 — Storage.
Module 7 checkpoint: Ingress (rules, pathType, TLS), Gateway API (the three objects and the translation from Ingress), NetworkPolicies (default deny, AND vs OR, DNS in egress) and the CNI's role. The Services & Networking domain (20%) is complete. Next: Module 8 — Storage.
Checkpoint Modulo 7: Ingress (regole, pathType, TLS), Gateway API (i tre oggetti e la traduzione da Ingress), NetworkPolicy (default deny, AND contro OR, DNS in egress) e il ruolo del CNI. Dominio Services & Networking (20%) completo. Prossimo: Modulo 8 — Storage.
★
Fase 2 · resolver en vivo
Phase 2 · solve it live
Fase 2 · risolvere dal vivo

Práctica — 8 ejercicios

Practice — 8 exercises

Pratica — 8 esercizi

Ingress y TLS, la traducción a Gateway API, y las policies que engañan.
Ingress and TLS, the Gateway API translation, and the policies that deceive.
Ingress e TLS, la traduzione a Gateway API, e le policy che ingannano.
0 / 8
🧠
Modelo mental. Dos ejes que no se mezclan: Ingress/Gateway enrutan (capa 7, hacia adentro) y NetworkPolicy prohibe (capa 3/4, adentro). Y en los dos casos, Kubernetes separa declaración de ejecución: el objeto siempre se crea; que haga algo depende de que haya un controller o un CNI que lo ejecute.
Mental model. Two axes that never mix: Ingress/Gateway route (layer 7, inward) and NetworkPolicy forbids (layer 3/4, inside). And in both cases Kubernetes separates declaration from execution: the object is always created; whether it does anything depends on a controller or a CNI executing it.
Modello mentale. Due assi che non si mescolano: Ingress/Gateway instradano (livello 7, verso l'interno) e NetworkPolicy proibisce (livello 3/4, dentro). E in entrambi i casi Kubernetes separa la dichiarazione dall'esecuzione: l'oggetto viene sempre creato; che faccia qualcosa dipende da un controller o da un CNI che lo esegua.
◆
Manifests publicados. Cada ejercicio tiene su YAML servido por este mismo sitio: k apply -f <url>/manifests/m7e1.yaml. Los E son la solución de referencia; los T plantan el problema — aplicalos y diagnosticá sin leerlos. k delete -f <misma-url> limpia. E2 depende de E1, y E4/E5 del Gateway de E4.
Published manifests. Every exercise has its YAML served by this very site: k apply -f <url>/manifests/m7e1.yaml. The E ones are the reference solution; the T ones plant the problem — apply them and diagnose without reading them. k delete -f <same-url> cleans up. E2 depends on E1, and E4/E5 on E4's Gateway.
Manifest pubblicati. Ogni esercizio ha il suo YAML servito da questo stesso sito: k apply -f <url>/manifests/m7e1.yaml. Gli E sono la soluzione di riferimento; i T piantano il problema — applicali e diagnostica senza leggerli. k delete -f <stessa-url> pulisce. E2 dipende da E1, ed E4/E5 dal Gateway dell'E4.
◆
Preparación. Snapshot antes de todo. E1–E3 necesitan un ingress controller — pineá la versión, no uses main, que es una rama móvil:
k apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.13.9/deploy/static/provider/baremetal/deploy.yaml
El controller queda como NodePort: sacá los puertos con k -n ingress-nginx get svc ingress-nginx-controller. E4–E5 necesitan los CRDs de Gateway API:
k apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.6.2/standard-install.yaml
Ojo: los CRDs te dejan crear y validar los objetos, pero no enrutan tráfico — para eso hace falta además una implementación (nginx-gateway-fabric, Istio, Cilium). E6, T1 y T2 funcionan tal cual con Calico. Los ejercicios usan los namespaces ing (E1–E5) y prod/dev (E6, T1, T2); aplicá los de policies de a uno, borrando el anterior.
Setup. Snapshot before anything. E1–E3 need an ingress controller — pin the version, do not use main, which is a moving branch:
k apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.13.9/deploy/static/provider/baremetal/deploy.yaml
The controller comes up as a NodePort: get the ports with k -n ingress-nginx get svc ingress-nginx-controller. E4–E5 need the Gateway API CRDs:
k apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.6.2/standard-install.yaml
Careful: the CRDs let you create and validate the objects, but they do not route traffic — that also needs an implementation (nginx-gateway-fabric, Istio, Cilium). E6, T1 and T2 work as-is with Calico. The exercises use namespaces ing (E1–E5) and prod/dev (E6, T1, T2); apply the policy ones one at a time, deleting the previous.
Preparazione. Snapshot prima di tutto. E1–E3 richiedono un ingress controller — fissa la versione, non usare main, che è un ramo mobile:
k apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.13.9/deploy/static/provider/baremetal/deploy.yaml
Il controller esce come NodePort: prendi le porte con k -n ingress-nginx get svc ingress-nginx-controller. E4–E5 richiedono i CRD di Gateway API:
k apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.6.2/standard-install.yaml
Attenzione: i CRD ti permettono di creare e validare gli oggetti, ma non instradano traffico — per quello serve anche un'implementazione (nginx-gateway-fabric, Istio, Cilium). E6, T1 e T2 funzionano così come sono con Calico. Gli esercizi usano i namespace ing (E1–E5) e prod/dev (E6, T1, T2); applica quelli delle policy uno alla volta, cancellando il precedente.
E1

Un Ingress, dos backends

One Ingress, two backends

Un Ingress, due backend

~6 min

Tenés dos Deployments expuestos como Services: web:80 y api:8080. Creá un solo Ingress de clase nginx para el host demo.io que mande /api a api y todo lo demás a web. Probalo sin tocar el DNS.

You have two Deployments exposed as Services: web:80 and api:8080. Create a single Ingress of class nginx for the host demo.io sending /api to api and everything else to web. Test it without touching DNS.

Hai due Deployment esposti come Service: web:80 e api:8080. Crea un solo Ingress di classe nginx per l'host demo.io che mandi /api ad api e tutto il resto a web. Provalo senza toccare il DNS.

Ver soluciónShow solutionMostra soluzione
manifest · soluciónmanifest · solutionmanifest · soluzione
# http-echo en los dos: la salida es una palabra, y el E2 se puede leer# http-echo on both: the output is one word, so E2 stays readable# http-echo su entrambi: l'output è una parola, e l'E2 resta leggibile
k create deploy web --image=hashicorp/http-echo -- -text=web -listen=:5678
k expose deploy web --port=80 --target-port=5678
k create deploy api --image=hashicorp/http-echo -- -text=api -listen=:8080
k expose deploy api --port=8080

# --rule se repite; el orden no importa (gana el path más largo)# --rule repeats; order does not matter (longest path wins)# --rule si ripete; l'ordine non conta (vince il path più lungo)
k create ingress demo --class=nginx \
  --rule="demo.io/api*=api:8080" \
  --rule="demo.io/*=web:80"

k get ing demo && k describe ing demo

# el NodePort del controller, y el Host a mano# the controller's NodePort, and the Host by hand# il NodePort del controller, e l'Host a mano
k -n ingress-nginx get svc ingress-nginx-controller
curl -H "Host: demo.io" http://<IP-NODO>:<NODEPORT>/api
curl -H "Host: demo.io" http://<IP-NODO>:<NODEPORT>/
Claves
Key points
Punti chiave
  • El * del --rule es lo que genera pathType: Prefix. Sin él, kubectl te pone Exact: mirá el YAML generado antes de aplicar.
  • Hay dos Services en el camino — el del controller y el de tu app. Cuando falla, verificá los dos.
  • El -H "Host:" te evita tocar /etc/hosts: el controller rutea por el header, no por DNS real.
  • The * in --rule is what produces pathType: Prefix. Without it, kubectl writes Exact: read the generated YAML before applying.
  • There are two Services on the path — the controller's and your app's. When it fails, check both.
  • -H "Host:" saves you editing /etc/hosts: the controller routes on the header, not on real DNS.
  • L'* in --rule è ciò che produce pathType: Prefix. Senza, kubectl scrive Exact: leggi lo YAML generato prima di applicare.
  • Ci sono due Service sul percorso — quello del controller e quello della tua app. Quando fallisce, controllali entrambi.
  • -H "Host:" ti evita di toccare /etc/hosts: il controller instrada sull'header, non sul DNS reale.
E2

pathType: el caso /apifoo

pathType: the /apifoo case

pathType: il caso /apifoo

~5 min

Sobre el Ingress del E1, predecí antes de probar a qué Service va cada request: /api, /api/v1/users, /apifoo, /. Después agregá una regla /salud con pathType: Exact y predecí /salud y /salud/x. Verificá todas con curl.

On E1's Ingress, predict before testing which Service each request goes to: /api, /api/v1/users, /apifoo, /. Then add a /salud rule with pathType: Exact and predict /salud and /salud/x. Verify them all with curl.

Sull'Ingress dell'E1, prevedi prima di provare a quale Service va ogni richiesta: /api, /api/v1/users, /apifoo, /. Poi aggiungi una regola /salud con pathType: Exact e prevedi /salud e /salud/x. Verificale tutte con curl.

Ver soluciónShow solutionMostra soluzione
manifest · soluciónmanifest · solutionmanifest · soluzione
/api            → api    # Prefix cubre también el path exacto# Prefix also covers the exact path# Prefix copre anche il path esatto
/api/v1/users   → api    # segmentos: /api + resto# segments: /api + rest# segmenti: /api + resto
/apifoo         → web    # "apifoo" es OTRO segmento, no /api# "apifoo" is ANOTHER segment, not /api# "apifoo" è UN ALTRO segmento, non /api
/               → web

/salud          → salud  # Exact matchea justo esto# Exact matches exactly this# Exact corrisponde esattamente a questo
/salud/x        → web    # Exact NO cubre subpaths# Exact does NOT cover subpaths# Exact NON copre i subpath

# primero el backend, o la regla apunta a un Service que no existe# the backend first, or the rule points at a Service that does not exist# prima il backend, o la regola punta a un Service inesistente
k create deploy salud --image=hashicorp/http-echo -- -text=salud -listen=:5678
k expose deploy salud --port=80 --target-port=5678
k edit ing demo   # agregar la regla /salud con pathType: Exact# add the /salud rule with pathType: Exact# aggiungi la regola /salud con pathType: Exact
for p in /api /api/v1/users /apifoo / /salud /salud/x; do
  echo -n "$p -> "; curl -s -H "Host: demo.io" http://<IP>:<NP>$p
done
Claves
Key points
Punti chiave
  • Prefix compara segmentos, no texto. Es el detalle que hace fallar este tema.
  • Gana el path más largo, no el que escribiste primero. El orden en el YAML es irrelevante.
  • Exact no cubre subpaths. Usalo solo cuando el enunciado diga literalmente "solo esa ruta".
  • Prefix compares segments, not text. It is the detail that fails this topic.
  • The longest path wins, not the one you wrote first. Order in the YAML is irrelevant.
  • Exact does not cover subpaths. Use it only when the wording literally says "only that route".
  • Prefix confronta segmenti, non testo. È il dettaglio che fa fallire questo tema.
  • Vince il path più lungo, non quello scritto per primo. L'ordine nello YAML è irrilevante.
  • Exact non copre i subpath. Usalo solo quando il testo dice letteralmente "solo quella rotta".
E3

TLS en el Ingress

TLS on the Ingress

TLS sull'Ingress

~6 min

Serví demo.io por HTTPS con un certificado auto-firmado. Creá el Secret, referencialo en el Ingress y verificá con curl -k que el certificado servido es el tuyo (mirá el CN). Extra: creá el Secret en otro namespace a propósito y observá qué certificado sirve el controller.

Serve demo.io over HTTPS with a self-signed certificate. Create the Secret, reference it in the Ingress and verify with curl -k that the served certificate is yours (check the CN). Extra: create the Secret in another namespace on purpose and observe which certificate the controller serves.

Servi demo.io in HTTPS con un certificato autofirmato. Crea il Secret, referenzialo nell'Ingress e verifica con curl -k che il certificato servito sia il tuo (guarda il CN). Extra: crea il Secret in un altro namespace di proposito e osserva quale certificato serve il controller.

Ver soluciónShow solutionMostra soluzione
manifest · soluciónmanifest · solutionmanifest · soluzione
openssl req -x509 -newkey rsa:2048 -nodes -days 365 \
  -keyout tls.key -out tls.crt -subj "/CN=demo.io"

k create secret tls demo-tls --cert=tls.crt --key=tls.key
k get secret demo-tls -o jsonpath='{.type}'   #  kubernetes.io/tls

k edit ing demo
#   spec.tls: [{hosts: [demo.io], secretName: demo-tls}]#   spec.tls: [{hosts: [demo.io], secretName: demo-tls}]#   spec.tls: [{hosts: [demo.io], secretName: demo-tls}]

curl -kv --resolve demo.io:<HTTPS-NP>:<IP> https://demo.io:<HTTPS-NP>/ 2>&1 | grep -i subject
#   CN=demo.io  → sirve el tuyo#   CN=demo.io  → yours is being served#   CN=demo.io  → serve il tuo
#   CN=Kubernetes Ingress Controller Fake Certificate → el secret no lo encontró#   CN=Kubernetes Ingress Controller Fake Certificate → it did not find the secret#   CN=Kubernetes Ingress Controller Fake Certificate → non ha trovato il secret
Claves
Key points
Punti chiave
  • El Secret va en el mismo namespace que el Ingress. Si no, el controller cae en su certificado falso: el síntoma es "anda pero el navegador se queja de otro CN", no un error.
  • El tipo tiene que ser kubernetes.io/tls, con las claves tls.crt y tls.key. create secret tls te lo garantiza; un generic no.
  • El TLS termina en el controller: de ahí al Service el tráfico va en claro dentro del cluster.
  • The Secret goes in the same namespace as the Ingress. Otherwise the controller falls back to its fake certificate: the symptom is "it works but the browser complains about another CN", not an error.
  • The type must be kubernetes.io/tls, with the keys tls.crt and tls.key. create secret tls guarantees that; a generic does not.
  • TLS terminates at the controller: from there to the Service the traffic is in the clear inside the cluster.
  • Il Secret va nello stesso namespace dell'Ingress. Altrimenti il controller ripiega sul suo certificato finto: il sintomo è "funziona ma il browser si lamenta di un altro CN", non un errore.
  • Il tipo deve essere kubernetes.io/tls, con le chiavi tls.crt e tls.key. create secret tls te lo garantisce; un generic no.
  • Il TLS termina al controller: da lì al Service il traffico è in chiaro dentro il cluster.
E4

Traducir el Ingress a HTTPRoute

Translate the Ingress into an HTTPRoute

Tradurre l'Ingress in HTTPRoute

~8 min

Es el ejercicio que más probable te tomen. Tomá el Ingress del E1 y escribí la HTTPRoute equivalente contra un Gateway prod-gw que vive en el namespace infra. Sin kubectl create: a mano, apoyándote en k explain. Después listá qué se te fue en la traducción (qué campo de Ingress no tiene lugar en la HTTPRoute, y dónde está ahora).

The most likely exam ask. Take E1's Ingress and write the equivalent HTTPRoute against a Gateway prod-gw living in the infra namespace. No kubectl create: by hand, leaning on k explain. Then list what was lost in translation (which Ingress field has no place in the HTTPRoute, and where it lives now).

L'esercizio più probabile all'esame. Prendi l'Ingress dell'E1 e scrivi l'HTTPRoute equivalente su un Gateway prod-gw che vive nel namespace infra. Senza kubectl create: a mano, appoggiandoti a k explain. Poi elenca cosa si è perso nella traduzione (quale campo dell'Ingress non ha posto nell'HTTPRoute, e dove sta ora).

Ver soluciónShow solutionMostra soluzione
manifest · soluciónmanifest · solutionmanifest · soluzione
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: demo-route
spec:
  parentRefs:
  - name: prod-gw
    namespace: infra
  hostnames: ["demo.io"]
  rules:
  - matches:
    - path: {type: PathPrefix, value: /api}
    backendRefs:
    - {name: api, port: 8080}
  - matches:
    - path: {type: PathPrefix, value: /}
    backendRefs:
    - {name: web, port: 80}

# verificar que se enganchó al Gateway# check it attached to the Gateway# verificare che si sia agganciata al Gateway
k describe httproute demo-route
#   CON implementación instalada → status.parents: Accepted=True#   WITH an implementation installed → status.parents: Accepted=True#   CON un'implementazione installata → status.parents: Accepted=True
#   con SOLO los CRDs           → status.parents VACÍO, y el Gateway PROGRAMMED=Unknown#   with ONLY the CRDs          → status.parents EMPTY, and the Gateway PROGRAMMED=Unknown#   con i SOLI CRD              → status.parents VUOTO, e il Gateway PROGRAMMED=Unknown
#   Nadie escribe el status si no hay controller. El YAML igual es correcto.#   Nobody writes the status without a controller. The YAML is still correct.#   Nessuno scrive lo status senza controller. Lo YAML è comunque corretto.
Claves
Key points
Punti chiave
  • El diccionario: ingressClassName→ nada (está en el Gateway) · host→hostnames (lista) · pathType: Prefix→type: PathPrefix · backend.service→backendRefs (lista, con pesos).
  • Lo que se fue: el listener y el TLS. En Ingress el puerto y el certificado estaban en el mismo objeto; en Gateway API viven en el Gateway, que es del admin. Esa es justamente la separación de roles.
  • Las dos puntas consienten: el Gateway con allowedRoutes, la ruta con parentRefs. Si Accepted=False, mirá esos dos campos antes que nada.
  • Sin comando imperativo, la doc es tu herramienta. k explain httproute.spec.rules --recursive funciona offline y te da la estructura entera.
  • Los CRDs te dan los objetos, no el ruteo. Instalar Gateway API te deja crear y validar Gateways y HTTPRoutes — que es lo que pide el examen — pero nada mueve tráfico hasta que instales una implementación (nginx-gateway-fabric, Istio, Cilium). Con solo CRDs el status queda en blanco: no es un error tuyo.
  • The dictionary: ingressClassName→ nothing (it is on the Gateway) · host→hostnames (a list) · pathType: Prefix→type: PathPrefix · backend.service→backendRefs (a list, with weights).
  • What was lost: the listener and the TLS. In Ingress the port and the certificate lived in the same object; in Gateway API they live on the Gateway, which belongs to the admin. That is exactly the role separation.
  • Both ends consent: the Gateway via allowedRoutes, the route via parentRefs. If Accepted=False, look at those two fields before anything else.
  • With no imperative command, the docs are your tool. k explain httproute.spec.rules --recursive works offline and gives you the whole structure.
  • The CRDs give you the objects, not the routing. Installing Gateway API lets you create and validate Gateways and HTTPRoutes — which is what the exam asks — but nothing moves traffic until you install an implementation (nginx-gateway-fabric, Istio, Cilium). With CRDs alone the status stays blank: that is not your mistake.
  • Il dizionario: ingressClassName→ nulla (sta sul Gateway) · host→hostnames (lista) · pathType: Prefix→type: PathPrefix · backend.service→backendRefs (lista, con pesi).
  • Cosa si è perso: il listener e il TLS. In Ingress la porta e il certificato stavano nello stesso oggetto; in Gateway API vivono sul Gateway, che è dell'admin. È esattamente la separazione dei ruoli.
  • Entrambe le parti acconsentono: il Gateway con allowedRoutes, la route con parentRefs. Se Accepted=False, guarda quei due campi prima di tutto.
  • Senza comando imperativo, la documentazione è il tuo strumento. k explain httproute.spec.rules --recursive funziona offline e ti dà l'intera struttura.
  • I CRD ti danno gli oggetti, non l'instradamento. Installare Gateway API ti permette di creare e validare Gateway e HTTPRoute — che è ciò che chiede l'esame — ma nulla muove traffico finché non installi un'implementazione (nginx-gateway-fabric, Istio, Cilium). Coi soli CRD lo status resta vuoto: non è un tuo errore.
E5

Canary con HTTPRoute (90/10)

Canary with HTTPRoute (90/10)

Canary con HTTPRoute (90/10)

~6 min

Con dos Services web-v1 y web-v2 (que devuelvan textos distintos), hacé que demo.io/ mande el 90% del tráfico a v1 y el 10% a v2. Verificalo con 20 requests seguidos y contá. Después movelo a 50/50 sin recrear la ruta.

With two Services web-v1 and web-v2 (returning different text), make demo.io/ send 90% of traffic to v1 and 10% to v2. Verify with 20 requests in a row and count. Then move it to 50/50 without recreating the route.

Con due Service web-v1 e web-v2 (che restituiscano testi diversi), fai in modo che demo.io/ mandi il 90% del traffico a v1 e il 10% a v2. Verifica con 20 richieste di fila e conta. Poi passa a 50/50 senza ricreare la route.

Ver soluciónShow solutionMostra soluzione
manifest · soluciónmanifest · solutionmanifest · soluzione
  rules:
  - matches:
    - path: {type: PathPrefix, value: /}
    backendRefs:
    - {name: web-v1, port: 80, weight: 90}
    - {name: web-v2, port: 80, weight: 10}

# contar la distribución# count the distribution# contare la distribuzione
for i in $(seq 20); do curl -s -H "Host: demo.io" http://<IP>:<NP>/; done | sort | uniq -c

k edit httproute demo-route     # weight: 50 y 50# weight: 50 and 50# weight: 50 e 50
Claves
Key points
Punti chiave
  • Esto es lo que Ingress no podía hacer sin anotaciones propietarias. Es el argumento más concreto de por qué existe Gateway API: lo que antes era específico de nginx ahora es un campo de la spec.
  • Los weight son proporciones relativas, no porcentajes: 9 y 1 dan lo mismo que 90 y 10.
  • Con 20 requests la muestra es chica: no esperes exactamente 18/2. La proporción se ve en el orden de magnitud.
  • Contar requests solo funciona con una implementación de Gateway API instalada. Con los CRDs pelados la ruta se crea pero no reparte nada: quedate con escribir bien el YAML, que es lo que se puntúa.
  • This is what Ingress could not do without proprietary annotations. It is the most concrete argument for Gateway API: what used to be nginx-specific is now a spec field.
  • weight values are relative proportions, not percentages: 9 and 1 give the same as 90 and 10.
  • With 20 requests the sample is small: do not expect exactly 18/2. The proportion shows in the order of magnitude.
  • Counting requests only works with a Gateway API implementation installed. With bare CRDs the route is created but splits nothing: settle for writing the YAML correctly, which is what scores.
  • Questo è ciò che Ingress non poteva fare senza annotazioni proprietarie. È l'argomento più concreto per Gateway API: ciò che era specifico di nginx ora è un campo della spec.
  • I weight sono proporzioni relative, non percentuali: 9 e 1 danno lo stesso di 90 e 10.
  • Con 20 richieste il campione è piccolo: non aspettarti esattamente 18/2. La proporzione si vede nell'ordine di grandezza.
  • Contare le richieste funziona solo con un'implementazione di Gateway API installata. Coi soli CRD la route si crea ma non distribuisce nulla: accontentati di scrivere bene lo YAML, che è ciò che dà punti.
E6

Default deny y la excepción justa

Default deny and the exact exception

Default deny e l'eccezione precisa

~8 min

En el namespace prod hay tres pods: web (app=web), cache (app=cache) y db (app=db, escucha en 5432). Verificá primero que todos llegan a db. Después hacé que solo web pueda hablarle a db en el 5432, y nadie más. Volvé a verificar los tres.

In namespace prod there are three pods: web (app=web), cache (app=cache) and db (app=db, listening on 5432). First verify that all reach db. Then make it so only web can talk to db on 5432, and nobody else. Re-verify all three.

Nel namespace prod ci sono tre pod: web (app=web), cache (app=cache) e db (app=db, in ascolto su 5432). Verifica prima che tutti raggiungano db. Poi fai in modo che solo web possa parlare a db sulla 5432, e nessun altro. Riverifica tutti e tre.

Ver soluciónShow solutionMostra soluzione
manifest · soluciónmanifest · solutionmanifest · soluzione
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: {name: db-solo-web, namespace: prod}
spec:
  podSelector:
    matchLabels: {app: db}      # el DESTINO, nunca el origen# the DESTINATION, never the source# la DESTINAZIONE, mai l'origine
  policyTypes: [Ingress]
  ingress:
  - from:
    - podSelector:
        matchLabels: {app: web}
    ports:
    - {protocol: TCP, port: 5432}

# verificar desde cada origen (el mismo comando, distinto pod)# verify from each source (same command, different pod)# verificare da ogni origine (stesso comando, pod diverso)
k exec -n prod web -- nc -zvw2 db 5432         # open
k exec -n prod cache -- nc -zvw2 db 5432    # timeout# timeout# timeout
Claves
Key points
Punti chiave
  • El podSelector de arriba es el destino, no el origen. Es el error de dirección más común: leé el enunciado y preguntate "¿a quién estoy protegiendo?".
  • No hace falta un deny-all aparte: esta policy ya aísla a db por el solo hecho de seleccionarlo. El deny-all explícito se usa cuando querés cerrar un namespace entero de una.
  • El port es el del container, no el del Service. Las policies trabajan sobre IPs de pod.
  • Verificá con TCP crudo (nc), no con curl a un puerto que no habla HTTP: el error te confundiría.
  • The top-level podSelector is the destination, not the source. It is the most common direction mistake: read the wording and ask "who am I protecting?".
  • You do not need a separate deny-all: this policy already isolates db simply by selecting it. An explicit deny-all is for closing a whole namespace at once.
  • The port is the container's, not the Service's. Policies work on pod IPs.
  • Verify with raw TCP (nc), not curl against a port that does not speak HTTP: the error would mislead you.
  • Il podSelector in alto è la destinazione, non l'origine. È l'errore di direzione più comune: leggi il testo e chiediti "chi sto proteggendo?".
  • Non serve un deny-all separato: questa policy isola già db per il solo fatto di selezionarlo. Il deny-all esplicito serve a chiudere un intero namespace in un colpo.
  • La port è quella del container, non del Service. Le policy lavorano sugli IP dei pod.
  • Verifica con TCP grezzo (nc), non con curl su una porta che non parla HTTP: l'errore ti confonderebbe.
T1

Troubleshooting — el guión de más

Troubleshooting — the extra dash

Troubleshooting — il trattino di troppo

~7 min

Una policy debía permitir a db (ns prod) recibir solo de los pods app=web del namespace dev. Pero desde dev le llega cualquier pod, y encima le llega un web de prod. Encontrá el carácter culpable y arreglalo.

A policy was meant to let db (ns prod) receive only from app=web pods in the dev namespace. But any pod from dev gets through, and on top of that a web from prod does too. Find the guilty character and fix it.

Una policy doveva permettere a db (ns prod) di ricevere solo dai pod app=web del namespace dev. Ma da dev arriva qualsiasi pod, e per giunta arriva anche un web di prod. Trova il carattere colpevole e sistemalo.

manifest · plantar el problemamanifest · plant the problemmanifest · piantare il problema
Ver soluciónShow solutionMostra soluzione
# MAL — dos ítems de lista: OR# WRONG — two list items: OR# SBAGLIATO — due elementi di lista: OR
  ingress:
  - from:
    - namespaceSelector:
        matchLabels: {kubernetes.io/metadata.name: dev}
    - podSelector:                   # ← el guión
        matchLabels: {app: web}

# BIEN — un solo ítem con dos selectores: AND# RIGHT — a single item with two selectors: AND# GIUSTO — un solo elemento con due selector: AND
  ingress:
  - from:
    - namespaceSelector:
        matchLabels: {kubernetes.io/metadata.name: dev}
      podSelector:                   # ← sin guión
        matchLabels: {app: web}
Claves
Key points
Punti chiave
  • Un guión = un ítem nuevo = OR. Selectores dentro del mismo ítem = AND. Es la trampa número uno del tema y un cambio de un solo carácter.
  • El segundo síntoma delata el resto: un podSelector suelto (sin namespaceSelector al lado) se refiere al namespace de la policy. Por eso entraba también el web de prod.
  • Verificá siempre desde varios orígenes, no solo desde el que querías permitir: un OR de más solo se nota probando lo que no debería pasar.
  • One dash = one new item = OR. Selectors within the same item = AND. It is the number-one trap here, and a one-character change.
  • The second symptom gives away the rest: a bare podSelector (with no namespaceSelector beside it) refers to the policy's namespace. That is why prod's web got in too.
  • Always verify from several sources, not just the one you meant to allow: an extra OR only shows up by testing what should not pass.
  • Un trattino = un nuovo elemento = OR. Selector dentro lo stesso elemento = AND. È la trappola numero uno del tema, e una modifica di un solo carattere.
  • Il secondo sintomo svela il resto: un podSelector isolato (senza namespaceSelector accanto) si riferisce al namespace della policy. Per questo entrava anche il web di prod.
  • Verifica sempre da più origini, non solo da quella che volevi permettere: un OR di troppo si nota solo provando ciò che non dovrebbe passare.
T2

Troubleshooting — el egress que mató el DNS

Troubleshooting — the egress that killed DNS

Troubleshooting — l'egress che ha ucciso il DNS

~7 min

Aplicaste un default-deny de [Ingress, Egress] en prod y después una regla de egress que permite hablarle a db. Aun así, los pods de prod no llegan a db: bad address 'db'. Diagnosticá y arreglá.

You applied a default-deny with [Ingress, Egress] in prod, then an egress rule allowing traffic to db. Still, prod pods cannot reach db: bad address 'db'. Diagnose and fix.

Hai applicato un default-deny con [Ingress, Egress] in prod, poi una regola di egress che permette il traffico verso db. Eppure i pod di prod non raggiungono db: bad address 'db'. Diagnostica e sistema.

manifest · plantar el problemamanifest · plant the problemmanifest · piantare il problema
Ver soluciónShow solutionMostra soluzione
# la pista está en el mensaje: no es "connection refused", es "bad address"# the clue is in the message: it is not "connection refused", it is "bad address"# l'indizio è nel messaggio: non è "connection refused", è "bad address"
# → falla la RESOLUCIÓN, no la conexión. El egress bloqueó el DNS.# → RESOLUTION fails, not the connection. Egress blocked DNS.# → fallisce la RISOLUZIONE, non la connessione. L'egress ha bloccato il DNS.

k exec -n prod web -- nc -zvw2 <IP-DEL-POD-DB> 5432   # por IP SÍ anda# by IP it DOES work# per IP funziona

# agregar la salida al DNS: UDP y TCP# add the DNS egress: UDP and TCP# aggiungere l'egress verso il DNS: UDP e TCP
  egress:
  - to:
    - namespaceSelector:
        matchLabels: {kubernetes.io/metadata.name: kube-system}
    ports:
    - {protocol: UDP, port: 53}
    - {protocol: TCP, port: 53}

k exec -n prod web -- nc -zvw2 db 5432             # open
Claves
Key points
Punti chiave
  • Un deny-all de egress rompe el DNS y con eso rompe todo. Es la consecuencia menos intuitiva del tema, y la que más tiempo hace perder.
  • El mismo truco de M6: "por IP sí, por nombre no" = DNS. Ese contraste te ahorra media hora.
  • Abrí el 53 en UDP y TCP: las respuestas grandes se reintentan por TCP, así que con solo UDP funciona a veces — el peor tipo de bug.
  • Regla práctica: si ponés Egress en policyTypes, escribí la regla del DNS en el mismo momento. No es un extra, es parte del default-deny.
  • An egress deny-all breaks DNS and with it everything. It is the least intuitive consequence here, and the one that costs the most time.
  • The same trick as M6: "by IP yes, by name no" = DNS. That contrast saves you half an hour.
  • Open 53 on UDP and TCP: large answers retry over TCP, so UDP alone works sometimes — the worst kind of bug.
  • Rule of thumb: if you put Egress in policyTypes, write the DNS rule at the same moment. It is not an extra, it is part of the default-deny.
  • Un deny-all di egress rompe il DNS e con esso tutto. È la conseguenza meno intuitiva del tema, e quella che fa perdere più tempo.
  • Lo stesso trucco del M6: "per IP sì, per nome no" = DNS. Quel contrasto ti risparmia mezz'ora.
  • Apri la 53 in UDP e TCP: le risposte grandi si ritentano via TCP, quindi con il solo UDP funziona a volte — il peggior tipo di bug.
  • Regola pratica: se metti Egress nei policyTypes, scrivi la regola del DNS nello stesso momento. Non è un extra, è parte del default-deny.

Autoevaluación — antes de marcar M7 como ★ dominado

Self-check — before marking M7 as ★ mastered

Autovalutazione — prima di segnare M7 come ★ padroneggiato

  • Crear un Ingress de dos reglas imperativamente, en menos de 1 min.
  • Predecir el matcheo de Prefix y Exact sin probar, incluido /apifoo.
  • Montar TLS y saber en qué namespace va el secret y de qué tipo.
  • Escribir una HTTPRoute de memoria y decir qué campo del Ingress ya no le corresponde.
  • Hacer un split de tráfico con pesos.
  • Escribir "solo X puede hablarle a Y en el puerto P" sin dudar de la dirección.
  • Distinguir AND de OR en un from mirando la indentación.
  • Reconocer al instante que un egress sin regla de DNS es lo que rompió todo.
  • Create a two-rule Ingress imperatively, in under 1 min.
  • Predict Prefix and Exact matching without testing, /apifoo included.
  • Set up TLS and know which namespace the secret goes in and of what type.
  • Write an HTTPRoute from memory and say which Ingress field no longer belongs to it.
  • Do a weighted traffic split.
  • Write "only X may talk to Y on port P" without hesitating on the direction.
  • Tell AND from OR in a from block by looking at the indentation.
  • Instantly recognise that an egress with no DNS rule is what broke everything.
  • Creare un Ingress di due regole in modo imperativo, in meno di 1 min.
  • Prevedere la corrispondenza di Prefix ed Exact senza provare, /apifoo incluso.
  • Configurare il TLS e sapere in quale namespace va il secret e di che tipo.
  • Scrivere un HTTPRoute a memoria e dire quale campo dell'Ingress non gli appartiene più.
  • Fare uno split di traffico con i pesi.
  • Scrivere "solo X può parlare a Y sulla porta P" senza esitare sulla direzione.
  • Distinguere AND da OR in un blocco from guardando l'indentazione.
  • Riconoscere all'istante che un egress senza regola DNS è ciò che ha rotto tutto.
Módulo 7 completado · Services & Networking, parte 2 de 2 · Siguiente: Storage — volumes, PV/PVC y StorageClasses
Module 7 complete · Services & Networking, part 2 of 2 · Next: Storage — volumes, PV/PVC and StorageClasses
Modulo 7 completato · Services & Networking, parte 2 di 2 · Prossimo: Storage — volume, PV/PVC e StorageClass