Cloud

Azure Container Apps : diagnostiquer un ingress privé avant de changer les révisions

Construire un runbook opérationnel pour les pannes d'ingress privé Azure Container Apps en séparant DNS, mode d'ingress, routage des révisions, logs applicatifs et preuves de rollback.

09 juin 2026 azurecontainer-appsprivate-endpointdnskqllogsmonitoringrunbookrollbackautomation

Azure Container Apps est souvent utilisé comme point d’accueil pratique pour des API internes, des backends légers ou des services événementiels. La difficulté opérationnelle commence quand un private endpoint, un environnement interne, Application Gateway, le forwarding DNS et le trafic entre révisions se retrouvent sur le même chemin de requête. Un 404, un 502, une réponse vide ou une panne intermittente peut ressembler à un bug applicatif alors que le vrai sujet est un hostname, un mode d’ingress, un routage de révision ou un enregistrement DNS privé.

Le cas d’usage est une API privée hébergée sur Azure Container Apps. Les clients internes appellent un hostname lisible, le trafic peut entrer par Application Gateway, et la container app expose un ingress interne dans un environnement Container Apps. Une nouvelle révision vient d’être déployée, ou un changement private endpoint/DNS vient d’être appliqué. Avant de faire un rollback à l’aveugle ou de modifier les poids de trafic, le runbook doit prouver quelle couche échoue.

Décrire le chemin privé comme un objet d’exploitation

Un chemin privé Container Apps comporte plus de pièces mobiles que le code applicatif ne le laisse penser. L’environnement peut être interne. L’application peut exposer un ingress interne ou externe. Un private endpoint peut être utilisé pour l’environnement. Un domaine personnalisé peut pointer vers Application Gateway ou vers le FQDN de l’environnement Container Apps. Dans l’environnement, le trafic passe par le proxy de la plateforme avant d’atteindre une ou plusieurs révisions.

text container-apps-private-path.txt
Client interne
Résout api.internal.example.com
Appelle le hostname privé attendu

Application Gateway éventuelle
Termine TLS ou transmet avec le host header configuré
Applique WAF et health probes
Envoie le trafic vers le chemin Container Apps

Environnement Container Apps
Expose un private endpoint ou un chemin internal load balancer
Résout le domaine d'environnement via DNS privé
Route le trafic via le proxy d'ingress plateforme

Container app
Accepte l'ingress interne sur le target port configuré
Répartit le trafic entre révisions actives
Émet des logs system et console

Dépendances backend
Exigent DNS privé, identité, secrets ou accès managé

Cette carte évite la première erreur classique : traiter chaque requête en échec comme une révision cassée. Si le hostname résout encore en public, si les health probes Application Gateway échouent ou si le trafic n’atteint jamais l’environnement Container Apps, changer une révision ne fera qu’ajouter du bruit.

Séparer DNS, ingress et symptômes de révision

Le runbook doit qualifier le symptôme avant toute modification. Un enregistrement DNS manquant n’appelle pas un rollback. Une révision qui crash, oui. Un traffic split qui pointe vers une révision dégradée demande un déplacement contrôlé, pas une exception WAF.

text symptom-reading.txt
Symptôme
Le hostname résout vers une adresse publique
  Vérifier zone DNS privée, lien VNet, règle de forwarding et cible du domaine personnalisé

Application Gateway retourne 502
  Vérifier backend health, host header, TLS/SNI et probe path vers Container Apps

La requête atteint Container Apps mais retourne 404
  Vérifier ingress type, target port, custom domain binding, path routing et label de révision

La requête est intermittente
  Vérifier révisions actives, poids de trafic, redémarrages de replicas et événements de scale

La requête atteint l'app mais une dépendance échoue
  Vérifier identité managée, DNS privé, Key Vault ou firewall du service aval

La question utile n’est pas seulement : “l’application est-elle up ?”. Elle devient : “ce hostname exact atteint-il l’environnement attendu, la configuration d’ingress attendue et la révision attendue ?”.

Prouver le DNS privé avant de diagnostiquer le conteneur

Pars du même réseau que les vrais clients. Si le runner de diagnostic est hors du chemin privé, il peut produire un résultat propre alors que la production échoue, ou l’inverse. Le premier contrôle doit capturer hostname, chaîne CNAME et adresse finale.

bash 01-container-apps-private-dns-check.sh
HOSTNAME=api.internal.example.com

nslookup "$HOSTNAME"
dig +short "$HOSTNAME"

a=$(dig +short "$HOSTNAME" | tail -n 1)
case "$a" in
10.*|172.16.*|172.17.*|172.18.*|172.19.*|172.2*|172.30.*|172.31.*|192.168.*)
  echo "private_resolution_ok=$a"
  ;;
*)
  echo "unexpected_public_or_empty_resolution=$a"
  exit 2
  ;;
esac

