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

01 · L'architecture d'un chatbot RAG sur Azure, de bout en bout

32 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 IA sur Azure)

En une phrase : un chatbot RAG d'entreprise, c'est une application conteneurisée (Web App for Containers ou pods AKS) qui authentifie l'utilisateur avec Microsoft Entra ID, cherche des passages dans Azure AI Search, fait générer la réponse par un modèle Microsoft Foundry, le tout en privé (Private Endpoints, Private DNS zones, VNet integration) et sans clé (managed identities, Workload Identity) ; le support N2/N3 sait suivre une question de saut en saut et sait, à chaque saut, où regarder.

Un chatbot RAG privé sur Azure : la vue d'ensemble

Ce chapitre suppose les bases AKS du parcours AKS : réseau et Private Link (02), identités, Workload ID et Secrets Store CSI Driver (03), GitOps et Argo CD (05), RAG sans clé sur AKS (06). Les modèles, jetons et 429 sont détaillés au chapitre 02, l'observabilité au chapitre 07. Ici, on assemble les pièces avec l'œil du support : qui parle à qui, par quel chemin, avec quelle identité, et quelle trace cela laisse.

Noms à jour (relevé du 23/10/2026) : Azure AI Foundry s'appelle Microsoft Foundry ; les Azure AI services sont devenus les Foundry Tools (ex. Azure Document Intelligence in Foundry Tools) ; les rôles Azure AI User / Owner sont renommés Foundry User / Foundry Owner (mêmes IDs de rôle). La propriété vnetImagePullEnabled d'App Service devient outboundVnetRouting.imagePullTraffic (l'ancienne reste acceptée). Les deux noms circulent encore dans le code d'un client.

# Section Ce qu'on y apprend
1 Les briques Rôle de chaque service, ce qui casse, où il écrit
2 Les quatre chemins Requête, ingestion, déploiement, télémétrie
3 Web App ou AKS Ce qui change pour le support
4 Le réseau privé Private Endpoints, DNS, VNet integration, cas particuliers
5 Les identités Managed identities, Workload Identity, rôles exacts
6 Le voyage d'une question Dix étapes, et où regarder à chacune
7 Qui logue quoi La carte des journaux et métriques
8 Méthode de diagnostic Par quoi commencer
9 Symptômes et causes La table de dépannage
10–14 Pièges, lab, entretien, fiche

Ce que tu dois savoir expliquer en entretien#

Question type Section
« Dessinez-moi l'architecture d'un chatbot RAG sur Azure. » 1, 2
« Un utilisateur dit que le bot ne répond plus. Par quoi commencez-vous ? » 6, 8
« Tout est privé : comment l'app joint-elle Azure OpenAI ? Pourquoi un 403 alors que le rôle est bon ? » 4, 5
« Le nouveau build n'est pas en production. Où regardez-vous ? » 2.3
« Un document n'apparaît pas dans les réponses. » 2.2
« Où sont les logs de ce composant ? » 7

1. Les briques et leur rôle#

Brique Rôle dans le chatbot Panne typique vue par le support Où elle écrit
Microsoft Entra ID Authentifie l'utilisateur (SSO) et délivre les jetons des managed identities Boucle de connexion, AADSTS…, 401 Sign-in logs Entra, AppServiceAuthenticationLogs
Web App for Containers Héberge un front ou une API en conteneur, privée (Private Endpoint en entrée, VNet integration en sortie) Conteneur qui ne démarre pas, image non tirée, 502/503 Log stream, AppServiceConsoleLogs, AppServiceHTTPLogs, AppServicePlatformLogs
AKS (un cluster par projet, en général) Héberge l'API RAG, l'ingestion, parfois le front ; Ingress interne CrashLoopBackOff, ImagePullBackOff, OOMKilled, pods non prêts ContainerLogV2, KubeEvents, KubePodInventory, Managed Prometheus
Argo CD (cluster extension) Applique dans le cluster ce que dit le dépôt Git (GitOps) Application OutOfSync ou Degraded, sync en échec Logs des pods argocd, interface Argo CD
Azure Container Registry Premium Stocke les images ; Premium pour le Private Endpoint ImagePullBackOff, UNAUTHORIZED, push refusé depuis le pipeline Diagnostic settings (ContainerRegistryLoginEvents, ContainerRegistryRepositoryEvents)
Azure DevOps Pipelines Construit l'image (Docker@2), la pousse dans ACR, met à jour le tag dans le dépôt de configuration Job bloqué, push en timeout (agent sans route vers l'ACR privé) Journaux du run
Azure AI Search Index vectoriel et hybride : retrouve les passages pertinents 403 (rôle), 503 throttling, index vide, indexer en échec Métriques Search Latency, Throttled Search Queries Percentage ; AzureDiagnostics
Microsoft Foundry (modèles Azure OpenAI) Embeddings de la question, génération de la réponse 429 (quota TPM/RPM), 401/403, 400 content_filter, latence Métriques Azure OpenAI Requests, Time to Response ; AzureDiagnostics (RequestResponse)
Document Intelligence (Foundry Tools) OCR et layout des documents à l'ingestion Documents mal découpés, skill en échec, quota Historique de l'indexer, métriques de la ressource
Storage Account Documents sources (Blob) Indexer refusé (réseau ou rôle) Diagnostic settings du Blob
Key Vault Secrets qui restent (clés tierces, certificats) via Secrets Store CSI Driver ou référence App Service Pod bloqué en ContainerCreating (FailedMount) AzureDiagnostics ou tables resource-specific du coffre
Application Insights, Log Analytics, Managed Prometheus, Managed Grafana, Langfuse Le plan d'observabilité (chapitre 07) « No data », trous de télémétrie —

