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.

14 sept. 2026 azureapi-managementapimai-gatewaymicrosoft-foundrycircuit-breakerload-balancingresilienceobservabilityautomationguardrailsrunbookrollbackproduction

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.

yaml ai-gateway-failover-contract.yml
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.

bash 01-capture-apim-backends.sh
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.

json backend-circuit-breaker-example.json
{
"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.

json ai-backend-canary.json
{
"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.

text circuit-breaker-test-matrix.txt
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.

kusto 02-apim-ai-backend-timeline.kql
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

yaml ai-gateway-circuit-breaker-decision.yml
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.