AI
Azure API Management AI gateway : valider un circuit breaker backend avant le basculement en production
Un runbook de production pour qualifier circuit breaker, Retry-After, pool de backends Microsoft Foundry, trafic canari, observabilité et rollback avant d'activer le failover d'une API IA.
Une API IA exposée par Azure API Management appelle un déploiement de modèle principal et garde un second endpoint Microsoft Foundry en secours. Lors d’un pic de charge, le principal commence à retourner des 429 et quelques 5xx. Ajouter les deux endpoints à un pool puis activer un circuit breaker paraît suffisant pour maintenir le service.
Le risque est de déplacer l’incident. Un seuil trop sensible ouvre le circuit sur une rafale courte. Un seuil trop permissif laisse les retries amplifier la saturation. Un backend secondaire moins capacitaire reçoit tout le trafic, ou une erreur d’identité 401 est confondue avec une panne transitoire. Ce runbook construit une décision de basculement à partir du contrat de trafic, de la configuration réellement déployée, d’un test canari et des preuves APIM et backend.
Figer le contrat avant de toucher au pool
Décrivez l’unité de production complète : API, opération, backends, déploiements de modèles, quotas, identité et comportement client. Le circuit breaker protège un backend; il ne corrige ni un budget de tokens incohérent, ni une identité invalide, ni une stratégie de retry non bornée.
gateway: apim-ai-prod
api: assistant-operations-v1
operation: POST /responses
backends:
primary: foundry-model-weu
secondary: foundry-model-neu
failure_signals:
transient:
- 429-with-retry-after
- 500-to-599
configuration:
- 401
- 403
client:
- 400
- 404
canary_share_percent: 5
max_end_to_end_latency_ms: 12000
max_attempts_across_all_layers: 2
promote_requires:
- secondary-model-and-deployment-validated
- identity-proven-on-both-backends
- circuit-opens-and-recovers-as-designed
- no-retry-amplification
rollback_on:
- secondary-saturation
- semantic-regression
- unexplained-401-or-403
- missing-correlation-evidence Un Private Endpoint peut sécuriser le chemin vers un endpoint Foundry, mais il ne décide pas si le backend est sain. DNS et réseau doivent être validés séparément; le circuit breaker ne doit pas masquer une résolution ou une route défectueuse.
Lire les backends réellement déployés
Inventoriez d’abord les ressources backend APIM avec l’API de management. Conservez la réponse et son ETag avec le changement : ils constituent la base du rollback.
SUBSCRIPTION_ID="00000000-0000-0000-0000-000000000000"
RG="rg-ai-platform-prod"
APIM="apim-ai-prod"
API_VERSION="2024-05-01"
BASE="https://management.azure.com/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RG/providers/Microsoft.ApiManagement/service/$APIM"
az rest --method get --url "$BASE/backends?api-version=$API_VERSION" --output json > apim-backends-before.json
jq '.value[] | {
name,
type: .properties.type,
url: .properties.url,
pool: .properties.pool,
circuitBreaker: .properties.circuitBreaker
}' apim-backends-before.json Vérifiez que le pool référence les bons IDs ARM, pas seulement des noms ressemblants. Comparez priorité, poids, région, modèle et déploiement. Un backend de priorité supérieure doit être capable de recevoir le trafic attendu lorsque le principal sort du pool.
Choisir les erreurs qui doivent ouvrir le circuit
Pour une API de modèle, 429 et 5xx peuvent représenter une saturation ou une indisponibilité transitoire. 401 et 403 signalent généralement un problème d’audience, de rôle, de credential ou de policy : basculer vers un autre backend peut cacher la dérive sans la réparer. Les 4xx produits par le client ne doivent pas faire sortir un backend sain du pool.
Le seuil doit être relié au volume. Trois erreurs sur cinq requêtes n’ont pas la même signification que trois erreurs sur cinquante mille. Utilisez un count pour un flux prévisible ou un percentage lorsque la volumétrie varie, puis testez le comportement avec un échantillon représentatif. acceptRetryAfter permet au backend d’influencer la durée d’ouverture à partir de Retry-After; n’activez cette option que si l’en-tête est présent, cohérent et borné dans vos scénarios de test.
{
"properties": {
"title": "Foundry model West Europe",
"description": "Primary production model endpoint",
"protocol": "http",
"url": "https://foundry-model-weu.example.azure.com",
"circuitBreaker": {
"rules": [
{
"name": "transient-model-failures",
"failureCondition": {
"count": 5,
"interval": "PT30S",
"statusCodeRanges": [
{ "min": 429, "max": 429 },
{ "min": 500, "max": 599 }
]
},
"tripDuration": "PT30S",
"acceptRetryAfter": true
}
]
}
}
} Cet exemple est un point de départ de test, pas une valeur universelle. Le circuit breaker backend n’est pas disponible sur le tier Consumption d’API Management. Vérifiez le tier et la version d’API supportée avant de préparer le rollout.
Prouver que le secondaire est un backend réel
Un endpoint secondaire déclaré n’est pas encore une capacité de reprise. Exécutez le même prompt de canari, avec la même identité et les mêmes paramètres essentiels, directement sur chaque backend puis via APIM. Vérifiez au minimum : modèle et version attendus, limites de tokens, filtres de sécurité, latence, région, accès réseau et journalisation.
Le canari doit être déterministe autant que possible et sans action métier. Il peut demander une sortie JSON courte dont le schéma est validé mécaniquement. Pour un agent, désactivez les outils avec effet pendant ce test; l’objectif est de qualifier le chemin modèle, pas d’exécuter un runbook.
{
"input": "Return only a JSON object with status=ready and contract=ai-gateway-v1.",
"max_output_tokens": 40,
"metadata": {
"test": "apim-circuit-breaker-canary",
"change": "chg-2026-09-14-01"
}
} Une réponse 200 ne suffit pas. Validez le schéma, le marqueur de déploiement attendu et la trace corrélée. Si le secondaire répond mais applique un autre filtre, un autre modèle ou un contrat de sortie différent, le failover est techniquement disponible mais fonctionnellement invalide.
Tester ouverture, dérivation et récupération
Déployez d’abord la règle sur un backend et une API canari. Générez une séquence bornée de réponses simulées ou utilisez un backend de test contrôlé; ne provoquez pas une saturation réelle du modèle. Le test doit montrer quatre états : trafic normal, seuil atteint, backend écarté, puis retour après la durée d’ouverture.
Case A - baseline
Primary returns 200
Expected: primary selected, one backend call, schema valid
Case B - below threshold
Primary returns fewer failures than configured count
Expected: circuit remains closed, no uncontrolled retry burst
Case C - threshold reached
Primary returns qualified 429 or 5xx responses
Expected: circuit opens, pool selects eligible secondary, clients see bounded behavior
Case D - configuration error
Primary returns 401 or 403
Expected: no failover caused by circuit rule, identity incident remains visible
Case E - recovery
Trip duration expires and primary is healthy
Expected: traffic resumes according to pool priority and weight, without oscillation Figez pour chaque cas l’heure UTC, l’ID de corrélation, le backend choisi, le code APIM, le code backend, la latence et le nombre total de tentatives. Quand le circuit est ouvert et qu’aucun backend éligible ne reste, APIM peut retourner 503; ce résultat doit faire partie du contrat client, pas être découvert pendant l’incident.
Corréler APIM, modèle et retries
Les noms de tables varient selon la destination et le mode de diagnostics. Partez du schéma réellement ingéré, puis construisez une vue qui sépare réponse gateway et réponse backend. La requête suivante illustre le résultat attendu, pas un nom de table garanti.
let Start = datetime(2026-09-14T14:00:00Z);
let End = datetime(2026-09-14T14:30:00Z);
ApiManagementGatewayLogs
| where TimeGenerated between (Start .. End)
| where ApiId == "assistant-operations-v1"
| extend CorrelationId = tostring(CorrelationId)
| project TimeGenerated,
CorrelationId,
BackendId,
ResponseCode,
BackendResponseCode,
TotalTime,
BackendTime,
LastErrorReason,
LastErrorMessage
| order by TimeGenerated asc Regroupez ensuite par corrélation et fenêtre courte. Une hausse des appels backend par requête cliente révèle une amplification de retry. Un 503 APIM sans tentative backend pendant la fenêtre d’ouverture peut être conforme. Un 401 backend doit rester visible comme incident d’identité. Une réponse secondaire 200 avec latence ou consommation hors budget n’est pas une reprise réussie.
Décider déploiement, ajustement ou rollback
deploy:
when:
- canary-proves-primary-and-secondary-contracts
- only-transient-failures-trip-the-circuit
- secondary-capacity-is-bounded-and-observed
- retry-count-remains-within-contract
- recovery-does-not-oscillate
adjust:
when:
- threshold-is-too-sensitive-for-observed-volume
- retry-after-exceeds-operational-budget
- pool-weight-overloads-secondary
action:
- change-one-parameter
- replay-the-same-test-matrix
hold:
when:
- backend-identity-or-model-version-is-ambiguous
- diagnostics-cannot-identify-selected-backend
- secondary-has-no-proven-capacity
rollback:
action:
- restore-backend-resources-from-reviewed-iac
- restore-previous-pool-priorities-and-weights
- remove-only-the-new-circuit-rule
- replay-baseline-and-identity-negative-test
- confirm-client-visible-errors-return-to-baseline Ne modifiez pas simultanément seuil, durée, poids et retries. Sans variable isolée, le canari ne prouve rien et le rollback devient approximatif. La configuration doit vivre dans l’IaC avec un test de contrat et un propriétaire pour les seuils.
Conclusion
Un circuit breaker APIM ne transforme pas automatiquement deux endpoints Microsoft Foundry en service résilient. Il faut qualifier les erreurs transitoires, protéger le secondaire, borner les retries et prouver que la reprise conserve identité, modèle, sécurité et contrat de sortie.
Déployez lorsque le canari montre l’ouverture, la dérivation et la récupération sans amplification ni oscillation. Bloquez si le backend sélectionné ou l’identité restent ambigus. Si le secondaire sature ou si la sémantique change, restaurez la ressource backend et les poids précédents, puis rejouez exactement la même matrice avant de refermer le changement.