2. Les quatre chemins#

Quatre flux traversent l'architecture. Les nommer en entretien montre qu'on raisonne en chemins, pas en services.

Chemin Déclencheur Saute par Casse le plus souvent sur
① Requête Une question d'utilisateur Entra ID → Web App ou Ingress → API RAG → AI Search → Foundry → réponse Authentification, DNS privé, rôle manquant, 429
② Ingestion Nouveau document, planification de l'indexer Storage → indexer AI Search → Document Intelligence → embeddings → index Shared private link, rôle de l'identité du service de recherche, quota du skill
③ Déploiement Commit Azure Repos → pipeline → ACR → dépôt de configuration → Argo CD → pods ; ou ACR → Web App Agent sans route vers l'ACR privé, AcrPull, Argo CD non synchronisé
④ Télémétrie En continu Code (OpenTelemetry), plateforme (Diagnostic settings), agents AKS → Azure Monitor ; SDK → Langfuse Diagnostic setting absent, échantillonnage, ingestion privée mal configurée

2.1 Le chemin de la requête#

sequenceDiagram
    actor U as Utilisateur
    participant E as Microsoft Entra ID
    participant W as Web App / Ingress
    participant A as API RAG (pod)
    participant S as Azure AI Search
    participant F as Foundry (Azure OpenAI)
    U->>W: HTTPS (réseau privé)
    W->>E: Redirection SSO (Easy Auth ou MSAL)
    E-->>W: Jeton utilisateur
    W->>A: POST /chat (en-tête traceparent)
    A->>E: Jeton de la managed identity (Workload Identity)
    E-->>A: Jeton d'accès
    A->>F: Embedding de la question
    A->>S: Recherche vectorielle ou hybride
    S-->>A: Passages + métadonnées
    A->>F: Invite augmentée (question + passages)
    F-->>A: Réponse (ou 429 + retry-after-ms)
    A-->>W: Réponse + citations
    W-->>U: Affichage

Le détail saut par saut, avec les commandes, est à la section 6.

2.2 Le chemin de l'ingestion#

Alimenter l'index : de PDF à vecteurs, sans réseau public

Deux façons d'alimenter l'index : un indexer Azure AI Search (pull, planifié) avec un skillset, ou un job d'ingestion dans AKS qui appelle lui-même Document Intelligence et pousse les documents (push). Le schéma montre la première, la plus courante.

Élément À savoir pour le support
Document Layout skill Appelle le modèle layout de Document Intelligence (sortie Markdown ou texte, sections) ; au-delà de 20 transactions gratuites par jour, il faut attacher une ressource Foundry facturable au skillset
Région Facturation par clé : Foundry et AI Search dans la même région ; facturation keyless (Entra ID) : pas d'exigence de même région
Azure OpenAI Embedding skill Appelle un déploiement d'embedding ; même modèle à l'indexation et à la requête, sinon les vecteurs ne se comparent pas
Rôles L'identité du service de recherche a besoin de Storage Blob Data Reader sur le compte, Cognitive Services OpenAI User sur la ressource du modèle d'embedding, Cognitive Services User sur la ressource Foundry des skills
Réseau privé Shared private link (group ID blob, openai_account, cognitiveservices_account ou foundry_account), approuvé côté ressource, et indexer en executionEnvironment: private
Tier Shared private link à partir de Basic ; pour un indexer avec skillset, la page des limites indique S1 minimum (services créés après le 03/04/2024 avec les limites hautes), d'autres pages indiquent S2 : vérifier la page des limites du service
Document absent Toujours commencer par l'historique d'exécution de l'indexer (portail ou GET /indexers/{nom}/status) : erreurs et avertissements par document

Piège : si le Storage a un Private Endpoint, il faut aussi un shared private link, même dans la même région ; sinon le Storage refuse la connexion. Et un shared private link reste « Pending » tant que personne ne l'a approuvé côté Storage.

2.3 Le chemin du déploiement#

Livrer une nouvelle version : pipeline, ACR, Argo CD et Web App

Étape Ce qui doit être vrai Vérifier
③ build + push L'agent du pipeline a une route vers le Private Endpoint de l'ACR : les agents hébergés par Microsoft ne le peuvent pas, il faut un self-hosted agent ou un Managed DevOps Pool injecté dans un VNet Journal du job, az acr repository show-tags -n <acr> --repository <image>
③ authentification Service connection Docker Registry en workload identity federation, identité avec un rôle de push (AcrPush, ou Container Registry Repository Writer sur un registre ABAC) Service connection, AADSTS70021 = federated credential qui ne correspond pas
④ ⑤ GitOps Le tag est dans le dépôt de configuration ; Argo CD le voit kubectl get applications -n argocd, état Synced / Healthy
⑦ pull AKS L'identité kubelet a AcrPull ; le cluster résout l'ACR en IP privée az aks check-acr -g <rg> -n <aks> --acr <acr>.azurecr.io
⑧ pull Web App acrUseManagedIdentityCreds=true (+ acrUserManagedIdentityID pour une user-assigned), AcrPull, ARM audience tokens acceptés par l'ACR, pull routé par le VNet (imagePullTraffic) az webapp config show, az acr config authentication-as-arm show -r <acr>

Le détail GitOps (push ou pull, Argo CD, Flux) est dans AKS 05.

2.4 Le chemin de la télémétrie#

