On-call ve Runbook: Nöbet Düzeni, Escalation ve Uygulanabilir Runbook Yazmak
Sürdürülebilir on-call primary ve secondary nöbetçiyle, page bütçesiyle ve her alert için kopyalanabilir komutlardan oluşan bir runbook'la kurulur. Üç kişilik Bookstore ekibine uyarlanmış bir rotation, amtool ile doğrulanan Alertmanager routing'i, repoda tutulan bir runbook ve bu runbook'un gerçek bir arızada denenmesiyle bulduğumuz hataları; ilk iki adımı alert metnine taşıyan otomasyonu anlatıyoruz.
Sürüm notu: Bu yazıdaki örnekler .NET 10, Prometheus 3.16.0 (promtool dahil), Alertmanager 0.35.0'ın amtool'u ve dotnet-counters 10.0 ile denenmiştir. PagerDuty ve benzeri paging araçları çalıştırılmadı; dokümanlarından anlatılıyor.
Önce Tahmin Et
Bookstore (sitedeki serilerin ortak örnek uygulaması, bir online kitapçı) ekibinde production'ı üç kişi işletiyor: Selin, Emre ve Ayşe. 7/24 bir on-call (nöbet) düzeni kurulacak. Google'ın SRE (Site Reliability Engineering; güvenilirliği yazılım mühendisliğiyle yönetme disiplini) kitabına göre bir mühendisin zamanının en fazla yüzde 25'i on-call'a gitmeli ve her an bir primary, bir secondary nöbetçi olmalı. Bu kurala uyan tek lokasyonlu bir ekip en az kaç kişi olmalı? Üç kişilik Bookstore ekibi bu kurala göre ne yapmalı? İkinci soru: checkout alert'i gece 02:10'da çaldı ve primary nöbetçi telefonu duymadı. Alert ne zaman, kime gitmeli? Cevaplar yazının ortasında.
Rotation Tasarımı
On-call'ın temel parçaları basit: bir rotation (nöbetin kişiler arasında hangi sırayla ve ne kadar süreyle döndüğü), her an bir primary (ilk çağrılan kişi) ve bir secondary (primary cevap vermezse ya da yardım isterse çağrılan kişi). Dünyanın farklı saat dilimlerinde ekipleri olan şirketler follow-the-sun (nöbetin gün boyunca saat dilimleri arasında devredilmesi) kullanıyor: herkes kendi mesai saatinde nöbet tutuyor, kimse gece uyandırılmıyor. Tek ofisli küçük ekipler için en yaygın düzen haftalık rotation.
SRE kitabının "Being On-Call" bölümü sayıları şöyle kuruyor: zamanın en az yüzde 50'si mühendislik işine, en fazla yüzde 25'i on-call'a gitmeli. Her an iki kişi (primary ve secondary) nöbetteyse ve nöbetler haftalıksa, yüzde 25 kuralı tek lokasyonlu bir ekip için en az sekiz kişi demek: her mühendis ayda bir hafta primary ya da secondary. İki lokasyonlu ekipler için her lokasyonda en az altı kişi öneriliyor. Önce Tahmin Et'teki ilk sorunun cevabı sekiz.
Bookstore'un üç kişisi bu kurala göre 7/24 nöbet tutamaz; tutarsa her biri zamanının üçte ikisini nöbette geçirir. Küçük ekip için gerçekçi seçenekler:
- Gece ve hafta sonu yalnız gerçekten acil olan page'ler: checkout'un burn rate alert'i evet; arama latency'si, disk doluluğu ya da sertifika uyarısı mesai saatine kalan ticket.
- Haftalık rotation, primary ve secondary ardışık: bu hafta Emre primary, Ayşe secondary; gelecek hafta Ayşe primary, Selin secondary.
- Nöbet haftasında başka planlı iş yok: o hafta sprint'e iş alınmıyor, toil ve küçük güvenilirlik işleri yapılıyor.
- Telafi yazılı: gece page'i alan kişi ertesi gün geç başlıyor ya da izin günü alıyor.
Page Bütçesi ve Sürdürülebilir On-Call
Bir nöbetin yükünü page sayısı belirliyor. SRE kitabı bir incident'ın (tespit, müdahale, postmortem ve takip işleriyle birlikte) ortalama 6 saat sürdüğünü varsayıyor ve buradan 12 saatlik bir nöbette en fazla 2 incident sonucunu çıkarıyor. Hedef dağılımın medyanı sıfır: çoğu nöbet hiç page'siz geçmeli. Her gün page üreten bir bileşen varsa sorun nöbetçide değil, sistemde ya da alert'te.
Page sayısını ölçmek Prometheus'ta tek sorgu. Bir önceki yazıdaki canlı arızada (ödeme sağlayıcısı 10 dakika boyunca isteklerin yüzde 30'unu reddetti) hangi alert'in kaç dakika çaldığı (sorguyu denemenin sonunda, 21:44:22'de çalıştırdık):
sum by (long_window, severity) (count_over_time(ALERTS{alertname="CheckoutErrorBudgetBurn", alertstate="firing"}[1h])) * 15 / 60
{long_window="3d", severity="ticket"} 41.5
{long_window="6h", severity="page"} 34.75
{long_window="1h", severity="page"} 7
Rule'lar 15 saniyede bir değerlendirildiği için örnek sayısı çarpı 15 saniye, dakikayı veriyor. Bir saatte iki page, toplam yaklaşık 42 dakika: laboratuvarın kısa pencereleri yüzünden production'da olacağından fazla (bir önceki yazıda açıkladık), ama nöbetçinin o gecesinin nasıl geçtiğini anlatan sayı bu. Haftalık toplamı tutmak için aynı sorgu [7d] ile çalışır.
Bu sayıyı haftalık handoff notuna yazmak, on-call yükünü görünür kılıyor. Bookstore'da handoff notu repoda bir şablon (docs/oncall/handoff.md); nöbeti devreden kişi pazartesi 15 dakikalık bir görüşmede bunu bir sonrakine aktarıyor:
# On-call handoff: week <NN>
From: <name> · To: <name> · Handoff call: Monday 10:00 (15 minutes)
## Pages this week
| When (UTC) | Alert | What it was | Time spent | Runbook OK? |
|---|---|---|---|---|
## Still open
- Incidents or tickets the next person inherits, with links.
## Watch out for
- Deploys, campaigns, provider maintenance windows planned for next week.
- Flapping alerts or known noise (and the ticket that will fix it).
## Error budget (30 days)
- Checkout availability: <remaining> · Checkout latency: <remaining>
## Toil
- Manual work done this week that should be automated (one line each, with a ticket).
Escalation ve Routing
Alert'in kime gideceğini iki katman belirliyor. Birincisi Alertmanager: alert'i severity'sine göre doğru alıcıya (receiver) yönlendiriyor, aynı alert'in tekrarlarını grupluyor ve page çalarken aynı sorunun ticket'ını tutuyor. İkincisi paging aracı (PagerDuty, incident.io, Splunk On-Call gibi): telefon araması, SMS (kısa mesaj), kimin nöbette olduğu takvimi ve escalation policy (cevap gelmezse alert'in kime, kaç dakika sonra geçeceği) orada.
# Bookstore için Alertmanager routing'i (kontrol: amtool check-config deploy/alertmanager/alertmanager.yml).
# page → paging aracı üzerinden nöbetçinin telefonu (secondary'ye escalation orada yapılır)
# ticket → ekibin iş takip aracı, mesai saatinde okunur
route:
receiver: team-ticket
group_by: [alertname, long_window]
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
routes:
- matchers: [severity="page"]
receiver: oncall-pager
repeat_interval: 1h
- matchers: [severity="ticket"]
receiver: team-ticket
group_wait: 10m
repeat_interval: 24h
# Bir alert'in page'i çalarken aynı alert'in ticket'ı gönderilmez.
inhibit_rules:
- source_matchers: [severity="page"]
target_matchers: [severity="ticket"]
equal: [alertname]
receivers:
- name: oncall-pager
pagerduty_configs:
# Anahtar asla repoda değil; deploy sırasında secret store'dan dosya olarak bağlanır.
- routing_key_file: /etc/alertmanager/secrets/pagerduty-routing-key
severity: critical
- name: team-ticket
webhook_configs:
- url: https://tickets.example.com/hooks/alertmanager
Routing ağacını ve iki alert'in nereye gittiğini amtool ile kontrol ettik:
amtool config routes --config.file=deploy/alertmanager/alertmanager.yml
Routing tree:
.
└── default-route receiver: team-ticket
├── {severity="page"} receiver: oncall-pager
└── {severity="ticket"} receiver: team-ticket
amtool config routes test --config.file=deploy/alertmanager/alertmanager.yml alertname=CheckoutErrorBudgetBurn severity=page long_window=1h
oncall-pager
amtool config routes test --config.file=deploy/alertmanager/alertmanager.yml alertname=CheckoutErrorBudgetBurn severity=ticket long_window=3d
team-ticket
amtool config routes test --verify.receivers=oncall-pager --config.file=deploy/alertmanager/alertmanager.yml alertname=CheckoutErrorBudgetBurn severity=ticket
team-ticket
WARNING: Expected receivers did not match resolved receivers.
Son komutun çıktısı da önemli: ticket bir page alıcısına gitmiyor ve --verify.receivers yanlış beklentide sıfırdan farklı bir çıkış koduyla duruyor. Bu komut CI pipeline'ında bir adım olarak, routing değişikliklerinin yanlışlıkla gece telefon çaldırmasını engelliyor.
Escalation policy'nin kendisi paging aracında yaşıyor. PagerDuty'nin açık incident response dokümanı primary cevap vermezse 5 dakika sonra secondary'ye geçen bir policy öneriyor ve üçüncü seviye olarak bütün ekibi son çare sayıyor. Önce Tahmin Et'teki ikinci sorunun cevabı bu: 02:10'da çalan page 02:15'te secondary'nin telefonunu çaldırmalı. Bu kısmı bir hesapla çalıştırmadık; ayarlar aracın dokümanından. Araç seçerken bir not: Atlassian Opsgenie'nin satışını Haziran 2025'te durdurdu ve ürünü 5 Nisan 2027'de kapatıyor; yeni bir kurulum için seçilmemeli.
Her Alert'in Bir Runbook'u
Runbook, belirli bir alert çaldığında ne yapılacağını adım adım anlatan doküman. Gece 02:10'da uyanan kişi sistemi en iyi bilen kişi olmayabilir; runbook ona ilk beş dakikada nereye bakacağını ve hangi durumda ne yapacağını söylüyor. İyi bir runbook'un parçaları: alert ne demek ve kullanıcı ne yaşıyor; ilk beş dakikanın kontrolleri (her biri kopyalanabilir bir komut ya da sorgu); her olası sebep için mitigation (önce etkiyi durdurmak, sebebi sonra bulmak); escalation; ve sonrası (zaman çizelgesi, postmortem kriteri).
Runbook'u kodun yanında, repoda Markdown olarak tutuyoruz: docs/runbooks/checkout-error-budget-burn.md. Wiki'deki bir sayfa koddan bağımsız eskir; repodaki runbook alert rule'u değiştiren pull request'te birlikte güncellenir. Alert de runbook'u gösteriyor:
- alert: CheckoutErrorBudgetBurn
expr: |
slo:checkout_availability:error_ratio_rate1h > (14.4 * 0.005)
and
slo:checkout_availability:error_ratio_rate5m > (14.4 * 0.005)
labels:
severity: page
long_window: 1h
annotations:
summary: "Checkout is burning its 30-day error budget 14.4 times too fast (1h and 5m windows)"
description: >-
Checkout errors (5m): {{ with query "slo:checkout_availability:error_ratio_rate5m" }}{{ . | first | value | humanizePercentage }}{{ end }}.
{{ with query "topk(1, sum by (server_address) (rate(http_client_request_duration_seconds_count{job='bookstore-api', error_type!=''}[5m])))" }}Most failing outgoing calls: {{ (. | first).Labels.server_address }} ({{ . | first | value | printf "%.2f" }}/s).{{ else }}No failing outgoing calls.{{ end }}
runbook_url: https://git.example.com/bookstore/blob/main/docs/runbooks/checkout-error-budget-burn.md
Runbook'un ilk beş dakikası, aşağıdaki denemeden sonra düzeltilmiş hâliyle:
## First 5 minutes
1. Acknowledge the page and write one line in the incident channel: "Looking at CheckoutErrorBudgetBurn".
2. How bad, and which answer? In Prometheus:
```promql
sum by (http_response_status_code) (rate(http_server_request_duration_seconds_count{job="bookstore-api", http_route="/api/orders/"}[5m]))
```
3. Is it the payment provider? Bookstore.Api's calls to it, by host and result:
```promql
sum by (server_address, http_response_status_code, error_type) (rate(http_client_request_duration_seconds_count{job="bookstore-api"}[5m]))
```
503, or `error_type="System.Threading.Tasks.TaskCanceledException"` (timeout; such series have no status code)
from the provider's host: go to "Payment provider".
4. Was there a deploy? Versions that sent metrics in the last hour:
```promql
count by (service_version) (count_over_time(target_info{job="bookstore-api"}[1h]))
```
Two versions, or a version younger than the alert: go to "Bad deploy".
5. Is the process itself healthy? On the host or in the pod, find the PID first (never attach by name: another process
may have the same name), then watch it:
```
dotnet-counters ps
dotnet-counters monitor -p <pid> --counters System.Runtime,Microsoft.AspNetCore.Hosting
```
Thread pool queue length growing or CPU near 100 %: go to "Neither".
Runbook'u Gerçek Bir Arızada Denemek
Runbook'u kâğıt üzerinde bırakmadık: bir önceki yazının canlı arızasında, ödeme sağlayıcısı ödemelerin yüzde 30'unu reddederken arızanın dördüncü dakikasında (21:06) adım adım uyguladık. O sırada 6 saatlik page çalıyordu. 2. adım:
{http_response_status_code="201"} 2.8841801665245628
{http_response_status_code="502"} 0.9085599098956854
Saniyede 2,88 başarılı ve 0,91 başarısız checkout: 5 dakikalık pencerede hata oranı yüzde 24 (pencerenin ilk dakikası arızadan önceydi). 3. adım sebebi gösterdi:
{error_type="404", http_response_status_code="404", server_address="localhost"} 0.1999978947590025
{http_response_status_code="200", server_address="127.0.0.1"} 2.8841801665245623
{http_response_status_code="200", server_address="localhost"} 0.0666659649196675
{error_type="503", http_response_status_code="503", server_address="127.0.0.1"} 0.9086392774812895
Ödeme sağlayıcısına (laboratuvarda 127.0.0.1 adresinde) giden çağrılar saniyede 0,91 kez 503 alıyor; checkout'ların 502 hızıyla aynı. Aynı çıktıda beklemediğimiz bir şey de vardı: localhost'a giden çağrıların bir kısmı 404 alıyordu. Bu, API'nin kendi OTLP (OpenTelemetry Protocol; telemetry verisini taşıyan protokol) exporter'ı. Serinin ikinci yazısında kullandığımız UseOtlpExporter() metric'lerle birlikte log'ları da aynı adrese gönderiyor ve Prometheus log kabul etmediği için 404 dönüyor. Runbook'u izlerken ilgisiz bir hatayı da bulmuş olduk. 4. adım:
{service_version="1.0.0+623d076ccae5222ab9e0e03dcf7caebe482e97cd"} 1
Son bir saatte metric gönderen tek bir sürüm var, yani deploy yok (+'dan sonrası build'in commit'i). 5. adım için dotnet-counters'ı API process'ine bağladık; çıktının ilgili satırları (CSV, zaman sütunu çıkarıldı):
Provider,Counter Name,Counter Type,Mean/Increment
System.Runtime,dotnet.thread_pool.queue.length ({work_item}),Metric,0
System.Runtime,dotnet.thread_pool.thread.count ({thread}),Metric,7
System.Runtime,dotnet.process.cpu.time (s / 1 sec)[cpu.mode=system],Rate,0.010300000000000864
System.Runtime,dotnet.process.cpu.time (s / 1 sec)[cpu.mode=user],Rate,0.024915000000007126
Microsoft.AspNetCore.Hosting,http.server.request.duration (s)[http.request.method=POST;http.response.status_code=201;http.route=/api/orders/;network.protocol.version=1.1;url.scheme=http;Percentile=50],Metric,0.069091796875
Microsoft.AspNetCore.Hosting,http.server.request.duration (s)[error.type=502;http.request.method=POST;http.response.status_code=502;http.route=/api/orders/;network.protocol.version=1.1;url.scheme=http;Percentile=50],Metric,0.0110931396484375
Process sağlıklı: thread pool kuyruğu boş, 7 thread var, CPU bir çekirdeğin yüzde 3,5'i. 502'ler 11 ms'de dönüyor; API bekleyip zaman aşımına düşmüyor, sağlayıcının hızlı reddini aktarıyor. Runbook'un gönderdiği yer "Payment provider" bölümü; laboratuvarda sağlayıcıyı 21:12:20'de düzelttik.
Deneme runbook'ta iki şeyi de sınadı. Birincisi, sağlayıcının timeout'unu tanımak için yazdığımız error_type değeri bir tahmindi. Sağlayıcıyı 90 saniye boyunca her ödemede 4 saniye bekletip (API'nin ödeme timeout'u 3 saniye) aynı sorguya baktık:
sum by (server_address, http_response_status_code, error_type) (increase(http_client_request_duration_seconds_count{job="bookstore-api"}[90s]))
{http_response_status_code="200", server_address="127.0.0.1"} 0
{http_response_status_code="200", server_address="localhost"} 6
{error_type="System.Threading.Tasks.TaskCanceledException", server_address="127.0.0.1"} 329.27580000000006
Tahmin doğru çıktı, ama tahmin olarak kalmamalıydı; bir de bilmediğimiz bir ayrıntı gördük: timeout'ta http_response_status_code etiketi hiç yok, çünkü cevap gelmedi. Status code'a göre filtreleyen bir sorgu timeout'ları hiç göstermezdi. localhost'taki 404'ler ise bu ölçümde yok, çünkü ondan önce exporter'ı düzelttik (aşağıda). İkincisi, 5. adımdaki dotnet-counters monitor -n Bookstore.Api process'i adıyla buluyor. Laboratuvarda API dotnet Bookstore.Api.dll olarak çalıştığı için process'in adı dotnet'tı; üstelik aynı makinede aynı adı taşıyan başka bir Bookstore.Api kopyası da çalışıyordu. Adla bağlanmak yanlış process'i izlemek demek. Runbook'u önce dotnet-counters ps ile PID'i (process ID, işletim sisteminin process'e verdiği numara) bulacak şekilde düzelttik. Exporter'ı da yalnız metric gönderecek şekilde değiştirdik:
.WithMetrics(metrics => metrics
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddView("http.server.request.duration", new ExplicitBucketHistogramConfiguration
{
Boundaries = [0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 0.8, 1, 2.5, 5, 7.5, 10]
})
// Yalnız metric. Nereye gideceklerini OTEL_EXPORTER_OTLP_ENDPOINT / _PROTOCOL belirler (burada: Prometheus'un OTLP
// receiver'ı). UseOtlpExporter() log'ları da oraya gönderirdi ve Prometheus onlara 404 döner.
.AddOtlpExporter());
Runbook Adımını Otomasyona Çevirmek
Runbook'un 2. ve 3. adımları her arızada aynı iki sorgu. Nöbetçinin bunları gece yarısı elle yazması yerine alert'in kendisi taşıyabilir: Prometheus'ta alert annotation'ları template olarak işleniyor ve template içinde query ile sorgu çalıştırılabiliyor. Yukarıdaki description bunu yapıyor; promtool testine sağlayıcının 503'lerini temsil eden bir seri ve beklenen metni ekledik:
# Üç hatasız günden sonra her checkout'un hata verdiği beş dakika: burn rate 200, yaklaşık 4,3 dakika sonra page,
# ve arıza bittikten birkaç dakika sonra sessizlik.
- interval: 1m
input_series:
- series: 'http_server_request_duration_seconds_count{job="bookstore-api", http_route="/api/orders/", http_request_method="POST", http_response_status_code="201"}'
values: '0+600x4320 2592000+0x5 2592000+600x30'
- series: 'http_server_request_duration_seconds_count{job="bookstore-api", http_route="/api/orders/", http_request_method="POST", http_response_status_code="502"}'
values: '0+0x4320 600+600x4 3000+0x30'
# Sebep: ödeme sağlayıcısı her ödemeye 503 dönüyor.
- series: 'http_client_request_duration_seconds_count{job="bookstore-api", server_address="payments.example.com", http_response_status_code="503", error_type="503"}'
values: '0+0x4320 600+600x4 3000+0x30'
alert_rule_test:
- eval_time: 72h4m
alertname: CheckoutErrorBudgetBurn
exp_alerts: []
- eval_time: 72h5m
alertname: CheckoutErrorBudgetBurn
exp_alerts:
- exp_labels:
severity: page
long_window: 1h
exp_annotations:
summary: "Checkout is burning its 30-day error budget 14.4 times too fast (1h and 5m windows)"
description: "Checkout errors (5m): 100%. Most failing outgoing calls: payments.example.com (10.00/s)."
runbook_url: https://git.example.com/bookstore/blob/main/docs/runbooks/checkout-error-budget-burn.md
...
Test geçti. Canlı denemede (sağlayıcı ödemelerin yarısını reddederken) Prometheus'un /api/v1/alerts cevabındaki metin:
curl -s http://localhost:9090/api/v1/alerts
...
{
"labels": {
"alertname": "CheckoutErrorBudgetBurn",
"long_window": "1h",
"severity": "page"
},
"annotations": {
"description": "Checkout errors (5m): 48.85%. Most failing outgoing calls: 127.0.0.1 (1.84/s).",
"runbook_url": "https://git.example.com/bookstore/blob/main/docs/runbooks/checkout-error-budget-burn.md",
"summary": "Checkout is burning its 30-day error budget 14.4 times too fast (1h and 5m windows)"
},
"state": "firing",
"activeAt": "2026-11-14T21:48:15.11230018Z"
}
...
Nöbetçi telefonu açtığında ilk iki adımın cevabı bildirimde; runbook linki de yanında. Sıradaki adım bunu bir otomasyona çevirmek olabilir: örneğin son bir saatte deploy varsa ve hata oranı ondan sonra başladıysa otomatik rollback. Deployment Stratejileri serisinin rollback ve kill switch yazısı bu kararların kriterlerini anlatıyor.
Self-healing'in (sistemin bir sorunu insan müdahalesi olmadan kendi düzeltmesi) de bir riski var. "Hata oranı yükselince API'yi restart et" kuralı bizim arızamızda hiçbir şeyi düzeltmezdi, işlenmekte olan siparişleri düşürür ve sağlayıcıya giden yükü restart anlarında yığardı. Otomasyonu, runbook'ta elle en az birkaç kez uygulanmış ve sonucu bilinen bir adım için kur; her otomasyonun kendisi için bir alert ("son bir saatte üçten fazla otomatik rollback") ekle. Toil'i ölçmek için de handoff notundaki satır yeterli: aynı elle müdahale iki hafta üst üste yazıldıysa otomasyon işinin önceliği gelmiştir.
Üretimde Dikkat
Runbook'u test et. Denenmemiş bir runbook ilk gerçek kullanımında yanlış çıkar: bizimkinde doğrulanmamış bir hata tipi adı ve yanlış process'e bağlanabilecek bir komut vardı. Game day'ler ve yeni gelen birinin runbook'u bir arıza senaryosunda izlemesi, düzeltmenin en ucuz yolu.
Shadow nöbet. Yeni ekip üyesi ilk birkaç hafta deneyimli birinin yanında, page'leri onunla birlikte alarak nöbete giriyor. PagerDuty'nin dokümanı da bunu öneriyor.
Nöbet sırasında ticket'lar bekleyebilir. Mesai dışında gelen acil olmayan bir uyarı için iş kaydı açıp uyumak doğru davranış; her uyarıya gece müdahale etmek, nöbeti sürdürülemez yapar.
Her page bir soru sorar: bu alert gerçekten page miydi? Haftalık handoff'ta "runbook işe yaradı mı, bu alert ticket olabilir miydi" sorularını sormak, alert yorgunluğunun birikmesini engelliyor.
Sık Düşülen Tuzaklar
Runbook'suz alert. "Ne yapacağımı bilmediğim alert" nöbetçinin zamanını tahmin yürütmeye harcatır. Runbook yazamadığın bir alert'in page olması da şüpheli.
Runbook'ta "duruma göre karar ver". Her adım somut bir komut ya da sorgu olmalı; "logları incele" bir adım değil, "şu sorguyu çalıştır, 503 görürsen şuraya git" bir adım.
Tek kişilik nöbet. Secondary olmayan bir düzende telefonu duymayan, hasta ya da yolda olan primary için kimse yok. Küçük ekipte bile secondary şart.
Restart'ı varsayılan çözüm yapmak. Bizim senaryomuzda restart ödeme sağlayıcısını düzeltmez, yalnız işlenmekte olan siparişleri düşürür. Restart'ın neyi çözdüğünü bilmeden runbook'a yazma.
Küçük Bir Deneme
★☆☆ Alertmanager configuration'ına severity="page" olup team="search" label'ı taşıyan alert'leri ayrı bir alıcıya gönderen bir route ekle ve amtool config routes test --verify.receivers ile doğrula.
★★☆ Sahte ödeme sağlayıcısının slowRatio'sunu 0,3 ve slowDelayMs'yi 4000 yap (Bookstore.Api'nin ödeme timeout'u 3 saniye). Runbook'u baştan sona izle: hangi adım sebebi gösteriyor, hangisi yanıltıyor? Runbook'u buna göre düzelt.
★★★ Kendi ekibin için bir aylık page bütçesi belirle (örneğin haftada en fazla 2 gece page'i). Prometheus'taki ALERTS serisinden haftalık page sayısını hesaplayan bir recording rule yaz ve bütçeyi aşınca ekibe ticket açan bir alert ekle.
Runbook bir alert'in ilk beş dakikasını kapsıyor. Ama bazı arızalar bir kişinin müdahalesini aşar: birden çok ekip, müşteri iletişimi ve saatler süren bir mitigation. Bir sonraki yazıda incident'ı ilan etmeyi, severity seviyelerini, incident commander rolünü ve "önce mitigation" ilkesini, ödeme sağlayıcısının timeout'ları üzerine bir tabletop exercise ile anlatıyoruz.