SLO & SLA

SLO & SLA #

Monitoring memperlihatkan bahwa service lambat. Alert memberitahu bahwa latensi naik. Tapi pertanyaan yang paling penting bagi bisnis adalah: seberapa banyak insiden yang bisa kita toleransi sebelum melanggar komitmen ke pelanggan? SLO (Service Level Objective) dan SLA (Service Level Agreement) memberikan jawaban yang terukur dan terstruktur. SLO adalah target internal yang dijaga oleh tim engineering; SLA adalah janji eksternal ke pelanggan dengan konsekuensi hukum/commercial jika dilanggar. Ansible bisa mengotomasi seluruh infrastruktur pengukurannya — dari recording rules di Prometheus hingga dashboard SLO di Grafana — sehingga kepatuhan terhadap target selalu terukur secara real-time dan laporan bulanan ke pelanggan bisa di-generate tanpa pekerjaan manual.

Konsep Dasar: SLI, SLO, Error Budget, dan SLA #

Empat konsep ini sering tertukar, padahal masing-masing punya peran yang berbeda dalam hierarki:

flowchart TD
    SLI["SLI<br/>Service Level Indicator<br/>metrik aktual yang diukur"] --> SLO["SLO<br/>Service Level Objective<br/>target yang ingin dicapai"]
    SLO --> EB["Error Budget<br/>100% - SLO target<br/>margin kegagalan yang ditoleransi"]
    EB --> BR["Burn Rate<br/>seberapa cepat budget terbakar"]
    EB --> SLA["SLA<br/>Service Level Agreement<br/>kontrak formal ke pelanggan"]

    SLI -. "contoh" .-> SLIex["99.5% request success rate"]
    SLO -. "contoh" .-> SLOex["target 99.9%<br/>selama 30 hari"]
    EB -. "contoh" .-> EBex["0.1% x 30 hari<br/>= ~43 menit downtime"]
    BR -. "contoh" .-> BRex["1x = normal<br/>14x = budget habis dalam ~2 hari"]
    SLA -. "contoh" .-> SLAex["99.5% uptime<br/>refund 10% biaya bulanan<br/>jika dilanggar"]

Definisi formal yang perlu kita pegang:

  • SLI (Service Level Indicator) — metrik aktual yang diukur. Contoh: persentase request dengan response code non-5xx dalam window 5 menit, atau latensi P99 dalam 1 jam terakhir. SLI selalu terukur dan bisa di-query dari Prometheus.
  • SLO (Service Level Objective) — target yang ingin dicapai untuk SLI tersebut. Contoh: SLI success rate harus ≥ 99.9% dalam window 30 hari. SLO adalah kontrak internal antara tim engineering dengan product/business.
  • Error Budget — margin kegagalan yang diizinkan. Jika SLO 99.9%, error budget = 0.1%. Dalam 30 hari, ini berarti 0.1% × 30 × 24 × 60 = 43.2 menit downtime. Tim boleh gagal sampai sebanyak itu; setelah lewat, harus ada tindakan (postmortem, moratorium fitur, refactor).
  • SLA (Service Level Agreement) — kontrak formal ke pelanggan dengan konsekuensi komersial. Biasanya SLO lebih ketat dari SLA: jika SLA 99.5% dan SLO 99.9%, tim punya margin 0.4% untuk insiden yang tidak melanggar SLA.
Jangan pernah menetapkan SLO sama dengan atau lebih ketat dari SLA. SLA adalah janji yang jika dilanggar berkonsekuensi kompensasi; SLO adalah target internal. Jika SLO = SLA, kita tidak memiliki buffer saat ada insiden — pelanggan akan mengajukan keluhan setiap kali target internal tidak tercapai, yang seharusnya masih dalam margin yang ditoleransi.

Memilih SLI yang Tepat #

SLI yang dipilih harus merepresentasikan pengalaman pengguna, bukan metrik internal yang mudah dicapai. Tiga kategori SLI yang paling umum:

