## Introduction

Ingress achemine le trafic HTTP(S) externe vers des Services à l’intérieur de votre cluster, mais la ressource Ingress seule n’expose rien. Une requête qui fonctionne traverse plusieurs couches qui doivent toutes être saines. La plupart des erreurs Kubernetes Ingress ne viennent pas d’un simple champ YAML isolé ; ce sont des pannes le long du chemin de requête à travers DNS, équilibreurs de charge, contrôleurs, Services, endpoints et Pods applicatifs.

Dépannez en testant chaque saut dans l’ordre, pas en modifiant des manifestes au hasard.

Client
 |
 DNS
 |
IP externe / Load Balancer
 |
Service du contrôleur Ingress
 |
Pod du contrôleur Ingress
 |
Règle Ingress
 |
Service applicatif
 |
EndpointSlice
 |
Pod applicatif 
 Ce que vous allez apprendre :

- Comment vérifier chaque couche avec des commandes concrètes.

- Comment diagnostiquer les erreurs Kubernetes Ingress 404/502/503/504 et TLS.

- Comment séparer l’API Ingress de l’implémentation du contrôleur.

Concept clé : un Ingress est une configuration consommée par un contrôleur Ingress. Créer une ressource Ingress n’installe ni ne configure de contrôleur.

## Construire un exemple minimal fonctionnel

Enregistrez le fichier suivant sous example.yaml. Il crée un namespace, un Deployment HTTP d’écho simple, un Service ClusterIP et un Ingress avec routage par hôte sur le chemin "/" en pathType: Prefix . Adaptez <namespace> et <host> .

apiVersion: v1
kind: Namespace
metadata:
 name: <namespace>
---
apiVersion: apps/v1
kind: Deployment
metadata:
 name: echo
 namespace: <namespace>
spec:
 replicas: 2
 selector:
 matchLabels:
 app: echo
 template:
 metadata:
 labels:
 app: echo
 spec:
 containers:
 - name: echo
 image: hashicorp/http-echo:0.2.3
 args: ["-text=ok"]
 ports:
 - containerPort: 5678
---
apiVersion: v1
kind: Service
metadata:
 name: echo-svc
 namespace: <namespace>
spec:
 selector:
 app: echo
 ports:
 - name: http
 port: 80
 targetPort: 5678
 type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
 name: echo
 namespace: <namespace>
spec:
 ingressClassName: nginx
 rules:
 - host: <host>
 http:
 paths:
 - path: /
 pathType: Prefix
 backend:
 service:
 name: echo-svc
 port:
 number: 80 
 Appliquez et inspectez :

kubectl apply -f example.yaml
kubectl get pods,svc,ingress -n <namespace>
kubectl describe ingress echo -n <namespace> 
 Signes de bonne santé :

- Pods Ready=1/1 et STATUS=Running.

- Le Service a une ClusterIP stable et un mapping 80 -> 5678.

- L’Ingress affiche une ADDRESS (IP externe ou nom d’hôte) après son approvisionnement par le contrôleur.

## Commencez par le chemin de requête

Utilisez ce tableau express pour tester chaque couche de manière délibérée.

