Frontend Labs
⋯

32. SLOs and burn-rate alerting

32.1 Define the SLOs

SLI Definition (good events / valid events) SLO (30d)
Orders availability non-5xx responses to POST /orders / all 99.9%
Orders latency responses < 250 ms / all 99%
Order fulfilment orders paid within 60 s of creation / orders created 99.5%

Error budget = 1 − SLO. At 99.9% over 30 days that's 43.2 minutes of full outage, or 0.1% of requests failing.

32.2 Multi-window, multi-burn-rate alerts (Google SRE Workbook)

Burn rate = how fast you're spending the budget relative to "exactly use it all in 30 days". A burn rate of 14.4 over 1 hour spends 2% of the monthly budget in that hour.

Severity Long window Short window Burn rate Budget consumed when it fires
page 1h 5m 14.4 2%
page 6h 30m 6 5%
ticket 1d 2h 3 10%
ticket 3d 6h 1 10%

The short window makes the alert reset quickly once the problem is fixed. The long window keeps it from firing on a 30-second blip.

apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata: { name: orbit-slo-orders, namespace: monitoring }
spec:
  groups:
    - name: slo.orders.availability.recording
      rules:
        - record: slo:orders_errors:ratio_rate5m
          expr: |
            sum(rate(http_server_request_duration_seconds_count{job="orbit/orders",http_response_status_code=~"5.."}[5m]))
            / sum(rate(http_server_request_duration_seconds_count{job="orbit/orders"}[5m]))
        # ... identical rules for 30m, 1h, 2h, 6h, 1d, 3d (generate them; see below)
    - name: slo.orders.availability.alerts
      rules:
        - alert: OrdersErrorBudgetBurnFast
          expr: |
            (slo:orders_errors:ratio_rate1h > (14.4 * 0.001) and slo:orders_errors:ratio_rate5m > (14.4 * 0.001))
            or
            (slo:orders_errors:ratio_rate6h > (6 * 0.001) and slo:orders_errors:ratio_rate30m > (6 * 0.001))
          labels: { severity: page, service: orders }
          annotations:
            summary: "orders is burning its 99.9% availability error budget fast"
            dashboard: https://grafana.acme.example/d/orbit-red?var-job=orbit/orders
            runbook_url: https://runbooks.acme.example/orbit/orders-availability
        - alert: OrdersErrorBudgetBurnSlow
          expr: |
            (slo:orders_errors:ratio_rate1d > (3 * 0.001) and slo:orders_errors:ratio_rate2h > (3 * 0.001))
            or
            (slo:orders_errors:ratio_rate3d > 0.001 and slo:orders_errors:ratio_rate6h > 0.001)
          labels: { severity: ticket, service: orders }
          annotations: { summary: "orders error budget burning steadily" }

Latency SLO uses the histogram bucket at the threshold (250 ms = the le="0.25" bucket boundary in the OTel default buckets):

1 - (
  sum(rate(http_server_request_duration_seconds_bucket{job="orbit/orders",le="0.25"}[1h]))
  / sum(rate(http_server_request_duration_seconds_count{job="orbit/orders"}[1h]))
)   # = fraction of SLOW requests: compare against (1 - 0.99) * burn rate

Best practice: don't hand-write these. Sloth or Pyrra take a 10-line SLO spec and generate all recording rules, alerts and a dashboard. OpenSLO is the vendor-neutral spec format.

# Sloth spec (generates ~30 rules)
version: prometheus/v1
service: orders
slos:
  - name: availability
    objective: 99.9
    sli:
      events:
        error_query: sum(rate(http_server_request_duration_seconds_count{job="orbit/orders",http_response_status_code=~"5.."}[{{.window}}]))
        total_query: sum(rate(http_server_request_duration_seconds_count{job="orbit/orders"}[{{.window}}]))
    alerting:
      page_alert: { labels: { severity: page } }
      ticket_alert: { labels: { severity: ticket } }

⚠️ Caveat (verify): the latency SLI assumes a histogram bucket boundary at exactly 0.25 s (the OTel default HTTP buckets). Confirm with count by (le) (http_server_request_duration_seconds_bucket). Check the Sloth spec against the Sloth version you run.

32.3 Error budget policy: the part that changes behavior

An SLO only matters if it drives decisions. Write it down and agree on it with product owners:

  • Budget remaining > 50%: ship freely; run chaos experiments in prod.
  • Budget < 25%: releases need extra review; prioritize reliability work.
  • Budget exhausted: feature freeze for the service except reliability fixes, until the 30-day window recovers.

Part IV checkpoint: run the k6 test, then break something (scale Postgres to 1 instance and kill it, or add a time.Sleep to orders). Go from the burn-rate alert → RED dashboard → an exemplar → the Tempo trace → that trace's logs in Loki, without typing a pod name.


Generated from real-life-k8s-guide.md · 8 Oct 2026 16:36