Cloud
Azure Container Apps : diagnostiquer une révision bloquée avant de redéployer
Un runbook de production pour séparer pull d'image, démarrage, probes, configuration, secrets, ressources et état de plateforme lorsqu'une révision Container Apps ne devient pas saine.
Le pipeline a publié une nouvelle image Azure Container Apps, mais la révision reste en Processing, passe en Degraded ou termine en Failed. L’ancienne révision sert encore la production. Sous pression, deux réflexes reviennent : relancer exactement le même déploiement ou augmenter CPU, mémoire et délais de probe jusqu’à ce que la plateforme accepte le conteneur.
Ces actions peuvent masquer la cause et multiplier les révisions inutilisables. Une révision bloquée peut signaler un pull d’image impossible, un processus qui quitte, une probe incohérente avec le port réel, une référence de secret absente, un volume non monté ou une dépendance inaccessible au démarrage. Ce runbook conserve l’ancienne révision comme témoin et décide s’il faut corriger un seul paramètre, recréer la candidate depuis la stable, revenir au manifeste précédent ou escalader un incident de plateforme.
Figer la candidate et protéger la révision stable
Commencez par nommer la révision, l’image et le changement exacts. Ne confondez pas l’état du déploiement ARM, l’état de provisioning de la révision, son état d’exécution et le trafic réellement servi.
incident: inc-20260916-004
resource_group: rg-orders-prod
container_app: ca-orders-api-prod
environment: cae-platform-prod
revision_mode: multiple
stable_revision: ca-orders-api-prod--r118
candidate_revision: ca-orders-api-prod--r119
stable_image: acrprod.azurecr.io/orders-api@sha256:<stable-digest>
candidate_image: acrprod.azurecr.io/orders-api@sha256:<candidate-digest>
candidate_created_at_utc: <timestamp>
observed_state:
arm_deployment: <succeeded|failed|unknown>
provisioning_state: <Provisioning|Provisioned|Failed>
running_state: <Processing|Running|Degraded|Failed|Unknown>
health_state: <Healthy|Unhealthy|None>
replicas: <count>
traffic_weight: <percent>
change_scope:
- image digest
- environment variables
- secrets or secret references
- probes and target port
- cpu and memory
- scale rules
- volumes
stop_conditions:
- stable revision is not healthy
- candidate already receives unexpected production traffic
- image digest or deployment manifest cannot be identified
- evidence collection would expose secret values Une opération ARM Succeeded prouve que la demande de configuration a été acceptée, pas que le conteneur est prêt. À l’inverse, une candidate dégradée ne justifie pas de toucher à la stable. En mode multi-révisions, maintenez la candidate à 0 % tant que son chemin de démarrage n’est pas expliqué.
Lire les quatre états avant les logs
Capturez l’application, la liste des révisions et le détail de la candidate. La propriété provisioningError est particulièrement utile : elle peut contenir le premier message de plateforme avant que les logs applicatifs existent.
RG="rg-orders-prod"
APP="ca-orders-api-prod"
CANDIDATE="ca-orders-api-prod--r119"
az containerapp show --resource-group "$RG" --name "$APP" --query '{revisionMode:properties.configuration.activeRevisionsMode,latestRevision:properties.latestRevisionName,traffic:properties.configuration.ingress.traffic,provisioningState:properties.provisioningState}' --output json
az containerapp revision list --resource-group "$RG" --name "$APP" --query '[].{name:name,created:properties.createdTime,active:properties.active,provisioning:properties.provisioningState,running:properties.runningState,health:properties.healthState,replicas:properties.replicas,traffic:properties.trafficWeight}' --output table
az containerapp revision show --resource-group "$RG" --name "$APP" --revision "$CANDIDATE" --query '{name:name,provisioning:properties.provisioningState,provisioningError:properties.provisioningError,running:properties.runningState,health:properties.healthState,replicas:properties.replicas,template:properties.template}' --output json Interprétez la combinaison, pas un champ isolé. Provisioning avec Processing indique que la plateforme attend encore une candidate exploitable. Provisioned avec Degraded signifie que la ressource existe mais qu’aucune replica prête ne satisfait le contrat. Failed avec une erreur de pull, de montage ou de démarrage oriente directement le diagnostic. Si les propriétés restent incohérentes ou inconnues pour plusieurs applications du même environnement, conservez cet indice pour une escalade de plateforme.
Corréler les événements système et la sortie du conteneur
Les logs système décrivent ce que Container Apps tente de faire. Les logs console exposent stdout et stderr lorsque le processus démarre assez longtemps pour écrire. Interrogez une fenêtre courte autour de la création de la candidate.
let Revision = "ca-orders-api-prod--r119";
let Start = datetime(<candidate-created-at-utc>);
union isfuzzy=true
(
ContainerAppSystemLogs_CL
| where TimeGenerated >= Start
| where RevisionName_s == Revision
| project TimeGenerated, Source="system", Reason=Reason_s, Detail=Log_s
),
(
ContainerAppConsoleLogs_CL
| where TimeGenerated >= Start
| where RevisionName_s == Revision
| project TimeGenerated, Source="console", Reason="application", Detail=Log_s
)
| order by TimeGenerated asc Classez le premier signal utile, pas la dernière erreur répétée :
ErrImagePullou authentification registre : vérifier référence, digest, identité de pull, rôleAcrPull, DNS et sortie vers le registre.ContainerCrashingou code de sortie non nul : lire le premier démarrage, la commande, les arguments et les variables requises.Deployment Progress Deadline Exceededou 0 replica prête : comparer startup et readiness probes au temps de démarrage et au port réellement écouté.- erreur de volume ou de secret : vérifier nom, référence et montage sans afficher la valeur.
OOMKilledou redémarrages sous charge d’initialisation : comparer limites, consommation attendue et comportement de la stable avant d’ajouter des ressources.
L’absence de logs console est également une preuve : le pull peut échouer, le runtime peut refuser la configuration ou le processus peut sortir avant l’initialisation du logger. Ne concluez pas que l’application est saine parce qu’elle n’a rien écrit.
Comparer la candidate à la stable, champ par champ
La comparaison doit porter sur le template effectif, pas seulement sur le commit applicatif. Exportez les deux révisions, réduisez-les aux champs versionnés, puis examinez le diff.
RG="rg-orders-prod"
APP="ca-orders-api-prod"
STABLE="ca-orders-api-prod--r118"
CANDIDATE="ca-orders-api-prod--r119"
az containerapp revision show -g "$RG" -n "$APP" --revision "$STABLE" --query 'properties.template' -o json > stable-template.json
az containerapp revision show -g "$RG" -n "$APP" --revision "$CANDIDATE" --query 'properties.template' -o json > candidate-template.json
jq -S . stable-template.json > stable-template.sorted.json
jq -S . candidate-template.json > candidate-template.sorted.json
diff -u stable-template.sorted.json candidate-template.sorted.json || true Cherchez d’abord les différences qui peuvent empêcher la première replica de devenir prête : image ou digest, command, args, variables, secretRef, ressources, ports, probes, volumes et règles de scale. Un changement de configuration global à l’application peut aussi affecter le démarrage alors qu’il n’apparaît pas dans le template ; capturez séparément ingress, registres, secrets nommés et Dapr si utilisé.
Prouver le pull d’image sans relancer le déploiement
Un tag mutable ne prouve pas l’image reçue. Résolvez la candidate vers un digest et vérifiez que le registre contient ce manifeste pour la bonne architecture. Contrôlez ensuite l’identité utilisée par Container Apps et son scope AcrPull.
RG="rg-orders-prod"
APP="ca-orders-api-prod"
ACR="acrprod"
IMAGE="orders-api@sha256:<candidate-digest>"
az acr manifest show-metadata --registry "$ACR" --name "$IMAGE" --query '{digest:digest,createdAt:createdAt,imageSize:imageSize}' --output yaml
az containerapp show -g "$RG" -n "$APP" --query '{identity:identity,registries:properties.configuration.registries[].{server:server,identity:identity}}' --output json Si le registre est privé, testez aussi la résolution DNS et le chemin sortant depuis le même environnement réseau. Un pull réussi depuis un laptop ou un runner public ne prouve pas que l’environnement Container Apps peut joindre ACR. Ne remplacez pas immédiatement l’identité par des credentials statiques : cela change le modèle de sécurité et brouille le diagnostic.
Vérifier port, démarrage et probes comme un contrat unique
Une application peut démarrer correctement et rester non prête parce que le port ou la probe ne décrit pas son vrai comportement. Comparez quatre éléments : port écouté par le processus, targetPort de l’ingress, port de chaque probe et durée réelle d’initialisation.
Candidate startup contract
Process listens on: 8080
Ingress targetPort: 8080
Startup probe: TCP 8080, budget compatible with cold start
Readiness probe: HTTP /ready on 8080
Liveness probe: HTTP /live on 8080
Ready means
Process accepts requests
Required configuration is loaded
Critical local initialization is complete
Ready does not require
Every optional downstream system is available
A migration with unbounded duration has completed
A write operation has succeeded N’augmentez pas tous les délais à l’aveugle. Mesurez le temps jusqu’au premier message « listening », puis jusqu’au premier succès readiness. Si la candidate ne lie jamais le port, une probe plus tolérante ne corrigera ni une commande erronée ni un crash. Si le démarrage est légitimement plus long, corrigez la startup probe avec un budget explicite et gardez une readiness probe plus stricte.
Vérifier configuration, secrets et volumes sans fuite
Listez les noms de secrets et les références du template, jamais leurs valeurs dans le ticket ou les logs. Une référence présente mais mal orthographiée, un secret supprimé ou un chemin de montage différent suffit à bloquer la révision.
RG="rg-orders-prod"
APP="ca-orders-api-prod"
CANDIDATE="ca-orders-api-prod--r119"
az containerapp secret list -g "$RG" -n "$APP" --query '[].name' --output tsv | sort
az containerapp revision show -g "$RG" -n "$APP" --revision "$CANDIDATE" --query 'properties.template.{containers:containers[].{name:name,env:env[].{name:name,secretRef:secretRef},volumeMounts:volumeMounts},volumes:volumes}' --output json Validez également les permissions de l’identité si l’application charge Key Vault, Storage ou une autre dépendance pendant son bootstrap. Toutefois, évitez de rendre la readiness dépendante d’un système facultatif : une panne externe pourrait alors empêcher toute replica de devenir prête et transformer une dégradation partielle en indisponibilité complète.
Recréer une candidate minimale plutôt que rejouer à l’identique
Une relance identique n’apporte une preuve que si l’hypothèse est un incident transitoire clairement borné. Sinon, partez de la révision stable et appliquez une seule différence corrigée. revision copy conserve un socle connu et rend le delta explicite.
RG="rg-orders-prod"
APP="ca-orders-api-prod"
STABLE="ca-orders-api-prod--r118"
FIXED_IMAGE="acrprod.azurecr.io/orders-api@sha256:<fixed-digest>"
az containerapp revision copy --resource-group "$RG" --name "$APP" --revision "$STABLE" --image "$FIXED_IMAGE" --output json Gardez la nouvelle candidate hors trafic public. Vérifiez son état, ses system logs, le démarrage, les probes et un smoke test ciblé via un label ou un chemin de validation prévu par l’architecture. Ne désactivez la candidate défaillante qu’après avoir conservé son provisioningError, son template et sa fenêtre de logs.
Décider correction, rollback ou escalade
CORRIGER UNE CANDIDATE
La cause est prouvee et bornee: digest, commande, port, probe, secretRef ou ressource.
Copier la stable, appliquer une seule correction, valider hors trafic puis promouvoir.
ROLLBACKER LE MANIFESTE
Plusieurs changements sont melanges ou le delta ne peut pas etre explique.
Redeployer le manifeste et les digests precedemment valides; garder la stable en service.
REJOUER UNE FOIS
Un incident transitoire externe est date et confirme, sans derive de configuration.
Rejouer avec le meme digest et comparer les deux traces; ne pas boucler.
ESCALADER LA PLATEFORME
Plusieurs apps de l'environnement echouent, les etats restent incoherents ou aucune erreur exploitable n'existe.
Conserver region, environment ID, revision, timestamps UTC, correlation IDs et erreurs de provisioning.
ARRETER
La stable est degradee, la candidate recoit du trafic inattendu ou le rollback n'est pas executable.
Proteger d'abord le service avant toute nouvelle revision. La validation finale reprend le même contrat que le diagnostic : provisioning Provisioned, running state Running, health Healthy, replica prête, logs système sans boucle, smoke test avec corrélation, puis trafic progressif observé. Si les erreurs réapparaissent à la promotion, remettez la stable à 100 % sans supprimer immédiatement la candidate.
Conclusion
Une révision Container Apps bloquée n’est pas une invitation à redéployer jusqu’au vert. C’est un échec localisable entre configuration acceptée, image récupérée, processus démarré, probe satisfaite et trafic autorisé.
Conservez la stable comme témoin, capturez les états et le premier événement système, comparez les templates effectifs, puis recréez une candidate avec un seul changement prouvé. La sortie du runbook doit être nette : promouvoir après validation, revenir au manifeste connu, rejouer une fois sur preuve transitoire ou escalader avec un dossier exploitable.