Qui écrit quoi, et où : la carte des journaux d'un chatbot

Tout ce qui n'est pas configuré n'existe pas au moment de l'incident : un Diagnostic setting créé après la panne ne rattrape rien. La carte complète est à la section 7 ; les requêtes KQL sont au chapitre 07.


3. Deux hébergements : Web App for Containers ou AKS#

Critère Web App for Containers AKS
Unité de déploiement Une app = un conteneur (un seul port HTTP exposé) Deployments, Services, Ingress, autant de pods que voulu
Entrée privée Private Endpoint sur l'app (sous-ressource sites) Ingress interne (load balancer interne ou Application Gateway for Containers)
Sortie privée VNet integration (subnet délégué à Microsoft.Web/serverFarms) Le pod est déjà dans le VNet (CNI)
Identité vers Azure Managed identity de l'app Workload Identity (ServiceAccount fédéré à une user-assigned managed identity)
Secrets restants Key Vault references dans les app settings Secrets Store CSI Driver (SecretProviderClass)
Déploiement Nouveau tag d'image puis redémarrage GitOps (Argo CD)
Premiers logs à ouvrir Log stream, puis AppServiceConsoleLogs / AppServicePlatformLogs kubectl describe pod, kubectl logs --previous, puis ContainerLogV2
Accès « dans » le conteneur SSH via Kudu (le client doit joindre le site SCM) kubectl exec, kubectl debug

3.1 Les réglages Web App que le support doit connaître#

Réglage Effet Panne si mal réglé
WEBSITES_PORT Port d'écoute du conteneur (80 par défaut) « didn't respond to HTTP pings on port… », site arrêté
WEBSITES_CONTAINER_START_TIME_LIMIT Délai de démarrage toléré (230 à 240 s par défaut selon la page, 1 800 s maximum) Conteneur lent (gros modèle chargé au démarrage) redémarré en boucle
Requête de warmup /robots933456.txt La plateforme attend n'importe quel code HTTP sur ce chemin Une app qui ne répond rien sur un chemin inconnu est relancée sans fin
acrUseManagedIdentityCreds, acrUserManagedIdentityID Pull de l'image avec la managed identity ImagePullFailure, UNAUTHORIZED
outboundVnetRouting.applicationTraffic / allTraffic (anciens vnetRouteAllEnabled, WEBSITE_VNET_ROUTE_ALL) Envoie le trafic sortant de l'app dans le VNet ; sans lui, seul le trafic RFC 1918 y passe Appels vers des services publics qui contournent le pare-feu, ou l'inverse
outboundVnetRouting.imagePullTraffic (ancien vnetImagePullEnabled) Tire l'image par le VNet (obligatoire pour un ACR privé) « Client with IP is not allowed access »
# Lecture seule : configuration conteneur, identité et routage d'une Web App
az webapp config show -g <rg> -n <app> --query "{linuxFx:linuxFxVersion, acrMI:acrUseManagedIdentityCreds, acrUAMI:acrUserManagedIdentityID}"
az webapp identity show -g <rg> -n <app>
az webapp vnet-integration list -g <rg> -n <app>
az resource show -g <rg> -n <app> --resource-type Microsoft.Web/sites --query properties.outboundVnetRouting
az webapp log tail -g <rg> -n <app>            # Log stream (exige la journalisation « filesystem »)

Côté AKS, les réglages équivalents (requests, limits, probes, ingress) et le dépannage des pods sont dans AKS 05, section 7.


4. Le réseau privé#

Le réseau privé : trois subnets, des Private Endpoints et leurs zones DNS

Trois idées suffisent :

  1. Private Endpoint = entrée privée d'un service PaaS (une NIC avec une IP du subnet snet-pe). Le service garde son nom public, c'est le DNS qui doit répondre l'IP privée.
  2. VNet integration = sortie privée d'une Web App. Elle ne rend pas l'app joignable en privé : c'est le Private Endpoint de l'app qui le fait.
  3. Public network access : Disabled sur chaque service : sans chemin privé fonctionnel, l'appel est refusé (403 ou timeout), même avec le bon rôle.

4.1 Les zones DNS à connaître#

