404 page not found en un Ingress de k3s: por qué Traefik no llega a tu aplicación
Creaste el Deployment, el Service y el Ingress, kubectl apply no se quejó de nada, y
curl devuelve 404 page not found. Esa respuesta no viene de tu aplicación: viene de Traefik,
el controlador de Ingress que trae k3s, y quiere decir que ninguna de sus reglas coincidió con tu petición.
Paso 1. Confirmar quién responde el 404
$ kubectl get ingressclass
$ kubectl -n <ns> get ingress
$ kubectl -n <ns> describe ingress <nombre>
En get ingress mira tres columnas: CLASS, HOSTS y ADDRESS. En
describe, cada ruta lista el Service y, entre paréntesis, las direcciones de los Pods que hay detrás. Si
ves esas direcciones, Ingress, Service y Pods están conectados y el problema está en cómo haces la petición. Si
ADDRESS sigue vacío pasados unos segundos, ningún controlador tomó tu Ingress.
Paso 2. Ubicar tu caso en la tabla
| Lo que ves | Causa | Corrección |
|---|---|---|
curl a la IP da 404 aunque todo está bien | Falta el nombre de host: el Ingress solo atiende las peticiones cuyo encabezado Host coincide con host: | Pide con el nombre: curl --resolve o -H "Host: app.ejemplo.com" |
ADDRESS vacío para siempre y 404 | ingressClassName no coincide con ninguna clase de get ingressclass; Kubernetes acepta cualquier nombre sin dar error | Pon el valor de la columna NAME: traefik en k3s, nginx con NGINX Ingress |
describe no muestra direcciones de Pods en la ruta | El Ingress apunta a un Service de otro namespace; un Ingress solo envía tráfico a Services de su propio namespace | Crea el Ingress en el mismo namespace que el Service |
https:// funciona y http:// da 404 | En Traefik, un Ingress con spec.tls no atiende el puerto 80 | Un segundo Ingress, sin spec.tls, para el puerto 80 |
Could not resolve host | El nombre no existe en ningún DNS (sistema de nombres de dominio) | curl --resolve o una línea en /etc/hosts |
Paso 3. Probar con el nombre, sin tocar el DNS
$ IP_INGRESS=$(kubectl -n <ns> get ingress <nombre> -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
$ curl -i http://"$IP_INGRESS"/ # sin nombre: 404 del controlador
$ curl -i --resolve app.ejemplo.com:80:"$IP_INGRESS" http://app.ejemplo.com/ # con nombre: tu aplicación
--resolve le dice a curl a qué IP ir para ese nombre, y manda el encabezado
Host correcto. Si la primera da 404 y la segunda responde tu aplicación, no hay nada roto: solo faltaba
el nombre. Ese 404 es buena señal, porque prueba que el controlador está vivo.
El YAML corregido
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tienda
namespace: tienda # el mismo namespace del Service
spec:
ingressClassName: traefik # el NAME de kubectl get ingressclass
rules:
- host: app.ejemplo.com # solo atiende peticiones con este Host
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: tienda
port:
name: http # el puerto del Service por su nombre
Si el 404 desaparece pero la aplicación no contesta, el siguiente sospechoso es el Service: revisa que tenga endpoints con la guía del Service que no responde.
¿Quieres provocarlo y arreglarlo sin romper nada tuyo? El laboratorio
04 del kit monta un Ingress con tres rutas, lo prueba con --resolve y con /etc/hosts, y
en el paso 11 te pone una clase que no existe para que veas el síntoma completo.
Por qué pasa tanto
Porque un Ingress mal escrito no da ningún error: Kubernetes guarda el objeto aunque la clase no exista o el Service esté en otro namespace, y el controlador simplemente no encuentra una regla que coincida. El único aviso es ese 404 genérico, que se parece mucho a "mi aplicación no tiene esa ruta" y manda a buscar donde no es.
Practica Kubernetes en tu propio clúster, gratis
Te envío al correo dos laboratorios completos con su solución: el 01 (namespace y deployment) y el 08 (ResourceQuota). Cada uno termina con un comando de verificación, no con "parece que funcionó". El Ingress con Traefik es el laboratorio 04 del kit.