<div style="overflow-x:auto;margin:1.5rem 0;"><table style="width:100%;border-collapse:collapse;font-size:0.95rem;line-height:1.5;"><thead><tr><th style="border:1px solid rgba(15,23,42,.18);padding:.75rem;text-align:left;background:#f8fafc;font-weight:700;">Couche</th><th style="border:1px solid rgba(15,23,42,.18);padding:.75rem;text-align:left;background:#f8fafc;font-weight:700;">Symptôme typique</th><th style="border:1px solid rgba(15,23,42,.18);padding:.75rem;text-align:left;background:#f8fafc;font-weight:700;">Commande de diagnostic</th><th style="border:1px solid rgba(15,23,42,.18);padding:.75rem;text-align:left;background:#f8fafc;font-weight:700;">Succès attendu</th><th style="border:1px solid rgba(15,23,42,.18);padding:.75rem;text-align:left;background:#f8fafc;font-weight:700;">Correctif probable</th></tr></thead><tbody><tr><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">DNS</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">L’hôte ne se résout pas</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">&lt;code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded"&gt;dig +short &lt;host&gt;&lt;/code&gt;</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Renvoie une IP ou un nom d’hôte LB</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Créer/ajuster un enregistrement A/AAAA/CNAME</td></tr><tr><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Load Balancer externe</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Connexion refusée/timeout</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">&lt;code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded"&gt;curl -v http://&lt;host&gt;/&lt;/code&gt;</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Connexion TCP réussie</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Approvisionner le LB, ouvrir pare-feu/SG, attendre l’approvisionnement</td></tr><tr><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Service du contrôleur</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">ADDRESS vide dans l’Ingress</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">&lt;code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded"&gt;kubectl get svc -A&lt;/code&gt;</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Le Service du contrôleur a une EXTERNAL-IP ou un NodePort</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Configurer le type de Service, corriger l’intégration cloud/LB</td></tr><tr><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Pods du contrôleur</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">503/timeout</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">&lt;code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded"&gt;kubectl get pods -A&lt;/code&gt;</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Pods du contrôleur Running/Ready</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Installer/réparer le contrôleur</td></tr><tr><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">IngressClass</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Ingress ignoré</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">&lt;code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded"&gt;kubectl get ingressclass&lt;/code&gt;</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">La classe correspondante existe et est par défaut ou nommée dans le spec</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Renseigner correctement &lt;code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded"&gt;spec.ingressClassName&lt;/code&gt;</td></tr><tr><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Ressource Ingress</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">404 du backend par défaut</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">&lt;code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded"&gt;kubectl describe ingress&lt;/code&gt;</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Accepté, règles listées, pas d’erreurs</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Corriger hôte/chemin, annotations, classe</td></tr><tr><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Service backend</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">502/503</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">&lt;code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded"&gt;kubectl get svc &lt;svc&gt; -o yaml&lt;/code&gt;</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Port/sélecteur corrects</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Corriger nom/port/sélecteur</td></tr><tr><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">EndpointSlice</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">503/504</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">&lt;code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded"&gt;kubectl get endpointslice -l kubernetes.io/service-name=&lt;svc&gt; -n &lt;ns&gt;&lt;/code&gt;</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Adresses avec Ready=true</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Corriger le sélecteur, readiness des Pods</td></tr><tr><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Pods applicatifs</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">502/504</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">&lt;code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded"&gt;kubectl describe pod &lt;pod&gt;&lt;/code&gt;</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Ready, écoute sur 0.0.0.0</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Corriger probes, adresse d’écoute, arguments du conteneur</td></tr><tr><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">TLS</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Avertissements navigateur</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">&lt;code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded"&gt;openssl s_client -connect &lt;host&gt;:443 -servername &lt;host&gt;&lt;/code&gt;</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Certificat correspondant à l’hôte, non expiré</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Corriger Secret, SAN, chaîne</td></tr><tr><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Réponse de l’app</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">404 applicatif</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">&lt;code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded"&gt;curl -v -H "Host: &lt;host&gt;" http://&lt;ingress-ip&gt;/&lt;/code&gt;</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">En-têtes/corps de l’app attendus</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Corriger routes ou réécritures de l’app</td></tr></tbody></table></div>

## Erreur 1 : aucun contrôleur Ingress n’est installé

Un objet Ingress n’est qu’une configuration. Sans contrôleur, rien n’écoute.

kubectl get pods -A | grep -E "ingress|traefik|haproxy|alb|gateway"
kubectl get deployments -A | grep -E "ingress|traefik|haproxy|alb|gateway"
kubectl get ingressclass 
 Cherchez ingress-nginx, Traefik, HAProxy ou le contrôleur de votre fournisseur cloud. L’installation diffère selon bare metal, minikube/kind, Kubernetes managé et machines virtuelles. Utilisez la méthode documentée de votre plateforme.

## Erreur 2 : IngressClass manquante ou incorrecte

Le contrôleur qui traite votre Ingress est choisi par spec.ingressClassName ou par l’IngressClass par défaut du cluster.

kubectl get ingressclass
kubectl describe ingressclass <class-name>
kubectl get ingress echo -n <namespace> -o yaml 
 Exemple de spec :

spec:
 ingressClassName: nginx 
 Utilisez le nom de classe annoncé par votre contrôleur ; les noms sont spécifiques à l’implémentation.

## Erreur 3 : l’Ingress n’a pas d’adresse externe

kubectl get ingress -A affiche une ADDRESS vide lorsque :

- Aucun contrôleur ne gère la ressource.

- Le Service du contrôleur n’a pas d’EXTERNAL-IP ( ).

- L’équilibreur de charge est encore en cours d’approvisionnement ou n’est pas pris en charge par votre infra.

- L’intégration LoadBalancer sur bare metal (p. ex. MetalLB) est absente/mal configurée.

- Le contrôleur est exposé via NodePort à la place.

 Vérifiez :

kubectl get svc -A
kubectl describe svc <controller-service> -n <controller-namespace>
kubectl get events -A --sort-by=.metadata.creationTimestamp 
 Si EXTERNAL-IP est <pending> , résolvez d’abord le problème du Service ou de l’infrastructure.

## Erreur 4 : le DNS ne pointe pas vers l’endpoint Ingress

Validez DNS et le routage par hôte :

dig +short <host>
nslookup <host>
curl -v http://<host>/ 
 Pour contourner le DNS tout en conservant l’en-tête Host :

curl -v --resolve <host>:80:<ingress-ip> http://<host>/
curl -vk --resolve <host>:443:<ingress-ip> https://<host>/ 
 --resolve prouve que l’Ingress et le backend fonctionnent même si le DNS public est erroné.

## Erreur 5 : le backend par défaut renvoie 404

Un 404 généré par le contrôleur signifie généralement que l’hôte ou le chemin ne correspond à aucune règle, ou que le mauvais contrôleur a traité la requête.

curl -v -H "Host: <host>" http://<ingress-ip>/
kubectl describe ingress <name> -n <namespace>
kubectl get ingress <name> -n <namespace> -o yaml 
 Faites la différence :

- 404 Ingress : les en-têtes incluent souvent des identifiants propres au contrôleur (p. ex. nginx). Corrigez hôte/chemin, classe ou annotations.

- 404 applicatif : la requête est arrivée à votre app ; adaptez le routage applicatif.

## Erreur 6 : correspondance de chemin et réécritures

- pathType: Prefix correspond à /api et /api/v1 .

- pathType: Exact correspond au chemin littéralement.

- ImplementationSpecific dépend du contrôleur et n’est pas portable.

Exemple standard sans réécriture :

paths:
 - path: /
 pathType: Prefix
 backend:
 service:
 name: echo-svc
 port:
 number: 80 
 Exemple spécifique à ingress-nginx (non portable) pour réécriture :

metadata:
 annotations:
 nginx.ingress.kubernetes.io/rewrite-target: / 
 N’appliquez pas des annotations nginx à Traefik, HAProxy ou aux contrôleurs cloud.

## Erreur 7 : mauvais nom ou port de Service

Le backend Ingress doit référencer le port du Service, pas le containerPort du Pod.

backend:
 service:
 name: api-service
 port:
 number: 8080 
 Vérifiez les correspondances et les noms :

kubectl get svc <service-name> -n <namespace> -o yaml
kubectl describe svc <service-name> -n <namespace>
kubectl get deployment <deployment-name> -n <namespace> -o yaml 
 Service.port mappe vers Service.targetPort , qui mappe vers le containerPort (ou un port nommé) dans le Pod.

## Erreur 8 : le Service n’a pas d’endpoints

Un Service peut exister avec zéro endpoint prêt. Confirmez :

kubectl get endpoints <service-name> -n <namespace>
kubectl get endpointslice -n <namespace> -l kubernetes.io/service-name=<service-name>
kubectl describe svc <service-name> -n <namespace>
kubectl get pods -n <namespace> --show-labels 
 Causes courantes :

- Le sélecteur ne correspond pas aux labels des Pods.

- Les Pods ne sont pas Ready (probes en échec).

- Les Pods se terminent ou sont dans un autre namespace.

Alignez les sélecteurs avec les labels ; l’Ingress ne peut pas référencer des Services d’un autre namespace via le backend standard.

## Erreur 9 : Pods en exécution mais non Ready

Running ne signifie pas routable. Inspectez les probes et les logs :

kubectl get pods -n <namespace>
kubectl describe pod <pod-name> -n <namespace>
kubectl logs <pod-name> -n <namespace> 
 Exemple de readiness probe :

readinessProbe:
 httpGet:
 path: /healthz
 port: 5678
 initialDelaySeconds: 2
 periodSeconds: 5 
 Assurez-vous que l’app écoute sur 0.0.0.0, pas sur 127.0.0.1.

## Erreurs 10–12 : 502, 503, 504

- 502 Bad Gateway : souvent targetPort erroné, backend qui refuse la connexion, confusion HTTP vs HTTPS.

- 503 Service Unavailable : aucun endpoint prêt, Service backend manquant ou erreurs de rechargement du contrôleur.

- 504 Gateway Timeout : application lente, dépendances en latence, NetworkPolicy bloquante ou délais d’expiration du contrôleur trop courts.

Testez le Service depuis le cluster pour isoler Ingress vs backend :

kubectl run curl-test --rm -it --restart=Never \
 --image=curlimages/curl -n <namespace> -- \
 curl -v http://<service-name>:<port>/ 
 Si ce test réussit mais échoue via l’Ingress, concentrez-vous sur le contrôleur, les règles de routage ou la configuration protocole/TLS.