Service Sous-ressource du Private Endpoint Private DNS zone
Web App (entrant, et Kudu/SCM) sites privatelink.azurewebsites.net (enregistrement de l'app et de scm)
Foundry / Azure OpenAI / Document Intelligence account privatelink.cognitiveservices.azure.com, privatelink.openai.azure.com, privatelink.services.ai.azure.com (une par suffixe utilisé)
Azure AI Search searchService privatelink.search.windows.net
Storage (Blob) blob privatelink.blob.core.windows.net
Key Vault vault privatelink.vaultcore.azure.net
ACR registry privatelink.azurecr.io (avec les enregistrements <acr>.<région>.data.azurecr.io)
AKS privé (API server) management privatelink.<région>.azmk8s.io
Azure Monitor (AMPLS) azuremonitor privatelink.monitor.azure.com, privatelink.oms.opinsights.azure.com, privatelink.ods.opinsights.azure.com, privatelink.agentsvc.azure-automation.net, privatelink.blob.core.windows.net
Azure Monitor workspace (requêtes Prometheus) prometheusMetrics privatelink.<région>.prometheus.monitor.azure.com
Azure Managed Grafana grafana privatelink.grafana.azure.com

Piège ACR : avec un Private Endpoint, l'ACR active les dedicated data endpoints (<acr>.<région>.data.azurecr.io) : une IP privée par réplica. Si l'enregistrement A du data endpoint manque dans la zone, docker login réussit mais le pull des couches échoue. Un cas réel documenté sur Microsoft Q&A : zones gérées en Terraform, A record du data endpoint oublié.

Piège Foundry : une ressource Foundry expose trois suffixes (cognitiveservices, openai, services.ai). Configurer le DNS pour chaque suffixe que le code utilise ; une seule zone liée ne suffit pas si le SDK appelle *.openai.azure.com.

4.2 Tester le réseau depuis l'appelant#

Le test se fait depuis l'appelant (pod, conteneur de la Web App), jamais depuis son poste.

# Depuis un pod existant de l'API, si l'image contient les outils (sinon, pod de debug : voir la remarque)
kubectl exec -n <ns> <pod-api> -- nslookup <search>.search.windows.net   # doit renvoyer 10.x.x.x (IP du snet-pe)
kubectl exec -n <ns> <pod-api> -- nc -z -w 3 <search>.search.windows.net 443 && echo "port 443 joignable"

# Pod de debug éphémère (crée une ressource : à faire valider avant en PRD)
kubectl run netdebug -n <ns> --rm -it --image=mcr.microsoft.com/cbl-mariner/busybox:2.0 -- sh

# Côté Web App : console SSH (Kudu) du conteneur ; le client doit joindre le site SCM (Private Endpoint + DNS scm)
az webapp ssh -g <rg> -n <app>            # ou portail : Development Tools > SSH
Résultat du test Interprétation
Le nom résout une IP publique Zone privée non liée au VNet qui résout (ou DNS personnalisé qui ne forwarde pas vers Azure DNS)
IP privée, mais timeout NSG ou UDR sur le chemin, Private Endpoint dans un état non Approved, VNet integration sans route
IP privée, 403 immédiat Le réseau est bon : c'est l'identité ou le rôle (section 5)
NXDOMAIN Zone privée liée mais sans enregistrement pour ce nom (Private Endpoint recréé, A record supprimé)

4.3 Les cas particuliers#

  • Azure AI Search sortant : le service n'est pas dans un VNet ; ses indexers sortent par shared private link (section 2.2).
  • Pipeline vers ACR privé : agent dans un VNet (Managed DevOps Pools, en évitant la plage 172.17.0.0/16 réservée par le service, ou self-hosted).
  • Télémétrie privée : l'ingestion Application Insights et Log Analytics passe par un Azure Monitor Private Link Scope ; les métriques Managed Prometheus d'un cluster passent par le DCE MSProm-<région>-<cluster> ajouté à l'AMPLS. Une zone privatelink.monitor.azure.com mal liée coupe la télémétrie de tous les composants du VNet.
  • Log stream et SSH d'une Web App privée : ils passent par le site SCM ; le poste de l'opérateur doit le joindre en privé.

5. Les identités : qui appelle qui, avec quel rôle#

Le principe : aucune clé. Chaque appelant a une identité Entra, chaque cible un rôle au plus près (la ressource, pas l'abonnement).

Appelant (identité) Cible Rôle minimal Erreur typique si absent
API RAG (Workload Identity ou managed identity de la Web App) Foundry / Azure OpenAI (inférence) Cognitive Services OpenAI User (ou Foundry User pour les projets et agents Foundry) 401/403 ; attention, Cognitive Services Contributor ne permet pas l'inférence avec Entra ID
API RAG Azure AI Search (requête) Search Index Data Reader (et RBAC activé sur le service) 403 ; 401 si le service est resté en mode API Key
API RAG Key Vault Key Vault Secrets User 403 ForbiddenByRbac
API RAG Storage (téléchargement des sources citées) Storage Blob Data Reader 403 AuthorizationPermissionMismatch
API RAG Application Insights (si local authentication désactivée) Monitoring Metrics Publisher Télémétrie rejetée (403 dans les logs du SDK)
Identité kubelet (AKS) ACR AcrPull ImagePullBackOff 401
Managed identity de la Web App ACR AcrPull ImagePullFailure
Service de recherche (managed identity) Storage, modèle d'embedding, ressource Foundry des skills Storage Blob Data Reader, Cognitive Services OpenAI User, Cognitive Services User Indexer en erreur sur chaque document
Service connection du pipeline (workload identity federation) ACR AcrPush (ou rôle de dépôt ABAC) unauthorized au push
Managed identity de Grafana Azure Monitor, Azure Monitor workspace Monitoring Reader, Monitoring Data Reader Tableaux « No data » (chapitre 07)

Points qui font perdre des heures :

  • Propagation : une affectation de rôle prend quelques minutes (Microsoft parle de 5 minutes pour Azure OpenAI, 5 à 10 minutes pour AI Search) ; un 403 juste après l'affectation n'est pas un diagnostic.
  • Custom subdomain : l'authentification Entra sur une ressource Azure OpenAI exige un sous-domaine personnalisé (https://<nom>.openai.azure.com), pas le point de terminaison régional.
  • AI Search : le mode d'authentification (API Key, Role-based access control, Both) se choisit dans Settings > Keys ; un service resté en API Key refuse les jetons Entra.
  • Workload Identity : le subject du federated credential doit être exactement system:serviceaccount:<namespace>:<sa>, le pod porter le label azure.workload.identity/use: "true", le ServiceAccount l'annotation azure.workload.identity/client-id. Détail et lab dans AKS 03.
  • AADSTS70021 (No matching federated identity record found) : issuer ou subject qui ne correspond pas, ou federated credential créé il y a quelques secondes (propagation).
# Lecture seule : qui a quel rôle sur la ressource Azure OpenAI ?
az role assignment list --scope <id-ressource-openai> -o table
# L'identité du pod : ServiceAccount, annotation, label du pod, variables injectées
kubectl get sa <sa> -n <ns> -o yaml
kubectl get pod <pod> -n <ns> -o jsonpath='{.metadata.labels}'
kubectl exec <pod> -n <ns> -- env | grep AZURE_
az identity federated-credential list --identity-name <uami> -g <rg> -o table

6. Le voyage d'une question, pas à pas#

Le voyage d'une question, et où regarder à chaque saut

Le scénario : un utilisateur interne pose « Quelle est la procédure de remboursement ? ». Voici les dix étapes, ce qui se passe, et où le support regarde si l'étape échoue.

# Étape Ce qui se passe Où regarder en premier Puis
1 Résolution du nom Le poste résout chat.interne… vers l'IP privée du Private Endpoint de la Web App (ou de l'Ingress) nslookup depuis le poste ; zone privatelink.azurewebsites.net DNS du hub, forwarders on-premises
2 Connexion SSO Easy Auth (ou MSAL dans le front) redirige vers Entra ID et reçoit un jeton AppServiceAuthenticationLogs, sign-in logs Entra (Conditional Access) App registration : redirect URI, secret ou certificat expiré
3 Requête HTTP Le front reçoit POST /chat ; Easy Auth injecte X-MS-CLIENT-PRINCIPAL AppServiceHTTPLogs (code, durée), Log stream AppRequests du front
4 Appel de l'API Le front appelle l'API RAG (Ingress interne) avec l'en-tête traceparent AppDependencies du front (cible, code) Logs de l'Ingress, kubectl get endpoints (pods prêts ?)
5 Pod API L'API traite la requête AppRequests de l'API ; kubectl logs ; KubePodInventory (restarts) ContainerLogV2 filtré sur le pod
6 Jeton de la managed identity Workload Identity échange le jeton du ServiceAccount contre un jeton Entra AADSTS… dans les logs du pod ; AppDependencies vers login.microsoftonline.com si l'instrumentation HTTP capture cet appel Section 5
7 Embedding de la question Appel du déploiement d'embedding Foundry AppDependencies (cible *.openai.azure.com), code 429 / 401 Métriques Azure OpenAI Requests par StatusCode
8 Recherche Requête vectorielle ou hybride sur l'index AppDependencies vers *.search.windows.net ; AzureDiagnostics Query.Search Search Latency, Throttled Search Queries Percentage
9 Génération Appel du modèle de chat avec les passages AppDependencies (durée, 429, retry-after-ms) ; Langfuse (prompt, jetons, coût) Time to Response, Processed Inference Tokens ; chapitre 02
10 Réponse L'API renvoie la réponse et les citations AppRequests (durée totale) ; Langfuse (trace, score, feedback) Qualité : chunks retrouvés, index à jour

Le fil qui relie tout : l'operation_Id (colonne OperationId dans Log Analytics), égal au trace id W3C. On part de la requête en échec et on liste tout ce qui porte le même identifiant :

// 1. Les requêtes en échec de la dernière heure, par service
AppRequests
| where TimeGenerated > ago(1h) and Success == false
| project TimeGenerated, AppRoleName, Name, ResultCode, DurationMs, OperationId
| order by TimeGenerated desc

// 2. Tout ce qui s'est passé pour une requête donnée (coller l'OperationId)
union AppRequests, AppDependencies, AppExceptions, AppTraces
| where OperationId == "<operation-id>"
| project TimeGenerated, Type, AppRoleName, Name, Target, ResultCode, DurationMs, Message, OuterMessage
| order by TimeGenerated asc
flowchart LR
    A["AppRequests en échec<br/>(front ou API)"] --> B["OperationId"]
    B --> C["AppDependencies<br/>même OperationId"]
    C --> D{"Première dépendance<br/>en erreur ?"}
    D -- "AI Search 403" --> E["Rôle / RBAC du service<br/>(section 5)"]
    D -- "Azure OpenAI 429" --> F["Quota TPM du déploiement<br/>(chapitre 02)"]
    D -- "Timeout" --> G["DNS / Private Endpoint<br/>(section 4)"]
    D -- "Aucune" --> H["AppExceptions<br/>bug applicatif"]

7. Qui logue quoi, et où#

Composant Signal Destination (table ou outil) Actif par défaut ?
Code de l'application Requêtes, dépendances, exceptions, logs, métriques Application Insights : AppRequests, AppDependencies, AppExceptions, AppTraces, AppMetrics Non : instrumentation OpenTelemetry + connection string
Code (GenAI) Prompts, réponses, appels d'outils (conventions gen_ai.*) AppGenAIContent (table dédiée, protégeable) Non
Code Traces LLM, jetons, coût, scores Langfuse Non : SDK Langfuse ou export OTLP
Web App stdout/stderr du conteneur Log stream (temps réel) ; AppServiceConsoleLogs Log stream : après az webapp log config --docker-container-logging filesystem ; table : Diagnostic setting
Web App Requêtes HTTP entrantes AppServiceHTTPLogs Non (Diagnostic setting)
Web App Démarrage du conteneur, pull d'image AppServicePlatformLogs ; fichiers *_docker.log dans /home/LogFiles Fichiers : oui ; table : Diagnostic setting
Web App Authentification Easy Auth AppServiceAuthenticationLogs Non
AKS (pods) stdout/stderr ContainerLogV2 Si Container Insights est activé
AKS Événements Kubernetes KubeEvents (événements Warning seulement par défaut) ; kubectl get events (1 heure dans le cluster) Container Insights
AKS État des pods, restarts, raisons KubePodInventory Container Insights
AKS Métriques nodes, pods, kube-state-metrics, applicatives Managed Prometheus (Azure Monitor workspace) Si --enable-azure-monitor-metrics
AKS control plane Audit, autoscaler, API server AKSAudit, AKSAuditAdmin, AKSControlPlane Non (Diagnostic setting)
Azure OpenAI Métriques (requêtes, jetons, latence) Azure Monitor Metrics Oui
Azure OpenAI Audit, RequestResponse, Trace, AzureOpenAIRequestUsage AzureDiagnostics Non (Diagnostic setting)
Azure AI Search Métriques (latence, QPS, throttling) Azure Monitor Metrics (30 jours dans le portail) Oui
Azure AI Search Opérations (requêtes, indexation) AzureDiagnostics (OperationName = Query.Search…) Non (Diagnostic setting)
Toutes ressources Création, suppression, changement de config Activity log (AzureActivity si exporté) Oui (Activity log)

À dire en entretien : « Les métriques de plateforme et l'Activity log existent toujours. Les journaux de ressource n'existent que si un Diagnostic setting les envoie quelque part. La télémétrie applicative n'existe que si le code est instrumenté. Avant tout incident, je vérifie que ces trois couches sont en place. »


8. Méthode : du symptôme au saut fautif#

flowchart TD
    S["Symptôme signalé"] --> Q1{"Tous les utilisateurs<br/>ou un seul ?"}
    Q1 -- "Un seul" --> U["Entra : sign-in logs,<br/>Conditional Access, groupe,<br/>cache navigateur"]
    Q1 -- "Tous" --> Q2{"Depuis quand ?<br/>Corrélé à un changement ?"}
    Q2 --> C["Activity log, historique<br/>Argo CD / pipeline,<br/>Azure Service Health"]
    Q2 --> Q3{"Erreur ou lenteur ?"}
    Q3 -- "Erreur 5xx" --> E["AppRequests en échec<br/>→ OperationId<br/>→ première dépendance en erreur"]
    Q3 -- "Lenteur" --> L["AppDependencies triées par durée<br/>Search Latency, Time to Response,<br/>pods en throttling CPU"]
    Q3 -- "Rien ne répond" --> R["Web App : Log stream, état du site<br/>AKS : kubectl get pods, events,<br/>endpoints de l'Ingress"]
    E --> X["Confirmer côté service :<br/>métriques + Diagnostic settings"]
    L --> X
    R --> X

Règles de conduite N2/N3 :

  1. Délimiter : qui est touché, depuis quand, quel changement récent (déploiement, rotation de secret, modification réseau).
  2. Remonter le chemin dans l'ordre : entrée, application, dépendances. Le premier saut en erreur est la piste.
  3. Confirmer côté service (métriques, Diagnostic settings) avant d'accuser le code ou d'ouvrir un ticket Microsoft.
  4. Ne rien modifier en production sans demande validée : le diagnostic se fait en lecture seule.
  5. Documenter l'OperationId, l'heure UTC, la requête KQL : c'est ce que demandera le support Microsoft.

9. Symptôme → cause → vérification → correction#

Symptôme Cause probable Vérification (lecture seule) Correction
Boucle de connexion, page d'erreur Entra Redirect URI, secret de l'App registration expiré, Conditional Access Sign-in logs Entra (code AADSTS) ; AppServiceAuthenticationLogs Renouveler le secret (ou passer en certificat / federated credential), corriger l'URI
403 sur le site depuis le réseau interne Le poste résout l'IP publique (Private Endpoint de l'app non résolu) nslookup <app>.azurewebsites.net depuis le poste Forwarder DNS vers la zone privatelink.azurewebsites.net
Web App : « didn't respond to HTTP pings » Mauvais port, démarrage trop long, app qui ne répond pas au warmup Log stream, AppServicePlatformLogs WEBSITES_PORT, WEBSITES_CONTAINER_START_TIME_LIMIT
Web App : ImagePullFailure / UNAUTHORIZED AcrPull manquant, ARM audience tokens refusés par l'ACR, pull pas routé par le VNet, data endpoint non résolu az acr config authentication-as-arm show, outboundVnetRouting, DNS *.data.azurecr.io Rôle, authentication-as-arm, imagePullTraffic, A record
Pod ImagePullBackOff Tag inexistant, AcrPull kubelet, DNS ACR kubectl describe pod, az aks check-acr Corriger tag ou rôle ou DNS
Pod ContainerCreating long, FailedMount Secrets Store CSI : SecretProviderClass, rôle Key Vault, réseau du coffre kubectl describe pod (Events) Rôle Key Vault Secrets User, nom d'objet, Private Endpoint du coffre
Pod CrashLoopBackOff juste après un déploiement Variable manquante, config, exception au démarrage kubectl logs --previous Rollback par Git (git revert) via Argo CD
OOMKilled Limite mémoire trop basse, fuite, gros documents KubePodInventory (ContainerLastStatus), kubectl describe pod Ajuster limits, corriger l'app
401 vers Azure OpenAI Pas de custom subdomain, mauvais scope de jeton, Workload Identity non injectée AppDependencies ; `env grep AZURE_` dans le pod
403 vers Azure OpenAI ou AI Search Rôle manquant ou en propagation, public network access refusé (appel sorti en public) az role assignment list, DNS depuis le pod Rôle data plane ; DNS privé
429 en série sur la génération Quota TPM/RPM du déploiement atteint, retries trop agressifs Métrique Azure OpenAI Requests split StatusCode, retry-after-ms Respecter retry-after-ms, réduire max_tokens, augmenter le TPM (chapitre 02)
503 ou « too many requests » d'AI Search Throttling (ressources du service saturées, indexation concurrente) Throttled Search Queries Percentage Réplicas, planifier l'indexation hors pointe
Réponses « je ne sais pas », citations absentes Index vide ou périmé, indexer en échec, filtre de sécurité Historique de l'indexer, nombre de documents de l'index Relancer l'indexer après correction (shared private link, rôle, quota du skill)
Nouveau build pas en production Pipeline bloqué au push, tag non mis à jour, Argo CD OutOfSync Journal du run, kubectl get applications -n argocd Agent en VNet, rôle AcrPush, sync Argo CD
Plus aucune télémétrie depuis un changement réseau AMPLS ou zone privatelink.monitor.azure.com modifiée DNS de <région>.in.applicationinsights.azure.com depuis l'app Corriger le lien de zone ou l'AMPLS

