Infrastructure
Azure Monitor : valider une alerte SLO à burn rate avant de pager l’astreinte
Un runbook de production pour construire et qualifier une alerte de burn rate dans Azure Monitor en séparant SLI, budget d’erreur, fenêtres courte et longue, qualité des logs, notification, validation et rollback.
Une alerte sur un taux d’erreur instantané répond mal à deux situations fréquentes. Elle peut réveiller l’astreinte pour un pic de deux minutes sans impact durable, ou rester silencieuse pendant une dégradation modérée qui consomme pourtant le budget d’erreur de la semaine en quelques heures. Une alerte SLO à burn rate vise une décision plus utile : le service brûle-t-il son budget assez vite pour exiger une action maintenant ?
Le cas d’usage est une API Azure exposée à des utilisateurs, instrumentée dans Application Insights et Log Analytics. Son SLO de disponibilité est défini sur les requêtes éligibles, et l’équipe veut router les dégradations rapides vers l’astreinte tout en laissant les consommations lentes dans une file de suivi. Le runbook sert à valider le calcul, les données, les fenêtres, le routage et le retour arrière avant d’activer une notification de production.
Écrire le contrat SLO avant la requête
Un burn rate n’est pas un seuil choisi dans un dashboard. C’est le rapport entre le taux d’erreur observé et le taux d’erreur autorisé par le SLO. Avec un objectif de 99,9 %, le budget d’erreur est de 0,1 %. Un burn rate de 1 consomme ce budget au rythme prévu ; un burn rate de 14,4 le consomme 14,4 fois plus vite.
Commencez par fixer ce qui entre réellement dans le calcul. Les health checks, requêtes synthétiques, erreurs client attendues et opérations de maintenance ne doivent pas être ajoutés ou retirés au fil de l’incident.
service: orders-api
environment: production
objective: 99.9
period: 30d
eligible_events:
table: AppRequests
filter:
- AppRoleName == orders-api
- SyntheticSource == empty
good_event:
- Success == true
- ResultCode < 500
excluded:
- route == /health
- planned_maintenance == true
owners:
service: team-orders
telemetry: team-platform
alert_routes:
fast_burn: on-call
slow_burn: service-backlog
rollback:
restore_rule_version: slo-orders-v1
keep_dashboard_query: true Le contrat doit aussi préciser la source du SLI. Mélanger une métrique de plateforme, des logs applicatifs et un test synthétique dans le même dénominateur produit un chiffre difficile à expliquer et presque impossible à rejouer.
Calculer le SLI et le burn rate sur une base stable
La requête suivante calcule le volume éligible, les erreurs et le burn rate. Les noms de colonnes doivent être adaptés au schéma réel ; la structure du calcul, elle, doit rester identique entre dashboard, alerte et replay.
let Objective = 0.999;
let AllowedErrorRate = 1.0 - Objective;
let EvaluationWindow = 1h;
AppRequests
| where TimeGenerated > ago(EvaluationWindow)
| where AppRoleName == "orders-api"
| where isempty(SyntheticSource)
| where Name != "GET /health"
| extend IsBad = iff(Success == false or toint(ResultCode) >= 500, 1, 0)
| summarize Eligible=count(), Bad=sum(IsBad)
| extend ErrorRate = todouble(Bad) / todouble(Eligible)
| extend BurnRate = ErrorRate / AllowedErrorRate
| project Eligible, Bad, ErrorRate, BurnRate Traitez explicitement le faible trafic. Une seule erreur sur deux requêtes donne un burn rate spectaculaire sans constituer la même preuve qu’un millier d’erreurs. Ajoutez un volume minimal ou une seconde condition sur le nombre d’événements mauvais. Le seuil doit être documenté avec le profil réel du service, pas repris d’un exemple générique.
Combiner une fenêtre courte et une fenêtre longue
Une alerte de burn rate exploitable demande généralement deux fenêtres. La fenêtre courte confirme que la dégradation est encore active. La fenêtre longue évite de pager sur un pic déjà terminé. Les deux conditions doivent être vraies pour la route urgente.
let Objective = 0.999;
let AllowedErrorRate = 1.0 - Objective;
let MinimumEligible = 100;
let BurnRate = (Window:timespan) {
AppRequests
| where TimeGenerated > ago(Window)
| where AppRoleName == "orders-api"
| where isempty(SyntheticSource)
| where Name != "GET /health"
| extend IsBad = iff(Success == false or toint(ResultCode) >= 500, 1, 0)
| summarize Eligible=count(), Bad=sum(IsBad)
| extend Rate=iff(Eligible >= MinimumEligible,
(todouble(Bad) / todouble(Eligible)) / AllowedErrorRate,
real(null))
| project Rate, Eligible, Bad
};
let Short = toscalar(BurnRate(5m) | project Rate);
let Long = toscalar(BurnRate(1h) | project Rate);
print ShortBurnRate=Short, LongBurnRate=Long
| extend ShouldPage = ShortBurnRate >= 14.4 and LongBurnRate >= 14.4 Les valeurs de fenêtre et de seuil sont un point de départ à valider contre le budget, le trafic et le délai d’intervention attendus. Pour une consommation plus lente, une seconde règle peut utiliser des fenêtres plus longues et créer un ticket sans appeler l’astreinte. Ce découpage garde une sémantique claire : urgence immédiate d’un côté, dérive à corriger de l’autre.
Vérifier la qualité de la télémétrie avant d’activer le paging
Une alerte SLO ne vaut pas mieux que son dénominateur. Avant l’activation, vérifiez la latence d’ingestion, les trous de collecte, le sampling et les changements de nom de route. Une baisse soudaine du trafic éligible peut faire disparaître une dégradation réelle ou amplifier quelques erreurs.
let Window = 24h;
AppRequests
| where TimeGenerated > ago(Window)
| where AppRoleName == "orders-api"
| summarize
Eligible=count(),
Failed=countif(Success == false or toint(ResultCode) >= 500),
DistinctOperations=dcount(Name),
P95IngestionDelay=percentile(ingestion_time() - TimeGenerated, 95)
by bin(TimeGenerated, 15m)
| order by TimeGenerated asc Comparez cette série à un signal indépendant : volume reçu par la passerelle, métrique de requêtes de l’App Service, test synthétique ou compteur métier. Il ne s’agit pas de fusionner tous ces signaux dans le SLI, mais de détecter quand sa source devient incomplète.
Rejouer des incidents connus avant le premier pager
Une règle ne doit pas passer directement du notebook à l’astreinte. Rejouez-la sur au moins trois fenêtres : un incident qui aurait dû pager, un pic court qui ne devait pas pager et une période saine avec le trafic habituel. Pour chaque fenêtre, conservez les volumes, les burn rates et la décision attendue.
Case A - sustained production failure
Expected: short and long windows breach
Route: on-call
Evidence: user impact and eligible volume are sufficient
Case B - two-minute deployment spike
Expected: short window may breach, long window stays below threshold
Route: no page, deployment event retained
Case C - slow weekly budget consumption
Expected: fast-burn rule stays quiet, slow-burn rule opens follow-up
Route: service backlog
Case D - telemetry gap
Expected: SLO page is blocked or marked unreliable
Route: observability incident, not application rollback Le replay doit utiliser exactement les mêmes filtres que la règle déployée. Une copie simplifiée dans un workbook peut rassurer à tort si la Scheduled Query Rule applique un autre périmètre ou une autre agrégation.
Déployer la règle sans perdre le contrôle
Déployez d’abord la règle désactivée ou reliée à un action group non urgent. Capturez sa configuration, son identité, son scope, sa fréquence et ses actions avant de modifier le routage.
RG="rg-observability-prod"
RULE="slo-orders-fast-burn"
az monitor scheduled-query show --resource-group "$RG" --name "$RULE" --query "{enabled:enabled,scopes:scopes,evaluationFrequency:evaluationFrequency,windowSize:windowSize,severity:severity,criteria:criteria,actions:actions}" --output json
# Validation gate before paging:
# - query replayed on known windows
# - minimum traffic condition present
# - ingestion latency below the shortest window
# - on-call action group tested independently
# - previous rule definition retained for rollback Activez ensuite la notification par étapes. Une période de shadow mode permet de comparer les décisions de la nouvelle règle aux incidents et aux alertes actuelles. Le passage au paging doit être une décision datée, avec un propriétaire et une première fenêtre de revue.
Décider validation, correction ou rollback
La clôture ne consiste pas à constater que la règle est verte. Elle doit déboucher sur une décision explicite.
validate:
when:
- known_incident_pages
- transient_spike_does_not_page
- eligible_volume_is_stable
- action_group_delivery_is_proven
action:
- enable_on_call_route
- review_after_first_budget_event
correct:
when:
- filters_or_denominator_are_wrong
- low_traffic_creates_false_burn
- ingestion_delay_exceeds_short_window
action:
- keep_shadow_mode
- fix_sli_or_collection
- replay_validation_matrix
rollback:
when:
- paging_is_noisy_or_unexplainable
- rule_misses_known_sustained_failure
- telemetry_change_breaks_denominator
action:
- detach_on_call_action_group
- restore_previous_rule_version
- keep_dashboard_query_for_evidence
- open_follow_up_with_failed_case Le rollback prioritaire consiste à retirer la route d’astreinte, pas à supprimer la requête et ses preuves. L’équipe conserve ainsi le signal en observation, corrige le calcul puis rejoue les mêmes cas avant une nouvelle activation.
Conclusion
Une alerte SLO à burn rate n’est utile que si elle transforme un budget d’erreur en décision d’exploitation. Le calcul doit partir d’un SLI stable, appliquer des fenêtres cohérentes avec le trafic, vérifier la qualité des données et distinguer consommation rapide et dérive lente.
Le runbook se termine par une preuve : l’incident soutenu page, le pic transitoire ne page pas, le faible trafic est borné et l’action group livre réellement la notification. Si l’une de ces conditions échoue, la règle reste en shadow mode ou revient à sa version précédente. Le pager n’est activé qu’une fois le comportement explicable et rejouable.