WIKI
SUPPORT-IA Support N2/N3 · plateformes IA Azure

06 · Web App for Containers privée : démarrage, réseau, secrets et journaux

22 min de lecture Mis à jour le 23 octobre 2026
Sur cette page

Version : 1.0 · Date : 2026-10-23 · Parcours SUPPORT-IA (support N2/N3 de chatbots sur Azure)

En une phrase : une Web App for Containers privée reçoit le trafic par un Private Endpoint, sort vers ACR, Azure OpenAI, AI Search et Key Vault par la VNet integration, tire son image avec sa managed identity, démarre seulement si le conteneur répond sur le bon port à temps ; presque toutes les pannes sont un port, un DNS, une route ou un rôle.

Web App for Containers 100 % privée : entrée par Private Endpoint, sortie par VNet integration

Le choix entre AKS, Container Apps et App Service est traité dans AKS/07-conteneurs-azure-et-entretiens. Ce chapitre suit une Web App for Containers Linux de chatbot (Python ou Node), du build de l'image à la lecture des journaux. La chaîne de build et ACR sont dans 05-gitops-argocd-azure-devops-acr ; le pendant AKS dans 04-aks-pour-le-support.

Ce que tu dois savoir expliquer en entretien#

Question type Section
« Que fait App Service quand il démarre ton conteneur ? Que veut dire didn't respond to HTTP pings ? » 2, 3
« Comment la Web App tire-t-elle une image d'un ACR privé ? » 4
« Private Endpoint ou VNet integration ? Pourquoi un 403 ? » 5
« Comment passez-vous les secrets ? Que se passe-t-il à la rotation ? » 6
« Slots, swap, Health check ? » 7, 8
« Où lis-tu les journaux du conteneur ? » 9

1. Les pièces du puzzle#

Élément Rôle À savoir pour le support
App Service plan Les VM (instances) partagées par les apps du plan Une app qui sature la mémoire gêne ses voisines ; deux VNet integrations au plus par plan
Web App for Containers L'app qui exécute une image (plus d'éventuels sidecars) Un seul port HTTP exposé par le conteneur
Site SCM (Kudu) Outils : journaux, API de déploiement, terminal Sur Linux, il tourne dans un conteneur séparé : il ne voit ni les fichiers ni les processus du conteneur applicatif
Managed identity (system ou user-assigned) Pull de l'image, Key Vault references, appels SDK Un rôle par usage : AcrPull, Key Vault Secrets User, Cognitive Services OpenAI User…
Deployment slots Copies vivantes de l'app (staging) Standard, Premium ou Isolated ; chaque slot a ses Private Endpoints
/home Stockage persistant partagé entre instances Désactivé par défaut pour un conteneur Linux personnalisé (WEBSITES_ENABLE_APP_SERVICE_STORAGE)

Docker Compose (multi-container) sera retiré le 31/03/2027 : les sidecars le remplacent. Une app encore en Compose est un sujet de migration à signaler.


2. Le Dockerfile d'une application de chat#

Règles qui évitent la majorité des pannes de démarrage :

Règle Pourquoi
Écouter sur 0.0.0.0, pas 127.0.0.1 App Service envoie ses pings de l'extérieur du conteneur
Port 80 ou 8080, sinon WEBSITES_PORT Ces deux ports sont détectés automatiquement ; tout autre port exige l'app setting (qui n'est pas visible dans le conteneur)
Répondre à n'importe quel chemin, même par une 404 Le warm-up appelle /robots933456.txt ; une application qui ne répond rien aux chemins inconnus est redémarrée en boucle
Démarrer vite, ou régler WEBSITES_CONTAINER_START_TIME_LIMIT 230 s par défaut avant échec du démarrage
Image petite (multi-stage), pas d'écriture massive hors de /home Espace disque hôte limité (15 Go par instance), image retirée à chaque changement d'instance
Ne pas gérer TLS dans le conteneur App Service termine TLS aux front-ends ; lire X-Forwarded-Proto
Journaux sur stdout / stderr C'est ce que montrent le Log stream et AppServiceConsoleLogs
Requêtes LLM longues : streaming Une requête HTTP sans réponse est coupée vers 230 s (Windows) ou 240 s (Linux), limite non réglable
# Python (FastAPI) : image de build puis image d'exécution minimale
FROM python:3.12-slim AS build
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt

FROM python:3.12-slim
WORKDIR /app
COPY --from=build /install /usr/local
COPY src/ ./src/
EXPOSE 8000
# 0.0.0.0 : joignable par les pings d'App Service ; WEBSITES_PORT=8000 côté app settings
CMD ["gunicorn", "src.main:app", "-k", "uvicorn.workers.UvicornWorker", "-b", "0.0.0.0:8000", "--timeout", "600"]
# Node.js : port 8080 détecté sans réglage
FROM node:22-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
ENV PORT=8080
EXPOSE 8080
CMD ["node", "server.js"]        # server.js doit écouter sur 0.0.0.0:${PORT}

Piège : WEBSITES_PORT n'est pas PORT. PORT sert aux images intégrées (runtimes Python, Node d'App Service) ; pour un conteneur personnalisé, c'est WEBSITES_PORT qui dit à la plateforme où envoyer le trafic.


3. Le démarrage d'un conteneur#

Démarrage d'un conteneur sur App Service : quatre étapes, quatre familles de pannes

Réglage (app setting ou propriété) Effet Défaut
WEBSITES_PORT Port vers lequel la plateforme envoie le trafic 80 (80 et 8080 détectés)
WEBSITES_CONTAINER_START_TIME_LIMIT Délai pour que le conteneur réponde aux pings de démarrage 230 s (Linux, de 10 à 1 800 s) ; une FAQ Microsoft parle de 240 s
WEBSITE_WARMUP_PATH / WEBSITE_WARMUP_STATUSES Chemin et codes HTTP qui valident le warm-up Tout code HTTP vaut « prêt »
Always On (General settings) Évite le déchargement de l'app après 20 min sans requête (ping toutes les 5 min) Désactivé ; disponible à partir de Basic
WEBSITES_ENABLE_APP_SERVICE_STORAGE Monte /home persistant et partagé false pour un conteneur Linux personnalisé

Ce qu'il faut savoir sur un redémarrage :

  • Changer l'image : l'ancien conteneur continue de servir tant que le nouveau n'est pas prêt ; le basculement peut prendre plusieurs minutes.
  • Redémarrage simple : seules les couches modifiées sont retirées. Nouvelle instance (scale-out, changement de tier, maintenance) : toutes les couches sont retirées, d'où des démarrages plus longs et parfois un dépassement du délai.
  • Si le conteneur n'est pas prêt dans le délai, la plateforme fait échouer la tentative et réessaie : l'utilisateur voit des 503.

4. Tirer l'image d'un ACR privé avec une managed identity#

# Lecture : comment l'app tire-t-elle son image ?
az webapp config container show -g rg-chat-prd -n app-chat-prd -o table
az webapp config show -g rg-chat-prd -n app-chat-prd \
  --query "{acrMi:acrUseManagedIdentityCreds, acrUami:acrUserManagedIdentityID, image:linuxFxVersion}" -o json
az webapp identity show -g rg-chat-prd -n app-chat-prd
az acr config authentication-as-arm show -r acrchatprd          # doit être enabled pour le pull par managed identity