10. Pièges de production#

Piège Pourquoi ça arrive Parade
« Le rôle est bon mais 403 » L'appel sort en public (DNS) et le service refuse ; ou rôle de plan de contrôle au lieu du plan de données Tester le DNS depuis l'appelant ; vérifier un rôle Data / User
Cognitive Services Contributor donné « pour aller vite » Ce rôle ne permet pas l'inférence avec Entra ID Cognitive Services OpenAI User (ou Foundry User)
Une seule Private DNS zone pour Foundry Le SDK appelle un autre suffixe (openai au lieu de cognitiveservices) Une zone par suffixe utilisé
Zones DNS dupliquées par projet Deux zones privatelink.azurecr.io liées à des VNets qui se résolvent mutuellement : enregistrements écrasés Zones centralisées dans le hub, une par service
Diagnostic settings créés « plus tard » Pas de données au moment de l'incident Les déployer avec l'infrastructure (Terraform, Azure Policy)
Log stream vide Journalisation filesystem non activée, ou poste sans accès au site SCM privé az webapp log config … --docker-container-logging filesystem ; accès réseau au SCM
Rotation de secret qui casse la prod Secret copié en variable d'environnement (CSI secretObjects) : rotation sans redémarrage Lire Key Vault à chaud ou redémarrer ; mieux, supprimer le secret (Entra)
Retries qui aggravent les 429 Le SDK OpenAI retente déjà 2 fois ; une boucle maison multiplie les appels max_retries=0 si boucle maison, respecter retry-after-ms
Shared private link « Pending » Personne n'a approuvé la connexion côté ressource cible Approbation par le propriétaire de la ressource
Indexer qui marchait, puis échoue « au hasard » executionEnvironment non fixé : le service bascule entre environnements executionEnvironment: private