openssl s_client -connect "$HOSTNAME:443" -servername "$HOSTNAME" </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer

Si ce test échoue, la correction doit rester à la couche DNS ou private endpoint : enregistrements de zone privée, liens VNet, forwarding resolver ou cible du domaine personnalisé. Une nouvelle image de conteneur ne réparera pas un chemin de résolution.

Lire ensemble les logs system et console Container Apps

Container Apps expose deux vues Log Analytics utiles : les logs system pour les événements de plateforme, et les logs console pour stdout/stderr applicatifs. Pendant un incident d’ingress, regarder seulement les logs applicatifs est trop étroit. La plateforme indique peut-être déjà qu’une révision provisionne, se désactive, échoue sur ses probes ou ne reçoit aucun trafic.

kusto 02-container-apps-private-ingress-triage.kql
let Window = 2h;
let App = "orders-api";
let Env = "aca-prod-weu";
let System =
ContainerAppSystemLogs_CL
| where TimeGenerated > ago(Window)
| where ContainerAppName_s == App or EnvironmentName_s == Env
| project TimeGenerated,
        Source="system",
        Environment=EnvironmentName_s,
        App=ContainerAppName_s,
        Revision=RevisionName_s,
        Replica="",
        Message=Log_s;
let Console =
ContainerAppConsoleLogs_CL
| where TimeGenerated > ago(Window)
| where ContainerAppName_s == App
| project TimeGenerated,
        Source="console",
        Environment=EnvironmentName_s,
        App=ContainerAppName_s,
        Revision=RevisionName_s,
        Replica=tostring(ContainerGroupName_g),
        Message=Log_s;
System
| union Console
| where Message has_any ("ingress", "probe", "revision", "error", "failed", "timeout", "502", "404")
| order by TimeGenerated desc

Cette requête donne au responsable d’incident une vue compacte : événements plateforme, noms de révisions, replicas et messages applicatifs dans une même timeline. Elle est volontairement large au départ. Une fois la révision ou la famille d’événements identifiée, on resserre la requête.

Comparer le trafic configuré aux logs observés

Les révisions Container Apps rendent le rollback tentant, mais les poids de trafic doivent être lus avec preuves. Si plusieurs révisions sont actives, un petit pourcentage peut suffire à créer un incident intermittent visible. Si des labels sont utilisés, un appelant peut cibler une révision labellisée même quand le trafic par défaut semble sain.

bash 03-container-apps-revision-check.sh
APP=orders-api
RG=rg-prod-apps

az containerapp ingress show --name "$APP" --resource-group "$RG" --query '{external:external,targetPort:targetPort,transport:transport,traffic:traffic}' --output table

az containerapp revision list --name "$APP" --resource-group "$RG" --query '[].{name:name,active:properties.active,traffic:properties.trafficWeight,created:properties.createdTime}' --output table

La comparaison est simple : si les logs montrent des échecs seulement sur une révision et que le trafic pointe dessus, déplace le trafic ou rollback cette révision. Si aucune requête n’atteint une révision, reste sur ingress, DNS ou gateway. Si toutes les révisions journalisent le même 403 aval, la dépendance est un suspect plus crédible.

Garder un rollback petit et observable

Un rollback ne doit pas devenir un contournement silencieux. Avant de déplacer le trafic, capture la révision fautive, les poids actuels, la référence du dernier déploiement et les preuves de diagnostic qui rendent le rollback raisonnable. Après le rollback, garde le même hostname et un header de corrélation pour valider.

bash 04-bounded-rollback.sh
APP=orders-api
RG=rg-prod-apps
GOOD_REV=orders-api--000018
BAD_REV=orders-api--000019

az containerapp ingress traffic set --name "$APP" --resource-group "$RG" --revision-weight "$GOOD_REV=100" "$BAD_REV=0"

CORRELATION_ID="ops-$(date +%Y%m%d%H%M%S)"
curl -vk "https://api.internal.example.com/health" -H "x-correlation-id: $CORRELATION_ID"

echo "validate_correlation_id=$CORRELATION_ID"

Si le rollback corrige la requête mais que DNS, gateway et ingress n’ont jamais été capturés, l’équipe garde un résultat fragile. Le prochain déploiement peut réintroduire la panne parce que la condition réelle n’a pas été écrite.

Conclusion

L’ingress privé Azure Container Apps n’est exploitable proprement que si le chemin est observable. DNS, private endpoint, mode d’ingress, proxy plateforme, routage des révisions et logs applicatifs doivent être lus avant de choisir une correction.

La règle pratique est directe : ne pas changer le trafic des révisions tant que le trafic n’a pas atteint Container Apps, ne pas modifier DNS tant que le chemin resolver n’est pas prouvé, et ne pas ajouter d’exception gateway sans symptôme gateway visible. Avec cette discipline, un incident Container Apps privé devient un diagnostic borné plutôt qu’un rollback à l’aveugle.