## Erreurs TLS : Secrets et certificats

TLS requiert un Secret de type kubernetes.io/tls dans le même namespace que l’Ingress. Les hôtes doivent correspondre aux SAN du certificat.

spec:
 tls:
 - hosts:
 - app.example.com
 secretName: app-example-tls 
 Validez :

kubectl get secret app-example-tls -n <namespace>
kubectl describe ingress <name> -n <namespace>
openssl s_client -connect <host>:443 -servername <host> | openssl x509 -noout -subject -issuer -dates -ext subjectAltName 
 Vérifiez l’expiration, la complétude de la chaîne et le nom d’hôte correct.

## Ça marche en port-forward mais pas via Ingress

kubectl port-forward deployment/<deployment> 8080:<container-port> prouve que le Pod écoute et répond. kubectl port-forward service/<service> 8080:<service-port> prouve que le Service route vers des endpoints. Si les deux fonctionnent mais pas la route publique, concentrez-vous sur le contrôleur, le DNS, TLS ou le réseau externe.

## Logs du contrôleur et configuration générée

Identifiez les Pods du contrôleur de manière générique :

kubectl get pods -A -l app.kubernetes.io/component=controller 
 Ensuite, inspectez les logs dans le bon namespace. Exemple (ingress-nginx uniquement) :

kubectl -n ingress-nginx logs deploy/ingress-nginx-controller 
 Cherchez des erreurs d’admission, des rechargements de configuration et des messages de santé des upstreams.

## Un flux de dépannage discipliné

- Confirmer le DNS : dig +short <host> .

- Confirmer l’accessibilité externe : curl -v http(s)://<host>/ .

- Contrôleur installé et Ready : kubectl get deployments,pods -A .

- IngressClass correcte : kubectl get ingressclass et spec.ingressClassName de l’Ingress.

- Inspecter l’Ingress et les événements : kubectl describe ingress .

- Tester hôte/chemin : curl -v -H "Host: <host>" http://<ingress-ip>/ .

- Vérifier nom et port du Service : kubectl describe svc .

- EndpointSlices en Ready : kubectl get endpointslice .

- Tester dans le cluster : kubectl run ... curl http://<svc>:/ .

- Consulter les logs du contrôleur.

- Consulter les logs et probes de l’application.

- Valider le Secret TLS et le nom d’hôte du certificat.

- Examiner NetworkPolicies/pare-feu.

- Re-tester depuis l’extérieur.

## Incident pratique de bout en bout

 Symptôme (exemple de sortie) :

- curl -v https://app.example.com/ renvoie 503.

Éléments observés :

kubectl get ingress echo -n demo
# ADDRESS renseignée ; règles OK
kubectl get svc echo-svc -n demo
# Port 80 -> 5678
kubectl get endpointslice -n demo -l kubernetes.io/service-name=echo-svc
# Aucun endpoint listé
kubectl get deploy echo -n demo -o yaml | grep labels -A2
# Pods du Deployment labellisés app=echo-app (décalage !) 
 Cause racine : le sélecteur du Service app=echo ne correspond pas au label des Pods app=echo-app , donc zéro endpoint prêt.

Correctif (aligner les labels) :

spec:
 selector:
 matchLabels:
 app: echo
 template:
 metadata:
 labels:
 app: echo 
 Validation :

kubectl apply -f deployment.yaml
kubectl get endpointslice -n demo -l kubernetes.io/service-name=echo-svc
# Des endpoints sont maintenant présents
curl -v https://app.example.com/
# 200 OK 
 Prévention : alertez sur les Services sans endpoints prêts ; validez les sélecteurs en CI ; testez les routes après déploiement.

## Tableau de décision express