11. Lab : cartographier un chatbot existant en lecture seule#

Objectif : en arrivant chez le client, produire en une heure la carte de son chatbot. Uniquement des commandes de lecture, après avoir vérifié le contexte (az account show).

# 0. Contexte : quel abonnement ? (si l'environnement n'est pas identifié, le considérer comme PRD)
az account show --query "{sub:name, id:id, tenant:tenantId}" -o table

# 1. Inventaire du resource group du projet
az resource list -g <rg> --query "[].{type:type, name:name}" -o table

# 2. Les Private Endpoints et leur état d'approbation
az network private-endpoint list -g <rg> \
  --query "[].{pe:name, cible:privateLinkServiceConnections[0].privateLinkServiceId, etat:privateLinkServiceConnections[0].privateLinkServiceConnectionState.status}" -o table

# 3. Les zones DNS privées et leurs liens VNet
az network private-dns zone list --query "[].name" -o table
az network private-dns link vnet list -g <rg-dns> -z privatelink.openai.azure.com -o table

# 4. Web App : image, identité, routage
az webapp config container show -g <rg> -n <app>
az webapp identity show -g <rg> -n <app>

# 5. AKS : add-ons d'observabilité, Workload Identity, Argo CD
az aks show -g <rg> -n <aks> --query "{oidc:oidcIssuerProfile.enabled, wi:securityProfile.workloadIdentity.enabled, prom:azureMonitorProfile.metrics.enabled, logs:addonProfiles.omsagent.enabled}"
az k8s-extension list -g <rg> -c <aks> -t managedClusters -o table
kubectl get applications -n argocd

# 6. Les Diagnostic settings de chaque service IA
az monitor diagnostic-settings list --resource <id-openai> -o table
az monitor diagnostic-settings list --resource <id-search> -o table

# 7. Les rôles de l'identité de l'API
az role assignment list --assignee <principal-id> --all -o table

Résultat attendu : un schéma comme celui de la section 1, annoté avec les vrais noms, la liste des zones DNS, des identités et de leurs rôles, et la liste des Diagnostic settings manquants (à proposer, pas à créer soi-même).


12. Questions d'entretien#

1. Décrivez l'architecture d'un chatbot RAG sur Azure.

Un front et une API conteneurisés (Web App for Containers ou AKS), authentification Entra ID, API RAG qui calcule l'embedding de la question, interroge Azure AI Search, puis envoie l'invite augmentée à un modèle Foundry. Ingestion par un indexer AI Search avec Document Intelligence et un skill d'embedding. Tout en Private Endpoints, aucune clé grâce aux managed identities et à Workload Identity. Images dans ACR Premium, déploiement GitOps par Argo CD. Observabilité : Application Insights, Log Analytics, Managed Prometheus, Managed Grafana, Langfuse.

2. Un utilisateur dit que le bot ne répond plus. Par quoi commencez-vous ?

Délimiter (un utilisateur ou tous, depuis quand, changement récent), puis remonter le chemin : Log stream ou AppServiceHTTPLogs à l'entrée, AppRequests en échec, puis toutes les lignes du même OperationId pour trouver la première dépendance en erreur. Je confirme côté service avec les métriques avant de conclure.

3. Tout est privé. Comment la Web App joint-elle Azure OpenAI ?

Par sa VNet integration (sortie dans un subnet délégué), puis le Private Endpoint d'Azure OpenAI dans le subnet des Private Endpoints. Le nom *.openai.azure.com doit résoudre l'IP privée grâce à la zone privatelink.openai.azure.com liée au VNet qui résout. Pour l'entrée, c'est le Private Endpoint de la Web App, pas la VNet integration.

4. 403 vers AI Search alors que le rôle est attribué. Hypothèses ?

Propagation du rôle (quelques minutes), service resté en mode API Key (les jetons Entra sont refusés), rôle de contrôle au lieu de Search Index Data Reader, ou appel qui sort en public parce que le DNS ne résout pas l'IP privée et que l'accès public est désactivé. Je teste le DNS depuis le pod, puis je liste les rôles sur la ressource.

5. Pourquoi le pipeline n'arrive-t-il pas à pousser dans l'ACR ?

L'ACR est privé : les agents hébergés par Microsoft n'ont pas de route vers le Private Endpoint. Il faut un self-hosted agent ou un Managed DevOps Pool injecté dans un VNet qui résout privatelink.azurecr.io, plus une service connection en workload identity federation avec un rôle de push.

6. La Web App n'arrive pas à tirer son image.

Vérifier acrUseManagedIdentityCreds (et l'ID de l'identité user-assigned), le rôle AcrPull, que l'ACR accepte les ARM audience tokens, que le pull passe par le VNet (imagePullTraffic) et que le data endpoint de l'ACR est résolu en IP privée. AppServicePlatformLogs et le Log stream donnent l'erreur exacte.

7. Un document récent n'est jamais cité.

Historique de l'indexer d'abord : document en erreur (réseau, rôle, skill), indexer pas lancé depuis, quota du Document Layout skill. Ensuite vérifier que le document est dans l'index (recherche sur son nom), puis les filtres de sécurité éventuels.

8. Comment suivez-vous une requête de bout en bout ?

Par le trace id W3C propagé dans l'en-tête traceparent, qui devient l'operation_Id de toutes les lignes Application Insights. À condition que chaque service soit instrumenté avec OpenTelemetry et porte un cloud_RoleName distinct.

9. Quelle différence entre Private Endpoint et VNet integration ?

Le Private Endpoint est l'entrée privée d'un service (une IP dans votre VNet). La VNet integration est la sortie de la Web App vers votre VNet. Une Web App totalement privée a besoin des deux.

10. Pourquoi Workload Identity plutôt qu'une clé dans un secret Kubernetes ?

Aucun secret à stocker ni à faire tourner, une identité par application, des rôles au plus près, une trace dans les journaux Entra. Un Secret Kubernetes n'est qu'encodé en base64 et se retrouve dans etcd.

11. Le bot est lent depuis ce matin. Votre démarche ?

AppDependencies triées par durée pour isoler le saut lent ; puis côté service : Time to Response et utilisation PTU pour Azure OpenAI, Search Latency pour AI Search, CPU throttling et restarts pour les pods. Langfuse pour voir si les invites ont grossi (plus de chunks, plus de jetons).

12. Qu'est-ce qui doit être en place avant le premier incident ?

Diagnostic settings sur chaque service IA et sur AKS, Container Insights en ContainerLogV2, Managed Prometheus, instrumentation OpenTelemetry avec cloud_RoleName, Langfuse, alertes sur 5xx, 429 et restarts, et des tableaux Grafana partagés.


13. Fiche à retenir#

  • Quatre chemins : requête, ingestion, déploiement, télémétrie ; savoir les dessiner et dire où chacun casse.
  • Private Endpoint = entrée privée ; VNet integration = sortie de la Web App ; DNS privé = la moitié des incidents.
  • Zones : privatelink.azurewebsites.net, .openai.azure.com, .cognitiveservices.azure.com, .services.ai.azure.com, .search.windows.net, .blob.core.windows.net, .vaultcore.azure.net, .azurecr.io (+ data endpoints).
  • Rôles data plane : Cognitive Services OpenAI User (pas Contributor), Search Index Data Reader, Key Vault Secrets User, Storage Blob Data Reader, AcrPull, Monitoring Metrics Publisher.
  • Workload Identity : federated credential (issuer, subject exact), annotation du ServiceAccount, label du pod.
  • Web App : WEBSITES_PORT, WEBSITES_CONTAINER_START_TIME_LIMIT, acrUseManagedIdentityCreds, imagePullTraffic, authentication-as-arm sur l'ACR.
  • Indexer privé : shared private link approuvé + executionEnvironment: private ; historique de l'indexer d'abord.
  • ACR privé : agent de build dans un VNet (Managed DevOps Pools ou self-hosted).
  • Une question = un OperationId ; partir de l'AppRequests en échec, trouver la première dépendance en erreur.
  • Diagnostic settings : rien n'est collecté sans eux ; métriques de plateforme et Activity log, si.

Références#

Relevé du 23/10/2026 :

Sur cette page
Échap

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