Kategori SLI Metrik Cocok untuk Kapan TIDAK cocok
Availability Persentase request yang berhasil (non-5xx) dalam window API HTTP, web service, database yang menerima query Service yang secara desain return error untuk input invalid (4xx bukan masalah)
Latency Persentase request dengan response time di bawah threshold User-facing service, real-time API, payment processing Background job, batch processing (latensi tidak diperhatikan user)
Throughput Request per detik yang berhasil diproses Service yang harus handle high traffic, data pipeline Service dengan traffic rendah dan tidak dipredict
Freshness Umur data yang tersedia saat di-query Data pipeline, analytics, dashboard yang merefresh data Service yang selalu return data real-time (freshness selalu 0)
Correctness Persentase output yang benar dibanding ground truth ML inference, calculation engine, ETL Service sederhana yang tidak ada kemungkinan “incorrect” output

Pemilihan SLI adalah keputusan produk, bukan teknis. Tanyakan ke product manager: “metrik apa yang paling mewakili user senang dengan service ini?” Jawaban yang benar biasanya availability atau latency — bukan CPU usage.


Recording Rules untuk SLI yang Efisien #

Kalkulasi SLI yang kompleks bisa sangat mahal jika dikalkulasi setiap kali di-query dari dashboard atau alert. Recording rules menghitung dan menyimpan hasilnya secara berkala — hasilnya adalah time series baru yang bisa di-query dengan murah:

# roles/prometheus/tasks/slo-rules.yml
---
- name: Deploy recording rules untuk SLO
  template:
    src: rules/slo-recording.yml.j2
    dest: /etc/prometheus/rules/slo-recording.yml
    owner: prometheus
    mode: '0644'
    validate: promtool check rules %s
  notify: Reload Prometheus
{# templates/rules/slo-recording.yml.j2 #}
groups:
  # Grup 1: SLI metrics (dasar pengukuran)
  - name: slo_recording_rules
    interval: 30s
    rules:
      # SLI: Availability — persentase request yang berhasil (non-5xx)
      - record: job:http_requests_total:success_rate5m
        expr: >
          sum(rate(http_requests_total{status!~"5.."}[5m])) by (job, service)
          /
          sum(rate(http_requests_total[5m])) by (job, service)

      # SLI: Latency P99 dari histogram
      - record: job:http_request_duration_seconds:p99_5m
        expr: >
          histogram_quantile(0.99,
            sum(rate(http_request_duration_seconds_bucket[5m])) by (job, service, le)
          )

      # SLI: Latency P95
      - record: job:http_request_duration_seconds:p95_5m
        expr: >
          histogram_quantile(0.95,
            sum(rate(http_request_duration_seconds_bucket[5m])) by (job, service, le)
          )

  # Grup 2: Error budget metrics (turunan dari SLI)
  - name: error_budget_rules
    interval: 30s
    rules:
      # Error budget burn rate (multi-window, multi-burn-rate)
      # Page alert jika burn rate 14x lebih cepat dari normal — akan habis dalam ~2 hari
      - record: job:error_budget_burn_rate:1h
        expr: >
          (1 - job:http_requests_total:success_rate5m)
          /
          (1 - {{ slo_availability_target | default(0.999) }})

      # Sisa error budget dalam 30 hari terakhir
      - record: job:error_budget_remaining:30d
        expr: >
          1 - (
            (1 - avg_over_time(job:http_requests_total:success_rate5m[30d]))
            /
            (1 - {{ slo_availability_target | default(0.999) }})
          )

      # Prediksi kapan error budget akan habis jika burn rate saat ini bertahan
      - record: job:error_budget_exhaustion_days:current
        expr: >
          (1 - {{ slo_availability_target | default(0.999) }})
          /
          (1 - job:http_requests_total:success_rate5m) * 30

Tiga group yang perlu kita pahami: (1) SLI metrics adalah pengukuran mentah — selalu kalkulasi ulang dari data asli. (2) Error budget metrics adalah turunan — kalkulasi dari SLI metrics. (3) Prediksi membantu tim merencanakan — “jika kondisi sekarang bertahan, budget habis dalam X hari”.

Selalu simpan SLI di level job/service, bukan instance. SLO adalah tentang service secara keseluruhan — instance tunggal yang gagal bukan pelanggaran SLO jika service lain masih sehat dan load balancer melakukan failover dengan benar.

Alert Berbasis Error Budget (Multi-Window, Multi-Burn-Rate) #

Alert tradisional (CPU > 80%, latensi > 200ms) sering memunculkan false alarm. Alert berbasis error budget jauh lebih presisi — hanya alert saat error budget benar-benar terbakar dengan cepat. Pola multi-window, multi-burn-rate dari Google SRE workbook adalah standar industri:

{# templates/rules/slo-alerts.yml.j2 #}
groups:
  - name: slo_alerts
    rules:
      # === PAGE ALERT (critical, butuh respons dalam 15 menit) ===
      # Dua window pendek — burn rate tinggi dalam window cepat
      - alert: SLO_HighBurnRate_Fast
        expr: >
          job:error_budget_burn_rate:1h > 14.4
          and
          job:error_budget_burn_rate:5m > 14.4
        for: 2m
        labels:
          severity: critical
          slo: availability
          team: platform
        annotations:
          summary: "SLO burn rate sangat tinggi: {{ '{{' }} $labels.service {{ '}}' }}"
          description: >
            Burn rate {{ '{{' }} $value | humanize {{ '}}' }}x dari normal.
            Error budget akan habis dalam ~2 hari jika burn rate ini bertahan.
            RESPON DALAM 15 MENIT.
          runbook_url: "https://wiki.company.com/runbooks/slo-burn-fast"
          dashboard_url: "https://grafana.company.com/d/slo-burn?var-service={{ '{{' }} $labels.service {{ '}}' }}"

      # === TICKET ALERT (warning, respon dalam working hours) ===
      # Window sedang — burn rate tinggi tapi tidak urgent
      - alert: SLO_MediumBurnRate
        expr: >
          job:error_budget_burn_rate:6h > 6
          and
          job:error_budget_burn_rate:30m > 6
        for: 15m
        labels:
          severity: warning
          slo: availability
        annotations:
          summary: "SLO burn rate tinggi: {{ '{{' }} $labels.service {{ '}}' }}"
          description: >
            Burn rate {{ '{{' }} $value | humanize {{ '}}' }}x dari normal dalam 6 jam terakhir.
            Error budget akan habis dalam ~5 hari. PERLU DISELIDIKI HARI INI.
          runbook_url: "https://wiki.company.com/runbooks/slo-burn-medium"

      # === SLOW BURN (warning, respon dalam minggu) ===
      # Window panjang — burn rate rendah tapi berkelanjutan
      - alert: SLO_SlowBurnRate
        expr: >
          job:error_budget_burn_rate:3d > 1
          and
          job:error_budget_burn_rate:6h > 1
        for: 1h
        labels:
          severity: warning
          slo: availability
        annotations:
          summary: "SLO slow burn terdeteksi: {{ '{{' }} $labels.service {{ '}}' }}"
          description: >
            Burn rate {{ '{{' }} $value | humanize {{ '}}' }}x dari normal selama 3 hari.
            Error budget akan habis dalam ~30 hari. RENCANAKAN PERBAIKAN.
          runbook_url: "https://wiki.company.com/runbooks/slo-burn-slow"

Logika di balik multi-window: jika burn rate tinggi konsisten di window pendek DAN window panjang, alarm valid. Ini menghindari false positive dari satu window pendek (misalnya traffic spike 5 menit) atau window panjang (misalnya metric scrape error sesaat).

Tabel di bawah menjelaskan threshold burn rate yang umum digunakan:

Burn Rate Window Alert Level Tindakan Implikasi
14.4x 1h + 5m Page (critical) Drop everything, investigasi sekarang Budget habis dalam ~2 hari
6x 6h + 30m Ticket (warning) Investigasi hari ini, fix minggu ini Budget habis dalam ~5 hari
3x 24h + 2h Ticket (warning) Buat tiket, rencanakan fix Budget habis dalam ~10 hari
1x 3d + 6h Slow burn (warning) Tambah ke backlog Budget habis tepat di akhir periode (30 hari)

Decision Tree: Pilih SLO Target yang Realistis #

Pemilihan target SLO bukan angka arbitrer. Trade-off utama: target lebih tinggi = lebih reliable untuk user, tapi lebih mahal (butuh redundancy, faster rollback, more SRE). Decision tree berikut membantu menentukan target yang sesuai:

flowchart TD
    A["Baru mulai<br/>definisikan SLO"] --> B{"Ada data<br/>historical<br/>reliability?"}
    B -- "Ya" --> C{"Lihat percentile<br/>terburuk 30 hari<br/>terakhir"}
    B -- "Tidak" --> D["Mulai ukur SLI<br/>selama 30 hari<br/>sebelum set SLO"]

    C --> E{"P99 latency<br/>saat ini<br/>di mana?"}
    E -- "50ms" --> F["Target SLO:<br/>99.9%<br/>latency 100ms"]
    E -- "200ms" --> G["Target SLO:<br/>99.5%<br/>latency 500ms"]
    E -- "2 detik" --> H["Target SLO:<br/>99%<br/>latency 5 detik"]

    D --> I["Setelah 30 hari<br/>data terkumpul"]
    I --> C

    F --> J{"Apakah ada<br/>SLA ke<br/>pelangan?"}
    G --> J
    H --> J

    J -- "Ya" --> K["SLA = SLO - 0.3%<br/>ada buffer insiden"]
    J -- "Tidak" --> L["SLO adalah<br/>target internal<br/>saja"]

Empat pertanyaan yang harus dijawab sebelum set SLO:

  1. Apa reliability saat ini? — Jika service sudah jalan di 99.5% dalam 30 hari terakhir, set SLO 99.5% (realistis) bukan 99.99% (mustahil tanpa improvement).
  2. Apa impact of failure? — Payment service yang down = kehilangan revenue langsung. Blog yang down = inconvenience. Target SLO harus proportional.
  3. Berapa biaya untuk naik 0.1%? — Dari 99.9% ke 99.95% mungkin butuh multi-region active-active, redundancy database, dan tim on-call 24/7. Apakah bisnis mau bayar?
  4. Apa SLA yang sudah dijanjikan ke pelanggan? — SLO internal harus lebih ketat dari SLA eksternal untuk punya buffer.

Dashboard SLO di Grafana #

Dashboard SLO yang baik menampilkan semua yang dibutuhkan untuk trust bahwa service memenuhi target. Rekomendasi struktur panel:

Panel Tipe Query Tujuan
Current Availability Stat (gauge) avg(job:http_requests_total:success_rate5m) Angka real-time “sekarang service ini sehat”
Error Budget Remaining Stat (gauge) job:error_budget_remaining:30d Sisa budget dalam periode 30 hari — angka paling penting untuk prioritas
30-Day Availability Trend Timeseries avg_over_time(job:http_requests_total:success_rate5m[30d]) Trend availability dengan garis target SLO
Latency P50/P95/P99 Timeseries job:http_request_duration_seconds:p99_5m Distribusi latensi dengan garis threshold
Burn Rate (multi-window) Timeseries job:error_budget_burn_rate:1h, :6h, :24h Seberapa cepat budget terbakar di berbagai window
SLO Status per Service Table topk(20, job:error_budget_remaining:30d) Tabel semua service yang di-track, sort by error budget

Untuk deploy dashboard SLO via Ansible:

- name: Deploy SLO dashboard ke Grafana
  copy:
    src: files/dashboards/slo-overview.json
    dest: /var/lib/grafana/dashboards/slo-overview.json
    owner: grafana
    mode: '0640'
  notify: Reload Grafana dashboards

Pattern penting: error budget remaining harus menjadi panel paling prominen — bukan success rate, bukan latency. Error budget adalah angka yang langsung menjawab “apakah kita aman bulan ini?”.


Sequence Diagram: Alur SLO dari Pengukuran ke Keputusan #

Untuk memahami bagaimana data SLO mengalir dari metrik mentah ke keputusan bisnis, perhatikan sequence di bawah:

sequenceDiagram
    participant App as Aplikasi
    participant Prom as Prometheus
    participant RR as Recording Rules
    participant AR as Alert Rules
    participant AM as Alertmanager
    participant Dash as Grafana SLO Dashboard
    participant PM as Product Manager

    App->>Prom: "Expose /metrics (request count, duration, status)"
    Prom->>Prom: "Scrape setiap 15s"
    Prom->>RR: "Evaluate recording rules (setiap 30s)"
    RR->>Prom: "Simpan SLI & error budget series"
    Prom->>AR: "Evaluate alert rules (setiap 15s)"
    AR->>AR: "Hitung burn rate multi-window"
    AR->>AM: "Kirim alert jika burn rate > threshold"
    AM->>AM: "Route alert by severity"
    AM-->>PM: "PagerDuty / Slack notification"

    PM->>Dash: "Buka SLO dashboard (mingguan review)"
    Dash->>Prom: "Query SLI series"
    Prom-->>Dash: "Data 30 hari terakhir"
    Dash-->>PM: "Visualisasi error budget, burn rate, trend"

    PM->>PM: "Keputusan:"
    Note over PM: "Budget > 50%? Aman, lanjut roadmap\nBudget 20-50%? Waspada, defer fitur non-kritis\nBudget < 20%? Stop fitur, fokus reliability"
    PM->>AR: "Update threshold SLO jika perlu"
    PM->>App: "Trigger refactor jika slow burn"

Pola yang penting dari sequence ini: SLO yang terukur dengan baik memungkinkan product manager membuat keputusan berdasarkan data, bukan intuisi. Tanpa angka error budget, meeting mingguan biasanya jadi perdebatan “service-nya sehat atau tidak” yang tidak ada ujungnya.


Laporan SLA Otomatis #

Untuk SLA yang dijanjikan ke pelanggan, laporan bulanan wajib dikirim tepat waktu. Ansible + Prometheus API bisa mengotomasi pengumpulan data dan generate laporan PDF/HTML:

# playbooks/generate-sla-report.yml
---
- name: Generate laporan SLA bulanan
  hosts: localhost
  vars:
    report_month: "{{ lookup('pipe', 'date +%Y-%m') }}"
    prometheus_url: "https://prometheus.company.com"
    slo_availability_target: 0.999
    slo_latency_target_ms: 200

  tasks:
    - name: Ambil data availability bulan ini
      uri:
        url: "{{ prometheus_url }}/api/v1/query_range"
        method: GET
        body_format: form-urlencoded
        body:
          query: 'avg_over_time(job:http_requests_total:success_rate5m{service="myapp"}[30d])'
          start: "{{ lookup('pipe', 'date -d\"first day of this month\" +%s') }}"
          end: "{{ lookup('pipe', 'date +%s') }}"
          step: "3600"
        headers:
          Authorization: "Bearer {{ vault_prometheus_token }}"
        return_content: true
      register: availability_data
      no_log: true

    - name: Ambil data latency P99 bulan ini
      uri:
        url: "{{ prometheus_url }}/api/v1/query_range"
        method: GET
        body_format: form-urlencoded
        body:
          query: 'avg_over_time(job:http_request_duration_seconds:p99_5m{service="myapp"}[30d])'
          start: "{{ lookup('pipe', 'date -d\"first day of this month\" +%s') }}"
          end: "{{ lookup('pipe', 'date +%s') }}"
          step: "3600"
        headers:
          Authorization: "Bearer {{ vault_prometheus_token }}"
        return_content: true
      register: latency_data
      no_log: true

    - name: Kalkulasi availability rate rata-rata
      set_fact:
        avg_availability: >-
          {{ (availability_data.json.data.result[0].values
              | map(attribute=1) | map('float') | sum
              / availability_data.json.data.result[0].values | length * 100) | round(4) }}          

    - name: Kalkulasi latency P99 rata-rata
      set_fact:
        avg_latency_ms: >-
          {{ (latency_data.json.data.result[0].values
              | map(attribute=1) | map('float') | sum
              / latency_data.json.data.result[0].values | length * 1000) | round(2) }}          

    - name: Tentukan status SLA
      set_fact:
        sla_status: >-
          {{ 'COMPLIANT' if (avg_availability | float >= 99.9 and avg_latency_ms | float < slo_latency_target_ms | float)
             else 'BREACH' }}          

    - name: Generate file laporan Markdown
      template:
        src: sla-report.md.j2
        dest: "/var/reports/sla-{{ report_month }}-{{ inventory_hostname }}.md"

    - name: Generate PDF dari Markdown
      command: >
        pandoc /var/reports/sla-{{ report_month }}-{{ inventory_hostname }}.md
        -o /var/reports/sla-{{ report_month }}-{{ inventory_hostname }}.pdf
        --pdf-engine=xelatex
        -V geometry:margin=1in
        --toc        
      when: sla_install_pandoc | default(false)

    - name: Kirim laporan ke email tim
      community.general.mail:
        host: "{{ smtp_host }}"
        port: "{{ smtp_port }}"
        to: "{{ sla_report_recipients }}"
        subject: "Laporan SLA {{ report_month }} — {{ sla_status }}"
        body: "{{ lookup('file', '/var/reports/sla-' + report_month + '-' + inventory_hostname + '.md') }}"
        attach:
          - "/var/reports/sla-{{ report_month }}-{{ inventory_hostname }}.md"
      when: sla_send_email | default(false)

Template laporan SLA yang informatif:

{# sla-report.md.j2 #}
# Laporan SLA — {{ report_month }}

**Periode:** 1 {{ report_month }} — {{ lookup('pipe', 'date +%d %B %Y') }}
**Status:** `{{ sla_status }}`

## Ringkasan

| Metrik | Target SLA | Aktual | Status |
|---|---|---|---|
| Availability | ≥ 99.9% | {{ avg_availability }}% | {{ '✓' if avg_availability | float >= 99.9 else '✗' }} |
| Latency P99 | ≤ {{ slo_latency_target_ms }}ms | {{ avg_latency_ms }}ms | {{ '✓' if avg_latency_ms | float < slo_latency_target_ms else '✗' }} |

## Insiden Signifikan

{% for incident in monthly_incidents | default([]) %}
- **{{ incident.date }}** — {{ incident.summary }} (durasi: {{ incident.duration }})
{% endfor %}
_(Tidak ada insiden yang melebihi 5 menit pada periode ini)_

## Tren

Lampirkan grafik availability 30 hari dan grafik latency P99 30 hari
dari dashboard SLO untuk konteks visual.

## Komitmen

Kami {{ 'MEMENUHI' if sla_status == 'COMPLIANT' else 'TIDAK MEMENUHI' }}
komitmen SLA untuk periode {{ report_month }}.
{{ 'Tidak ada kompensasi yang dijadwalkan.' if sla_status == 'COMPLIANT' else 'Tim kami akan mengirim detail kompensasi yang berlaku sesuai kontrak.' }}
Generate laporan SLA via cron job di akhir bulan, bukan manual. Tambahkan 0 9 1 * * di crontab untuk menjalankan generate-sla-report.yml di tanggal 1 setiap bulan jam 9 pagi. Laporan tersedia sebelum meeting bulanan, dan tidak ada yang lupa generate.

ANTI-PATTERN vs BENAR pada SLO Implementation #

Beberapa jebakan umum saat mengadopsi SLO. Memahaminya di awal akan menghemat waktu berminggu-minggu:

# ANTI-PATTERN: SLO tanpa error budget calculation
# "SLO kita 99.9%" - ok, tapi 99.9% dari apa? Per jam? Per hari? Per bulan?
# Tanpa error budget yang eksplisit, SLO jadi jargon tanpa konsekuensi

# BENAR: definisikan SLO + window + error budget secara eksplisit
# Format: SLI + target + window + error budget
slo_definitions:
  - name: api-availability
    sli: "percentage of HTTP requests with status < 500"
    target: 99.9
    window: 30d
    error_budget: "43.2 minutes per 30 days"   # Eksplisit, bukan hanya angka
    owner: platform-team
# ANTI-PATTERN: alert yang langsung page saat SLO sedikit di bawah target
- alert: SLOBreach
  expr: job:http_requests_total:success_rate5m < 0.999
  for: 1m
  labels:
    severity: critical
  # Masalah: success rate 99.8% dalam 5 menit BUKAN berarti SLO 99.9%/30d dilanggar
  # Ini false positive yang akan bombardir on-call

# BENAR: alert berdasarkan burn rate, bukan absolute value
- alert: SLO_HighBurnRate_Fast
  expr: >
    job:error_budget_burn_rate:1h > 14.4
    and
    job:error_budget_burn_rate:5m > 14.4    
  for: 2m
  labels:
    severity: critical
  # Alert hanya jika budget terbakar CEPAT (akan habis dalam 2 hari)
  # SLO 99.8% sebentar itu bukan masalah; SLO 99.5% selama 6 jam = masalah
# ANTI-PATTERN: target SLO yang tidak realistis
- alert: CriticalServiceDown
  expr: up{job="critical"} == 0
  # Implikasi: setiap downtime 1 menit pun page on-call
  # Target internal 100% availability = tidak ada error budget = setiap insiden adalah "SLO breach"
  # On-call burnout dalam 3 bulan

# BENAR: set SLO yang menyisakan margin
# Realita: 99.99% availability butuh multi-region active-active, redundansi 3x, on-call 24/7
# Untuk kebanyakan service, 99.9% adalah sweet spot antara reliability dan biaya
# Definisi "down" juga harus jelas: 5xx > 1% dari traffic? Atau single endpoint fail?
slo_target: 99.9           # Realistis
sla_target: 99.5           # Lebih longgar dari SLO
error_budget_30d: 43m      # Tim punya margin
# ANTI-PATTERN: SLO yang hanya ada di spreadsheet
# "Target kita 99.9%" - ditulis di Google Sheet, tidak terlihat di dashboard
# Tim tidak tahu apakah mereka on-track sampai akhir bulan

# BENAR: SLO visible di dashboard real-time
# - Panel "Error Budget Remaining" prominen di dashboard service
# - Alert page on-call jika burn rate tinggi
# - Status SLO di-review mingguan di meeting
# - Laporan SLA ke pelanggan di-generate otomatis dari data real-time

Tiga pola yang bisa diambil: (1) SLO tanpa error budget hanya angka — error budget yang eksplisit membuat SLO punya konsekuensi. (2) Alert berdasarkan absolute value = false positive — burn rate alert merefleksikan impact ke SLO. (3) SLO yang tidak terlihat = SLO yang dilupakan — dashboard dan alert membuat SLO bagian dari daily operations.


Integrasi SLO dengan Alert dan Incident Response #

SLO yang terdefinisi dengan baik punya implikasi langsung ke alert policy dan incident handling. Tiga koneksi kunci:

  1. Burn rate alert → runbook linkage — setiap alert burn rate punya runbook_url yang menjelaskan langkah diagnosis dan mitigasi. Lihat Alerting untuk detail tentang alert routing.

  2. SLO violation → incident severity classification — saat SLO dilanggar, tentukan severity incident berdasarkan berapa banyak error budget yang terbakar:

    • Page alert (14x burn rate) = severity 1 incident, butuh on-call immediate
    • Ticket alert (6x burn rate) = severity 2, fix dalam working hours
    • Slow burn (1x untuk 3 hari) = severity 3, backlog item
  3. Error budget policy → feature freeze — saat error budget tinggal < 20%, tim biasanya menyetujui “feature freeze” — semua engineering effort difokuskan ke reliability, bukan fitur baru. Ini keputusan product, bukan teknis — playbook bisa diotomasi untuk notifikasi.

Detail lebih lanjut tentang incident response workflow ada di Incident Response. Untuk setup Prometheus yang menjadi dasar pengukuran SLI, lihat Monitoring.


Testing SLO Alert Rules #

Sama seperti alert rules biasa, SLO alert rules harus di-test untuk memastikan threshold dan window menghasilkan alert yang tepat. Pakai promtool test rules:

# tests/slo-alerts.test.yml
rule_files:
  - /etc/prometheus/rules/slo-recording.yml
  - /etc/prometheus/rules/slo-alerts.yml

evaluation_interval: 1m

tests:
  # Test 1: SLO alert TIDAK firing saat error rate rendah
  - interval: 1m
    name: "low error rate stays calm"
    input_series:
      - series: 'http_requests_total{job="api",status="200"}'
        values: '9990x60'
      - series: 'http_requests_total{job="api",status="500"}'
        values: '10x60'    # 0.1% error rate — di bawah SLO 99.9%
    alert_rule_test:
      - eval_time: 1h
        alertname: SLO_HighBurnRate_Fast
        exp_alerts: []     # Should NOT fire

  # Test 2: SLO alert firing saat error rate tinggi
  - interval: 1m
    name: "high error rate triggers page"
    input_series:
      - series: 'http_requests_total{job="api",status="200"}'
        values: '500x60'
      - series: 'http_requests_total{job="api",status="500"}'
        values: '500x60'    # 50% error rate — disaster
    alert_rule_test:
      - eval_time: 10m
        alertname: SLO_HighBurnRate_Fast
        exp_alerts:
          - exp_labels:
              severity: critical
              slo: availability
              job: api
            exp_annotations:
              summary: "SLO burn rate sangat tinggi"
              runbook_url: "https://wiki.company.com/runbooks/slo-burn-fast"

  # Test 3: Medium burn rate fires ticket alert, not page
  - interval: 1m
    name: "medium burn rate fires ticket"
    input_series:
      - series: 'http_requests_total{job="api",status="200"}'
        values: '900x360'
      - series: 'http_requests_total{job="api",status="500"}'
        values: '100x360'    # 10% error rate over 6 hours — 6x burn rate
    alert_rule_test:
      - eval_time: 6h
        alertname: SLO_MediumBurnRate
        exp_alerts:
          - exp_labels:
              severity: warning
              slo: availability

Jalankan test ini di CI pipeline setiap kali threshold atau window diubah:

promtool test rules /etc/prometheus/tests/slo-alerts.test.yml

Ringkasan #

  • SLI → SLO → Error Budget → Burn Rate adalah rantai yang logis: ukur SLI, tetapkan target SLO, hitung error budget yang tersisa, monitor burn rate untuk prediksi habisnya budget.
  • SLI harus merepresentasikan pengalaman pengguna — availability dan latency adalah yang paling umum. CPU usage dan memory usage bukan SLI yang baik karena tidak berkorelasi langsung dengan user satisfaction.
  • Recording rules untuk kalkulasi SLI yang kompleks — jauh lebih efisien dari query real-time di setiap evaluasi alert atau render dashboard.
  • Multi-window burn rate (1h+5m untuk page, 6h+30m untuk ticket, 3d+6h untuk slow burn) menangkap masalah yang cepat maupun lambat tanpa terlalu banyak false alarm — single window = noisy, multi-window = robust.
  • Alert berdasarkan burn rate, bukan absolute value — success rate 99.8% dalam 5 menit BUKAN berarti SLO 30 hari dilanggar. Burn rate 14x berarti budget habis dalam 2 hari, itu yang harus page.
  • Dashboard SLO harus menampilkan error budget remaining sebagai panel paling prominen — ini adalah angka yang paling bermakna untuk keputusan prioritas engineering.
  • SLO lebih ketat dari SLA — SLA 99.5% = SLO 99.9%. Buffer 0.4% memberi ruang untuk insiden yang tidak melanggar kontrak tapi tetap perlu diinvestigasi.
  • Laporan SLA otomatis via Ansible + Prometheus API menghilangkan pekerjaan manual penyusunan laporan bulanan ke pelanggan atau manajemen — generate via cron job di awal bulan.
  • Test SLO alert rules dengan promtool test rules di CI — pastikan threshold dan window menghasilkan alert yang tepat untuk berbagai skenario error rate.
  • SLO tanpa error budget hanya angka — error budget yang eksplisit dengan unit waktu (43 menit/bulan) membuat SLO punya konsekuensi dan jadi bahan diskusi di planning meeting.

← Sebelumnya: Incident Response   Berikutnya: Best Practice →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact