Infrastructure
Azure Functions Flex Consumption : diagnostiquer un scale-out bloqué avant d'augmenter le plafond
Un runbook de production pour séparer demande, concurrence, groupes de scale, plafond d'instances, quota régional et saturation aval avant de modifier la capacité d'une Function App.
Une Function App sur le plan Flex Consumption absorbe correctement la charge habituelle. Lors d’un pic, la file d’attente monte, le p95 se dégrade et le nombre d’instances semble plafonner. La correction paraît évidente : augmenter maximumInstanceCount. Cette action peut ne rien résoudre si la concurrence par instance, le groupe de scale, le quota mémoire régional ou une dépendance aval constitue la vraie limite.
Le but de ce runbook est de décider avec des preuves : relever le plafond, ajuster la concurrence, préallouer des instances, corriger une dépendance, ou revenir à la configuration précédente. Il s’applique aux fonctions HTTP et événementielles, en tenant compte du scale par fonction propre à Flex Consumption.
Figer le symptôme et le budget de capacité
Ne commencez pas par la valeur du plafond. Décrivez la demande que le système doit absorber, la fenêtre touchée et la limite que vous cherchez à protéger.
app: func-orders-prod
region: westeurope
plan: flex-consumption
incident_window: 2026-09-07T06:40:00Z/2026-09-07T07:10:00Z
symptom:
trigger: service-bus-orders
backlog: rising
p95_duration: above-service-budget
failures: intermittent-timeouts
capacity_contract:
maximum_instance_count: 100
instance_memory_mb: 2048
per_instance_concurrency: current-iac-value
always_ready: current-iac-value
protected_downstream: orders-database
decision:
promote_only_if: backlog-drains-without-downstream-regression
rollback_on: higher-error-rate-or-dependency-saturation Conservez le commit applicatif, le commit IaC, l’heure du dernier changement de configuration et une référence de trafic normal. Sans cette ligne de base, une montée à 40 instances peut sembler insuffisante alors qu’elle est simplement plus rapide que la veille et limitée par la base de données.
Lire la configuration réellement déployée
Flex Consumption expose le plafond, la taille mémoire, la concurrence HTTP et les instances toujours prêtes dans functionAppConfig.scaleAndConcurrency. Lisez l’état Azure et comparez-le à l’IaC ; ne déduisez pas la configuration depuis le portail ou une variable de pipeline.
RG="rg-orders-prod"
APP="func-orders-prod"
az resource show \
--resource-group "$RG" \
--name "$APP" \
--resource-type "Microsoft.Web/sites" \
--api-version "2023-12-01" \
--query "properties.functionAppConfig.scaleAndConcurrency" \
--output json > flex-scale-current.json
jq '{maximumInstanceCount, instanceMemoryMB, triggers, alwaysReady}' \
flex-scale-current.json
# Comparer ensuite avec la ressource Bicep, ARM ou Terraform approuvee.
git diff --no-index expected-scale.json flex-scale-current.json || true Vérifiez aussi la région, le runtime, la version, le type de trigger et le compte de stockage utilisé par l’hôte. Une dérive de maximumInstanceCount est un cas possible, pas une explication générale du retard.
Identifier le groupe de scale qui porte la demande
Flex Consumption ne traite pas nécessairement l’application comme un seul pool. Les fonctions HTTP partagent un groupe, les blobs Event Grid un autre, les fonctions Durable un autre, tandis que d’autres triggers peuvent évoluer indépendamment. Le plafond d’instances s’applique aux instances à la demande de chaque groupe, pas à une somme simple visible au niveau de l’application.
Pour chaque fonction touchee
Nom et type de trigger
Groupe attendu: http, blob, durable ou function:<nom>
Signal de demande: requetes, backlog, partitions ou orchestrations
Concurrence effective par instance
Instances distinctes pendant le pic
Temps moyen et p95 par execution
Dependances appelees par execution
Ne pas conclure a un plafond global depuis
le nombre total d'instances de l'application
une seule metrique agregee
le maximum configure sans le quota regional
un backlog qui ne prouve pas que le trigger est sain Cette cartographie évite deux erreurs : relever un plafond pour le mauvais groupe et additionner des instances qui n’exécutent pas la fonction en difficulté.
Prouver demande, concurrence et instances avec les traces
Application Insights permet de relier le volume traité, les erreurs, la durée et les instances observées. Adaptez la requête au modèle de télémétrie et au nom de rôle réellement émis par l’application.
let Start = datetime(2026-09-07T06:30:00Z);
let End = datetime(2026-09-07T07:20:00Z);
requests
| where timestamp between (Start .. End)
| where cloud_RoleName == "func-orders-prod"
| summarize executions=count(),
failed=countif(success == false),
p50_ms=percentile(duration, 50),
p95_ms=percentile(duration, 95),
active_instances=dcount(cloud_RoleInstance),
sample_instances=make_set(cloud_RoleInstance, 8)
by bin(timestamp, 1m), operation_Name
| extend failure_rate=round(100.0 * todouble(failed) / executions, 2)
| order by timestamp asc Interprétez la courbe, pas un point isolé. Une demande qui monte avec des instances et un p95 stables indique un scale-out utile. Des instances stables avec une demande croissante orientent vers concurrence, plafond, quota ou signal de trigger. Des instances qui montent tandis que le p95 et les erreurs se dégradent orientent d’abord vers le code, l’initialisation ou une dépendance.
Séparer plafond, rythme de scale et quota régional
Le nombre maximal configuré est un plafond, pas une réservation. La plateforme ajoute les instances selon sa courbe de scale ; elle peut temporairement limiter les demandes de scale-out et retenter. Le quota mémoire régional de l’abonnement peut arrêter la progression avant la valeur configurée, surtout lorsque plusieurs applications Flex consomment la même capacité régionale.
Plafond de l'application
Valeur Azure egale a l'IaC
Valeur suffisante pour le debit cible a concurrence actuelle
Aucun changement recent non qualifie
Quota regional
Memoire totale disponible dans la region
Autres Function Apps Flex actives pendant la fenetre
Marge pour les groupes qui evoluent independamment
Rythme de scale
Pic brutal ou demande progressive
Instances always ready presentes pour la charge previsible
Duree de demarrage et initialisation des dependances
Throttling transitoire distingue d'un plafond durable Un pic court qui se termine avant l’arrivée des instances appelle souvent une stratégie alwaysReady ciblée ou un lissage de la demande. Un plateau durable exactement aligné sur le maximum justifie d’étudier le plafond. Un plateau inférieur impose d’abord de vérifier quota, groupe et santé du trigger.
Vérifier la concurrence et la saturation aval
La concurrence change le nombre d’instances nécessaires. Trop haute, elle peut saturer CPU, mémoire, connexions ou thread pool sur chaque instance. Trop basse, elle demande davantage d’instances pour le même débit. Pour les fonctions HTTP, la concurrence par instance se configure dans Flex Consumption ; pour les autres triggers, la source de contrôle dépend de l’extension et du modèle de concurrence.
dependencies
| where timestamp between (datetime(2026-09-07T06:30:00Z) .. datetime(2026-09-07T07:20:00Z))
| where cloud_RoleName == "func-orders-prod"
| summarize calls=count(),
failed=countif(success == false),
p95_ms=percentile(duration, 95),
result_codes=make_set(resultCode, 8)
by bin(timestamp, 1m), target, type
| extend failure_rate=round(100.0 * todouble(failed) / calls, 2)
| order by timestamp asc Si la base, l’API ou le broker ralentit à mesure que les instances arrivent, augmenter le plafond accélère l’incident. Bornez alors la concurrence, protégez la dépendance et mesurez le débit réellement terminé, pas seulement le nombre d’exécutions démarrées.
Tester un seul levier sur un canari
Ne changez pas simultanément plafond, mémoire, concurrence et instances toujours prêtes. Créez une Function App canari équivalente, rejouez une charge représentative et modifiez un seul levier depuis l’IaC.
control:
code: same-artifact
maximum_instance_count: current
concurrency: current
always_ready: current
candidate_a:
change: maximum-instance-count-only
prove:
- active-instances-rise-beyond-old-ceiling
- backlog-drains-faster
- dependency-p95-remains-within-budget
candidate_b:
change: per-instance-concurrency-only
prove:
- throughput-improves-per-instance
- cpu-memory-and-errors-remain-stable
candidate_c:
change: always-ready-only
prove:
- predictable-burst-avoids-cold-start
- idle-cost-is-accepted Utilisez les mêmes messages, partitions, tailles de payload et limites aval que le chemin de production. Un test HTTP synthétique ne valide pas le comportement d’un trigger Service Bus.
Décider, valider ou rollbacker
raise_instance_ceiling:
when:
- active_group_reaches_current_ceiling
- regional_memory_quota_has_headroom
- downstream_accepts_added_parallelism
- canary_drains_backlog_without_regression
tune_concurrency_or_always_ready:
when:
- per_instance_pressure_or_cold_start_explains_delay
- one_parameter_canary_meets_capacity_budget
hold_and_fix_dependency:
when:
- more_instances_increase_dependency_latency_or_errors
- trigger_health_or_scale_group_is_not_proven
rollback:
action:
- restore_previous_iac_values
- redeploy_configuration_only
- replay_the_same_load_window
- confirm_backlog_errors_and_dependency_pressure_return_to_baseline Après promotion, surveillez au moins un cycle de charge comparable. Le rollback restaure le jeu de valeurs précédent, pas une valeur improvisée pendant l’incident. Si des messages ont été retardés ou retraités, réconciliez aussi les effets métier avant de clore.
Conclusion
Un scale-out Flex Consumption apparemment bloqué n’est pas automatiquement un plafond trop bas. La preuve doit relier le groupe concerné, sa demande, la concurrence, les instances actives, le quota régional et la capacité aval.
Relevez maximumInstanceCount uniquement lorsque le groupe atteint réellement cette limite et que le canari prouve un meilleur débit sans déplacer la saturation. Sinon, ajustez le levier qui correspond au symptôme, conservez une seule variable par essai et gardez la configuration précédente prête à être restaurée.