Mise en place (pour comprendre ce qu'on lit) :

PRINCIPAL=$(az webapp identity assign -g rg-chat-prd -n app-chat-prd --query principalId -o tsv)
az role assignment create --assignee "$PRINCIPAL" --role AcrPull --scope "$(az acr show -n acrchatprd --query id -o tsv)"
az webapp config set -g rg-chat-prd -n app-chat-prd --generic-configurations '{"acrUseManagedIdentityCreds": true}'
# Avec une user-assigned identity : préciser son client ID
az webapp config set -g rg-chat-prd -n app-chat-prd --generic-configurations '{"acrUserManagedIdentityID": "<client-id>"}'
# Registre derrière un Private Endpoint : faire passer le pull par la VNet integration
az resource update -g rg-chat-prd -n app-chat-prd --resource-type "Microsoft.Web/sites" \
  --set properties.outboundVnetRouting.imagePullTraffic=true
Symptôme (Log stream, AppServicePlatformLogs) Cause Correction
UNAUTHORIZED / token validation failed Le registre n'accepte pas les jetons d'audience ARM az acr config authentication-as-arm update --status enabled
UNAUTHORIZED Pas d'AcrPull, mauvaise identité (user-assigned sans acrUserManagedIdentityID), acrUseManagedIdentityCreds à false Rôle et propriétés ci-dessus
Timeout ou refus sur *.azurecr.io Pull par Internet alors que le registre est privé outboundVnetRouting.imagePullTraffic=true (ancien nom vnetImagePullEnabled ou WEBSITE_PULL_IMAGE_OVER_VNET)
Échec sur le data endpoint seulement DNS du data endpoint (<acr>.<région>.data.azurecr.io) absent Enregistrements dans privatelink.azurecr.io
manifest unknown / tag introuvable Tag non poussé (pipeline en échec) az acr repository show-tags
Identifiants admin dans DOCKER_REGISTRY_SERVER_* Ancienne méthode (admin user ou service principal) Passer à la managed identity ; ces variables ne sont jamais exposées à l'application

5. Réseau : entrée par Private Endpoint, sortie par VNet integration#

5.1 Deux mécanismes indépendants#

Private Endpoint (entrant) VNet integration (sortant)
Sens Clients → app App → VNet et au-delà
Subnet N'importe quel subnet (pas forcément dédié) Subnet dédié et délégué à Microsoft.Web/serverFarms ; jamais le même que celui du Private Endpoint
Plans Basic, Standard, PremiumV2/V3/V4, IsolatedV2, Functions Premium Selon le plan
Par défaut L'accès public coexiste : il faut désactiver le public network access Seul le trafic privé (RFC 1918) entre dans le VNet
DNS privatelink.azurewebsites.net : un enregistrement pour l'app et un pour scm Les zones privatelink.* des services appelés doivent être liées au VNet d'intégration
Slots Un Private Endpoint par slot (sites-<slot>) Configuré par app

5.2 Routage sortant : application et configuration#

La VNet integration distingue :

  • Application routing : le trafic émis par le code. outboundVnetRouting.applicationTraffic=true (case Outbound internet traffic du portail ; anciens noms vnetRouteAllEnabled, WEBSITE_VNET_ROUTE_ALL) envoie tout le trafic applicatif dans le VNet, sinon seulement le trafic RFC 1918.
  • Configuration routing : ce que la plateforme fait au démarrage, par la route publique par défaut : pull de l'image (imagePullTraffic), partage de contenu, sauvegarde, et Key Vault references.
  • outboundVnetRouting.allTraffic=true route les deux familles dans le VNet (CLI uniquement, auditable par Azure Policy).

Règle à retenir : une app Linux qui appelle des Private Endpoints doit router tout son trafic dans le VNet ; sinon, selon la résolution DNS, une partie part par la route publique et revient en 403.

# Lecture : intégration, accès public, routage
az webapp vnet-integration list -g rg-chat-prd -n app-chat-prd -o table
az webapp show -g rg-chat-prd -n app-chat-prd \
  --query "{subnet:virtualNetworkSubnetId, public:publicNetworkAccess, routing:outboundVnetRouting}" -o json
az webapp config show -g rg-chat-prd -n app-chat-prd --query "{routeAllLegacy:vnetRouteAllEnabled, alwaysOn:alwaysOn, health:healthCheckPath}"

5.3 Les zones DNS privées d'un chatbot#

Service Zone privée Remarque
Web App (entrant) privatelink.azurewebsites.net Enregistrements app et app.scm
Azure OpenAI / Foundry privatelink.openai.azure.com, privatelink.cognitiveservices.azure.com, privatelink.services.ai.azure.com Une zone par suffixe réellement utilisé
AI Search privatelink.search.windows.net
Key Vault privatelink.vaultcore.azure.net
ACR privatelink.azurecr.io Registre et data endpoint régional

Le nom appelé ne change jamais (monapp.azurewebsites.net, moncoffre.vault.azure.net) : c'est la chaîne CNAME vers privatelink.* qui doit finir sur une IP privée depuis le VNet. Le certificat par défaut couvre *.azurewebsites.net : il faut appeler la Web App par ce nom, pas par l'IP du Private Endpoint.

5.4 Diagnostic réseau#

# Depuis le conteneur de l'app (terminal SSH du portail ou az webapp exec en préversion, si l'image a un shell)
nslookup oai-chat-prd.openai.azure.com          # IP privée attendue (10.x, 172.16-31.x, 192.168.x)
nslookup kv-chat-prd.vault.azure.net
curl -sv https://srch-chat-prd.search.windows.net -o /dev/null   # 403 de la ressource ≠ timeout réseau
# Depuis une VM du réseau client : la Web App elle-même
nslookup app-chat-prd.azurewebsites.net          # doit passer par app-chat-prd.privatelink.azurewebsites.net
Symptôme Cause Vérifier Corriger
L'utilisateur reçoit 403 en appelant l'app Son DNS résout vers l'IP publique alors que l'accès public est coupé nslookup depuis le poste ou le VNet client Zone privatelink.azurewebsites.net liée, ou redirection DNS depuis le DNS de l'entreprise
Kudu / déploiement injoignable en privé Enregistrement scm manquant nslookup app.scm.azurewebsites.net Ajouter l'enregistrement (automatique avec un DNS zone group)
L'app reçoit 403 d'OpenAI, Search ou Key Vault, avec un message « public network access is disabled » Résolution vers l'IP publique, ou trafic non routé dans le VNet nslookup depuis le conteneur, outboundVnetRouting Lier la zone au VNet d'intégration, router tout le trafic
403 « permission » avec résolution privée correcte Rôle data plane manquant sur la managed identity az role assignment list --assignee <principalId> --all Attribuer le rôle (propagation jusqu'à quelques minutes)
Timeouts vers un Private Endpoint malgré une IP privée NSG ou UDR sur le subnet d'intégration, firewall, peering absent Network Watcher (next hop), règles NSG Ouvrir le flux 443
L'app ne peut plus sortir vers Internet après « route all » Le VNet n'a pas de sortie (NAT Gateway, firewall) Table de routage du subnet Prévoir la sortie, ou n'envoyer que le trafic privé

6. App settings et Key Vault references#

Les app settings arrivent dans le conteneur comme variables d'environnement ; les modifier redémarre l'app. Une valeur peut être une Key Vault reference :

@Microsoft.KeyVault(SecretUri=https://kv-chat-prd.vault.azure.net/secrets/search-api-key)
@Microsoft.KeyVault(VaultName=kv-chat-prd;SecretName=search-api-key)
Point Comportement
Identité utilisée La system-assigned par défaut ; une user-assigned via keyVaultReferenceIdentity (utile quand le secret est lu dès la création)
Droits Key Vault Secrets User (modèle Azure RBAC) ou permission Get (access policies)
Coffre privé Accès par le VNet : configurer la VNet integration et router le trafic ; le coffre ne doit pas dépendre des IP publiques sortantes de l'app
Rotation Sans version dans la référence : nouvelle version prise sous 24 h ; tout changement de configuration force la relecture ; API …/config/configreferences/appsettings/refresh pour forcer
Échec de résolution L'app reçoit la chaîne littérale @Microsoft.KeyVault(...) à la place du secret
Diagnostic Portail : Environment variables → éditer le setting → statut de résolution ; Diagnose and solve problems → Key Vault Application Settings Diagnostics
Slots Un coffre par environnement : marquer ces settings deployment slot setting (sticky)
Journal du coffre Un SecretGet 403 depuis l'IP publique suivi d'un succès depuis l'IP privée est normal

Encore mieux que la référence : pour Azure OpenAI ou AI Search, appeler le service avec la managed identity de l'app (DefaultAzureCredential) plutôt qu'avec une clé : plus de secret, plus de rotation.


7. Deployment slots et swap#

Étape du swap (cible = production) Ce qui se passe
1 Les settings sticky de la cible sont appliqués au slot source, qui redémarre (fin de la phase 1 en swap with preview)
2 Attente du redémarrage de toutes les instances ; un échec annule le swap
3 Warm-up : requête sur / (ou WEBSITE_SWAP_WARMUP_PING_PATH, codes WEBSITE_SWAP_WARMUP_PING_STATUSES)
4 Échange des règles de routage : la production sert l'app préchauffée
5 L'ancien code repart dans le slot source avec ses settings
  • Slots disponibles à partir de Standard ; nombre limité par tier.
  • Retour arrière : refaire le même swap immédiatement.
  • Swap with preview : az webapp deployment slot swap … --action preview, puis --action swap ou --action reset.
  • Pour un conteneur : déployer le nouveau tag dans le slot staging (tâche AzureWebAppContainer@1 ou az webapp config container set --slot staging), tester, puis swapper ; tag unique, pas latest.
  • La configuration du Health check n'est pas propre au slot : elle suit le swap.
az webapp deployment slot list -g rg-chat-prd -n app-chat-prd -o table
az webapp config container show -g rg-chat-prd -n app-chat-prd --slot staging -o table

8. Health check#

Point Comportement
Fonctionnement Ping du chemin choisi sur chaque instance, toutes les minutes ; sain = code 200 à 299 en moins d'une minute
Retrait Après 10 échecs par défaut (WEBSITE_HEALTHCHECK_MAXPINGFAILURES, de 2 à 10), l'instance sort du load balancer
Limite de retrait Pas plus de 50 % des instances par défaut (WEBSITE_HEALTHCHECK_MAXUNHEALTHYWORKERPERCENT) ; si tout est malade, rien n'est retiré
Remplacement Instance malade depuis 1 h remplacée ; au plus une par heure et trois par jour par plan
Prérequis utile Au moins deux instances ; avec une seule, pas de reroutage (seulement le remplacement)
Pièges Redirections non suivies (301/302 = malade) ; chemin derrière l'authentification ; changer la config redémarre l'app

Pour un chatbot : un chemin /ready qui vérifie les dépendances critiques (avec un délai court), distinct d'un /healthz de liveness sans dépendance, comme sur AKS (chapitre 04).


9. Les journaux : Log stream, Diagnostic settings, Application Insights#

Où lire les journaux d'une Web App for Containers

Outil Contenu Activation Quand
Log stream stdout/stderr du conteneur et messages de la plateforme, en direct az webapp log config --docker-container-logging filesystem, puis az webapp log tail ou portail Monitoring > Log stream « Ça démarre ? Ça plante maintenant ? »
Fichiers Docker Journaux bruts de l'hôte et du conteneur Kudu : https://<app>.scm.azurewebsites.net/api/logs/docker (zip : /api/logs/docker/zip) ; en privé, passe par l'enregistrement scm Récupérer l'historique récent
Diagnostic settings → Log Analytics AppServiceConsoleLogs (stdout/stderr), AppServicePlatformLogs (opérations du conteneur), AppServiceHTTPLogs (requêtes) ; AppServiceAppLogs seulement pour Java SE et Tomcat sur Linux Diagnostic setting sur l'app Chercher hier, corréler, alerter
Application Insights Requêtes, dépendances (appels OpenAI, Search), exceptions, traces Instrumentation OpenTelemetry ou SDK dans le code Suivre une requête, trouver l'appel lent
Diagnose and solve problems Détecteurs : démarrage du conteneur, Key Vault references, disponibilité Rien Premier réflexe dans le portail
// Démarrages et arrêts du conteneur sur les deux dernières heures
AppServicePlatformLogs
| where TimeGenerated > ago(2h)
| order by TimeGenerated desc
| take 100

// Codes HTTP renvoyés par l'app (500, 502, 503 ?)
AppServiceHTTPLogs
| where TimeGenerated > ago(1h)
| summarize requetes = count(), duree_moyenne_ms = avg(TimeTaken) by ScStatus
| order by requetes desc

// Recherche du message de démarrage raté
AppServicePlatformLogs
| where TimeGenerated > ago(1d)
| search "didn't respond"

Piège : les requêtes du Health check ne figurent pas dans les journaux HTTP (elles sont internes). Et AppServiceAppLogs reste vide pour un conteneur Python ou Node : leurs journaux sont dans AppServiceConsoleLogs.


10. Pannes fréquentes : symptôme → cause → vérification → correction#

Symptôme Cause probable Vérifier Corriger
Container … didn't respond to HTTP pings on port: 80, failing site start L'app écoute sur un autre port Log stream ; port dans le Dockerfile WEBSITES_PORT = port réel
Même message, bon port Écoute sur 127.0.0.1 ; démarrage plus long que la limite ; ne répond pas aux chemins inconnus Log stream (l'app a-t-elle fini de démarrer ?) 0.0.0.0, WEBSITES_CONTAINER_START_TIME_LIMIT, WEBSITE_WARMUP_PATH
Le conteneur s'arrête en boucle CMD faux, variable manquante, dépendance absente, mémoire Log stream, AppServiceConsoleLogs Corriger l'image ou les settings
Échec du pull (UNAUTHORIZED) Identité, AcrPull, authentication-as-arm Section 4 Section 4
Échec du pull (timeout) Registre privé, pull hors VNet outboundVnetRouting imagePullTraffic=true, DNS
L'app voit @Microsoft.KeyVault(...) au lieu du secret Référence non résolue : rôle, réseau, nom du secret, syntaxe Statut dans le portail, détecteur Rôle Key Vault Secrets User, route all, nom exact
403 pour l'utilisateur DNS public alors que l'accès public est coupé nslookup côté client Zone privée liée, DNS d'entreprise
403 de l'app vers OpenAI / Search / Key Vault DNS ou routage public, ou rôle manquant Message (réseau ou permission), nslookup dans le conteneur Section 5.4
502 / 503 intermittents Conteneur qui redémarre, instance retirée par Health check, warm-up raté AppServicePlatformLogs, métrique Health check Selon la cause
504 ou coupure après ~4 min Réponse LLM non streamée au-delà de 230/240 s Durée des requêtes (TimeTaken) Streaming, traitement asynchrone
Lenteur de la première requête App déchargée (Always On désactivé), nouvelle instance Always On, événements de scale Activer Always On, préchauffage
Nouvelle version déployée, ancienne servie Même tag (latest), basculement pas fini, mauvais slot az webapp config container show Tag unique, attendre, vérifier le slot
no space left on device Image ou écritures trop grosses sur le disque hôte (15 Go) Taille de l'image Image plus petite, écrire dans /home
Kudu ne montre pas les fichiers de l'app Le site SCM est un autre conteneur (Linux) — Utiliser le terminal SSH du conteneur
flowchart TD
    A["Web App KO"] --> B{"Le conteneur démarre-t-il ?<br/>Log stream / AppServicePlatformLogs"}
    B -- "Échec du pull" --> B1["Identité, AcrPull, authentication-as-arm,<br/>imagePullTraffic, DNS privatelink.azurecr.io"]
    B -- "didn't respond to HTTP pings" --> B2["WEBSITES_PORT, 0.0.0.0,<br/>START_TIME_LIMIT, warm-up"]
    B -- "S'arrête en boucle" --> B3["stdout : exception, variable,<br/>@Microsoft.KeyVault non résolue"]
    B -- "Démarré" --> C{"L'utilisateur atteint-il l'app ?"}
    C -- "403 / timeout" --> C1["Private Endpoint, DNS privatelink.azurewebsites.net,<br/>public network access"]
    C -- "Oui, erreurs 5xx" --> D{"Les dépendances répondent-elles ?"}
    D -- "403 / timeout" --> D1["DNS des services, route all,<br/>rôles de la managed identity"]
    D -- "Lentes" --> D2["Application Insights : dépendances,<br/>quotas OpenAI, streaming"]

11. Lab : une Web App for Containers privée qui tire depuis ACR (60 minutes)#

Dans une subscription de test uniquement ; supprimer le resource group à la fin.

RG=rg-lab-webapp; LOC=francecentral; ACR=acrlab$RANDOM; APP=app-lab-$RANDOM
az group create -n $RG -l $LOC
az acr create -g $RG -n $ACR --sku Premium
az acr import -n $ACR --source mcr.microsoft.com/azuredocs/aci-helloworld:latest --image hello:1    # tag unique
az appservice plan create -g $RG -n plan-lab --is-linux --sku P0v3
az webapp create -g $RG -p plan-lab -n $APP --container-image-name $ACR.azurecr.io/hello:1
PRINCIPAL=$(az webapp identity assign -g $RG -n $APP --query principalId -o tsv)
az role assignment create --assignee "$PRINCIPAL" --role AcrPull --scope "$(az acr show -n $ACR --query id -o tsv)"
az webapp config set -g $RG -n $APP --generic-configurations '{"acrUseManagedIdentityCreds": true}'
az webapp log config -g $RG -n $APP --docker-container-logging filesystem
az webapp restart -g $RG -n $APP           # nouveau pull, cette fois avec la managed identity
az webapp log tail -g $RG -n $APP          # observer le pull et le démarrage

Variantes à tester, une à la fois, en lisant le Log stream :

  1. WEBSITES_PORT=8000 alors que l'image écoute sur 80 : message didn't respond to HTTP pings, puis retirer le setting.
  2. Retirer le rôle AcrPull et redémarrer : échec du pull UNAUTHORIZED.
  3. Ajouter un app setting @Microsoft.KeyVault(VaultName=inexistant;SecretName=x) : statut d'erreur dans le portail, chaîne littérale dans la variable.
  4. Aller plus loin : VNet, Private Endpoint ACR, az acr update --public-network-enabled false, puis observer l'échec du pull tant que imagePullTraffic et la zone DNS ne sont pas en place.

Nettoyage : az group delete -n $RG --yes --no-wait.


12. Questions d'entretien#

1. Que fait App Service quand il démarre ton conteneur ?

Il tire l'image (avec la managed identity si configurée), résout les app settings dont les Key Vault references, démarre le conteneur, puis envoie des pings HTTP sur le port déclaré jusqu'à obtenir une réponse, dans la limite de WEBSITES_CONTAINER_START_TIME_LIMIT (230 s par défaut). Ensuite seulement il route le trafic.

2. didn't respond to HTTP pings on port 80 : ta démarche ?

C'est un symptôme. Je vérifie le port réel de l'application et WEBSITES_PORT, qu'elle écoute sur 0.0.0.0, qu'elle répond même par une 404 aux chemins inconnus, et la durée de démarrage dans le Log stream ; si elle charge un index ou un modèle, j'augmente la limite de démarrage.

3. Private Endpoint et VNet integration, quelle différence ?

Le Private Endpoint ne sert qu'au trafic entrant : il donne une IP privée à l'app, et il faut couper l'accès public. La VNet integration ne sert qu'au trafic sortant : elle place l'app dans un subnet délégué pour joindre les Private Endpoints d'OpenAI, Search, Key Vault et ACR. Les deux s'utilisent ensemble pour une app 100 % privée.

4. Comment la Web App tire-t-elle une image d'un ACR privé ?

Managed identity avec AcrPull, acrUseManagedIdentityCreds (et acrUserManagedIdentityID si user-assigned), le registre doit accepter les jetons d'audience ARM, et le pull doit passer par le VNet (outboundVnetRouting.imagePullTraffic) avec la zone privatelink.azurecr.io pour le registre et son data endpoint.

5. L'app reçoit un 403 d'Azure OpenAI. Comment sais-tu si c'est le réseau ou les droits ?

Le message le dit souvent (« public network access is disabled » contre une erreur de permission). Je vérifie depuis le conteneur que le FQDN résout vers une IP privée et que le trafic est routé dans le VNet ; si oui, c'est le rôle Cognitive Services OpenAI User de la managed identity, avec quelques minutes de propagation.

6. Comment gérez-vous les secrets ?

Le mieux est de ne pas en avoir : managed identity vers OpenAI et Search. Sinon, des Key Vault references dans les app settings, lues avec la managed identity (Key Vault Secrets User), marquées sticky par slot. Sans version, la rotation est prise sous 24 h ; une référence non résolue arrive en chaîne littérale dans l'application.

7. Log stream, Diagnostic settings, Application Insights : lequel et quand ?

Le Log stream pour voir en direct un démarrage ou un plantage ; les Diagnostic settings vers Log Analytics pour chercher après coup (AppServiceConsoleLogs, AppServicePlatformLogs, AppServiceHTTPLogs) et alerter ; Application Insights pour suivre une requête et ses dépendances, par exemple un appel OpenAI lent.

8. Comment déployer sans coupure et revenir en arrière ?

Déployer le nouveau tag dans un slot staging, le tester (swap with preview si besoin), puis swapper : la production reçoit une app déjà démarrée et préchauffée. Retour arrière : refaire le swap. Attention aux settings sticky et au Health check qui suit le swap.

9. Les utilisateurs ont des coupures au bout de quatre minutes sur les longues réponses.

App Service coupe une requête sans réponse vers 230 s (Windows) ou 240 s (Linux), ce n'est pas réglable. Pour un chatbot, il faut streamer la réponse du LLM ou passer en traitement asynchrone.

10. Le Health check retire une instance : comment ça marche ?

Ping toutes les minutes ; après 10 échecs par défaut (réglable de 2 à 10), l'instance sort du load balancer, sans dépasser 50 % des instances ; si elle reste malade une heure, elle est remplacée. Il faut au moins deux instances pour en profiter.


13. Fiche à retenir#

  • Port : 80 ou 8080 détectés, sinon WEBSITES_PORT ; écouter sur 0.0.0.0 ; un seul port ; répondre à /robots933456.txt.
  • WEBSITES_CONTAINER_START_TIME_LIMIT : 230 s par défaut (10 à 1 800) ; échec = 503 et nouvel essai.
  • Always On : déchargement après 20 min sinon ; requêtes coupées vers 230/240 s → streaming.
  • Pull ACR : managed identity + AcrPull + acrUseManagedIdentityCreds (+ acrUserManagedIdentityID) + authentication-as-arm + imagePullTraffic.
  • Private Endpoint = entrant (couper l'accès public, privatelink.azurewebsites.net + scm) ; VNet integration = sortant (subnet délégué Microsoft.Web/serverFarms).
  • outboundVnetRouting : applicationTraffic (ex vnetRouteAllEnabled), imagePullTraffic (ex vnetImagePullEnabled), allTraffic.
  • Zones : privatelink.openai.azure.com, privatelink.search.windows.net, privatelink.vaultcore.azure.net, privatelink.azurecr.io.
  • Key Vault reference : @Microsoft.KeyVault(SecretUri=…), system identity par défaut, rotation sous 24 h, échec = chaîne littérale, settings sticky.
  • Slots dès Standard ; swap = settings appliqués, redémarrage, warm-up, échange ; retour arrière = re-swap.
  • Health check : 1 ping/min, 10 échecs, 50 % max retirés, remplacement après 1 h, deux instances minimum.
  • Journaux : Log stream (--docker-container-logging filesystem), AppServiceConsoleLogs, AppServicePlatformLogs, AppServiceHTTPLogs, Application Insights.
  • Docker Compose retiré le 31/03/2027 → sidecars.

Références#

Relevé du 23/10/2026 sur Microsoft Learn :

Sur cette page
Échap

Tape au moins 2 lettres. Plusieurs mots : tous doivent apparaître.