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.

07 sept. 2026 azureazure-functionsflex-consumptionserverlessscalingconcurrencyobservabilitykqlcapacityrunbookrollbackproduction

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.

yaml flex-scale-incident.yml
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.

bash 01-read-flex-scale-config.sh
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.

text scale-group-map.txt
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.

kusto 02-flex-scale-evidence.kql
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.

text capacity-boundary-checks.txt
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.

kusto 03-downstream-pressure.kql
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.

yaml flex-scale-canary-matrix.yml
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

yaml flex-scale-decision.yml
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.