<div style="overflow-x:auto;margin:1.5rem 0;"><table style="width:100%;border-collapse:collapse;font-size:0.95rem;line-height:1.5;"><thead><tr><th style="border:1px solid rgba(15,23,42,.18);padding:.75rem;text-align:left;background:#f8fafc;font-weight:700;">Symptôme</th><th style="border:1px solid rgba(15,23,42,.18);padding:.75rem;text-align:left;background:#f8fafc;font-weight:700;">Couche la plus probable</th><th style="border:1px solid rgba(15,23,42,.18);padding:.75rem;text-align:left;background:#f8fafc;font-weight:700;">Première commande</th><th style="border:1px solid rgba(15,23,42,.18);padding:.75rem;text-align:left;background:#f8fafc;font-weight:700;">Cause fréquente</th><th style="border:1px solid rgba(15,23,42,.18);padding:.75rem;text-align:left;background:#f8fafc;font-weight:700;">Prochaine action</th></tr></thead><tbody><tr><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Échec DNS</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">DNS</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">&lt;code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded"&gt;dig +short &lt;host&gt;&lt;/code&gt;</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Enregistrement manquant/obsolète</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Corriger A/AAAA/CNAME</td></tr><tr><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Connexion refusée</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">LB/Service</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">&lt;code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded"&gt;curl -v http://&lt;host&gt;/&lt;/code&gt;</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">LB non prêt, pare-feu</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Vérifier LB, ouvrir les ports</td></tr><tr><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Timeout</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">LB/Contrôleur/App</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">&lt;code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded"&gt;curl -v&lt;/code&gt; + logs du contrôleur</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">NetworkPolicy, app lente</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Tester le Service en cluster</td></tr><tr><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">404 contrôleur</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Règle Ingress</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">&lt;code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded"&gt;kubectl describe ingress&lt;/code&gt;</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Hôte/chemin non concordants</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Corriger règle/pathType</td></tr><tr><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">404 applicatif</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">App</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">&lt;code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded"&gt;curl -v&lt;/code&gt; + en-têtes</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Route app manquante</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Corriger routes applicatives</td></tr><tr><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">502</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Backend</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Curl en cluster vers Service</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Mauvais targetPort/protocole</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Aligner ports/protocoles</td></tr><tr><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">503</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Endpoints</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">&lt;code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded"&gt;kubectl get endpointslice&lt;/code&gt;</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Aucun endpoint prêt</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Corriger sélecteur/probes</td></tr><tr><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">504</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">App/dépendances</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Logs/metrics app</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Backend lent</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Optimiser ou ajuster timeouts</td></tr><tr><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Alerte TLS</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">TLS</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">&lt;code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded"&gt;openssl s_client ...&lt;/code&gt;</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Nom/expiry/chaîne</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Corriger Secret/certificat</td></tr><tr><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Mauvais certificat</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">TLS/SNI</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">&lt;code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded"&gt;openssl s_client -servername&lt;/code&gt;</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Hôte non concordant</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Fournir un cert par hôte</td></tr><tr><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Boucle de redirection</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Proxy/app</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">&lt;code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded"&gt;curl -IL&lt;/code&gt;</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Protocole/en-têtes</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Faire confiance à X-Forwarded-Proto, corriger redirs</td></tr><tr><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Adresse externe vide</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Infra</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">&lt;code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded"&gt;kubectl get svc -A&lt;/code&gt;</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">LB en pending</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Configurer LB/NodePort</td></tr><tr><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">OK par IP seulement</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Règle Ingress</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">&lt;code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded"&gt;curl --resolve&lt;/code&gt;</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Hôte non concordant</td><td style="border:1px solid rgba(15,23,42,.14);padding:.75rem;vertical-align:top;">Corriger règles d’hôte</td></tr></tbody></table></div>

## Ingress versus Gateway API

Ingress reste largement utilisé. Gateway API offre un routage plus riche et une meilleure séparation des rôles, mais nécessite toujours un contrôleur compatible et la même discipline de dépannage à travers DNS, Services, EndpointSlices, Pods et TLS. Migrer n’élimine pas ces fondamentaux.

## Liste de contrôle finale de dépannage

- Contrôleur installé et Pods en Ready

- Service du contrôleur joignable (EXTERNAL-IP/NodePort)

- spec.ingressClassName correct

- DNS résout vers l’endpoint Ingress

- Règles d’hôte et de chemin correctes, bon pathType

- Nom et port du Service corrects

- Sélecteur du Service correspondant aux labels des Pods

- EndpointSlices contenant des adresses en Ready

- Application à l’écoute sur 0.0.0.0

- Secret TLS présent dans le même namespace ; certificat valide et correspondant au nom d’hôte

- NetworkPolicy/pare-feu autorisant contrôleur <-> Pods et externe <-> contrôleur

- Logs du contrôleur et de l’application examinés

## Conclusion

La plupart des erreurs Kubernetes Ingress ne sont pas des fautes de frappe YAML ; ce sont des maillons rompus sur le chemin de la requête. Commencez par le DNS et l’endpoint externe, poursuivez via le contrôleur et les règles Ingress, et terminez au niveau du Service, des EndpointSlices et des Pods. Distinguez les 404 du contrôleur des 404 applicatifs, et validez TLS sans deviner. Des correctifs fiables viennent de l’isolation de la couche en échec, de la preuve de chaque saut avec des commandes, puis seulement des changements de configuration.