İzleme ve uyarı: p95, hata oranı, SLO ve gerçekten işe yarayan uyarılar
Bir sistemin çöktüğünü kullanıcıdan duyuyorsanız izleme yok demektir. Varsa da yanlış şeyi ölçüyordur. İkincisi daha tehlikeli, çünkü panoda her şey yeşil görünürken insanlar hata alıyor olabilir.
Portföyümdeki bir çalışmada tam olarak bunu yaşadım. Kurgusal bir eğitim platformu, sınav saatlerinde çöküyordu. Sunucunun kendi ölçtüğü p95 yanıt süresi 185 ms civarındaydı, kulağa gayet iyi geliyor. Aynı anda öğrenciler 5 saniyelik zaman aşımına düşüyordu. Bu yazı, neyin, neden ve hangi eşikle ölçüldüğünü anlatıyor.
Ortalama değil, yüzdelik
Yanıt süresini ortalamayla izlemek en yaygın hata. 100 isteğin 95'i 20 ms, 5'i 4 saniye sürerse ortalama yaklaşık 220 ms çıkar. Panoda makul duran bir sayı. Ama her 20 kullanıcıdan biri 4 saniye bekliyor.
Yüzdelikler bu yüzden var. p95, isteklerin %95'inin bu sürenin altında kaldığı değer. p99 aynı mantıkla daha uçtaki kullanıcıları gösterir. Genelde p95 uyarı için, p99 da inceleme için kullanılır.
Eğitim platformunda provada ölçülen değerler:
| v1 (eski) | v2 (düzeltilmiş) | |
|---|---|---|
| p50 | 829,6 ms | 7 ms |
| p95 | 1.201 ms | 43 ms |
| p99 | 2.250,4 ms | 94,2 ms |
| Hata oranı | %39,6 | %0 |
Bir not: v1'in p95 değeri sadece başarılı isteklerden hesaplandı. Zaman aşımına düşen isteklerin süresi bu sayıya girmiyor. Yani hata oranını izlemeden yalnızca gecikmeye bakmak da yanıltır. İkisi her zaman birlikte izlenmeli.
Sunucu içi metrikler kuyruğu göremez
185 ms ile 5 saniye arasındaki fark nereden geliyordu? Sunucu, süreyi isteği işlemeye başladığı andan itibaren ölçüyor. Ama Node.js tek iş parçacıklı bir olay döngüsüyle çalışır. Senkron bir veritabanı çağrısı döngüyü kilitlediğinde, yeni gelen istekler işlenmeye başlanmadan önce sırada bekler. O bekleme süresi sunucunun kendi metriğine hiç yansımaz.
Çözüm, olay döngüsünün ne kadar geciktiğini doğrudan ölçmek. Node.js'te bunun için hazır bir araç var:
import { monitorEventLoopDelay } from "node:perf_hooks";
const h = monitorEventLoopDelay({ resolution: 10 });
h.enable();
// her ölçüm aralığında:
const lagMs = h.percentile(99) / 1e6; // nanosaniyeden milisaniyeye
h.reset();
Eğitim platformu çalışmasında bu metrik için 500 ms eşikli bir kural ekledim. Sınav provasında sorunu başladıktan 6 saniye sonra yakaladı, en kötü değer 1.196 ms olarak kaydedildi. Aynı sırada sunucu içi p95 hâlâ "normal" görünüyordu.
Başka dillerde karşılığı farklı olabilir: iş parçacığı havuzunun doluluğu, bekleyen istek kuyruğunun uzunluğu, veritabanı bağlantı havuzunda bekleme süresi. Fikir aynı: işin başlamasından önceki bekleme süresini de görmeniz gerekiyor.
Sentetik kontrol: dışarıdan bir kullanıcı gibi bakın
İkinci güvence, sistemi dışarıdan gerçek bir kullanıcı gibi test etmek. Aynı çalışmadaki izleme ajanı 1–2 saniyede bir şu adımları çalıştırıyor: ders listesini aç, ardından bir sınavı aç, toplam süreyi ölç. Eşik 2 saniye.
Bu kontrolün değeri, uygulamanın dışında durması. Sunucu, ağ, DNS, proxy, veritabanı; hangisi bozulursa bozulsun sentetik kontrol yavaşlar ya da hata verir. v1 provasında bu kontrol 5 saniyeye (istemci zaman aşımı sınırı) dayandı ve uyarı üretti.
Sentetik kontrolü tasarlarken dikkat edilmesi gereken iki şey var. İlki, gerçekten önemli bir akışı taklit etmesi, sadece ana sayfayı açması yetmez. İkincisi, veri yazıyorsa bunu gerçek veriden ayrılmış bir test hesabıyla yapması.
SLO ve hata bütçesi
SLO (service level objective), sistemin ne kadar iyi çalışması gerektiğine dair ölçülebilir bir hedef. "Hızlı olsun" bir SLO değil; "istekler için p95 ≤ 500 ms ve hata oranı ≤ %1" bir SLO. Eğitim platformunun kapasite planında hedef tam olarak buydu. v1 bu hedefi yalnızca tek eşzamanlı kullanıcıda, saniyede 15,2 istekle karşılayabildi. v2 saniyede 4.708 isteğe kadar SLO içinde kaldı.
SLO'nun bir de erişilebilirlik tarafı var. %99,9 hedefi, 30 günlük bir ayda yaklaşık 43 dakikalık kesinti hakkı demek. Buna hata bütçesi deniyor. Bütçe bol kaldıysa daha cesur dağıtımlar yapılabilir; eriyorsa yeni özellik yerine kararlılığa odaklanmak gerekir. Hedefi %100 koymak ise pratikte hiçbir şey yapmamak demek.
Platformun provasında erişilebilirlik v1'de %75, v2'de %100 çıktı. Hedefi baştan koyduğunuz için bu iki sayı size tek bakışta bir şey söyler.
Uyarı yorgunluğu olmadan uyarı kurmak
Çok sayıda uyarı, hiç uyarı olmamasından sonra gelen en kötü durum. Gece üçte yanlış alarmla uyanan biri üçüncü seferde bildirimleri sessize alıyor. Eğitim platformundaki kurallarda uyguladığım birkaç ilke var.
Her kuralın bir "süre" koşulu olsun
Anlık bir sıçrama uyarı sebebi değil. Her kuralda bir eşik ve bir forSec değeri var; değer eşiğin üstünde o kadar süre kalırsa uyarı ateşleniyor:
{ id: "latency-p95", severity: "uyarı",
value: (s) => s.window?.p95 ?? null,
threshold: 1000, op: ">", forSec: 6, unit: "ms" },
{ id: "event-loop-lag", severity: "kritik",
value: (s) => s.window?.loopLag ?? null,
threshold: 500, op: ">", forSec: 4, unit: "ms" },
Prometheus kullanıyorsanız aynı şeyi uyarı kuralındaki for: alanıyla yapıyorsunuz. Kurallar saf fonksiyon olarak yazıldığı için testleri de var. Bir uyarı kuralını test etmek garip gelebilir, ama yanlış yazılmış bir eşik ancak gerçek bir kesintide ortaya çıkarsa çok geç kalmış olursunuz.
Belirtiye göre uyar, sebebe göre değil
CPU %90'da diye birini uyandırmak çoğu zaman gereksiz; kullanıcılar etkilenmiyor olabilir. Uyarılar kullanıcının hissettiği şeylere göre kuruluyor: erişilemezlik, hata oranı, yanıt süresi, sentetik kontrol. Bu projedeki yedi kuralın hepsi bu türden: API erişilemiyor, veritabanı hazır değil, p95 1 saniyenin üstünde (uyarı) ya da 3 saniyenin üstünde (kritik), olay döngüsü gecikmesi 500 ms'nin üstünde, hata oranı %2'nin üstünde, sentetik kontrol 2 saniyenin üstünde. CPU ve bellek panoda dururken uyarıya dönüşmüyor.
İki seviye yeter
Kritik: kesinti var, 5 dakika içinde müdahale. Uyarı: yavaşlama var, mesai içinde 1 saat, yoğun saatte 10 dakika içinde bakılır. Üçüncü, dördüncü seviyeler genelde kimsenin bakmadığı bir çöp kutusuna dönüşüyor.
Her uyarının bir ilk adımı olsun
Bir uyarı geldiğinde "şimdi ne yapacağım?" sorusunun cevabı yazılı değilse, o uyarı yarım kalmış demektir. Eğitim platformunun nöbet kılavuzunda her kural için ilk adımlar var. Örneğin olay döngüsü uyarısında önce hangi rotanın ortalamasının yükseldiğine bakılıyor, sonra son dağıtımdaki sorgular tam tarama için kontrol ediliyor.
Çözüldüğünü de bildir
Uyarı kapandığında süreyi ve en kötü değeri içeren bir "çözüldü" mesajı gönderiliyor. Bu hem nöbetçinin rahatlamasını sağlıyor hem de olay sonrası değerlendirmeye hazır bir zaman çizelgesi bırakıyor. v1 provasında dört olay kaydedildi, üçü kritikti. Her birinin başlangıç, tespit ve çözülme zamanları raporda duruyor.
Başlangıç için kontrol listesi
- Yanıt süresini ortalamayla değil p95 ve p99 ile izleyin.
- Gecikmeyi her zaman hata oranıyla birlikte okuyun.
- Kuyrukta bekleme süresini gösteren bir metrik ekleyin (Node.js'te olay döngüsü gecikmesi).
- En önemli kullanıcı akışı için dışarıdan çalışan bir sentetik kontrol kurun.
- Yazılı bir SLO belirleyin ve erişilebilirliği ona göre raporlayın.
- Her uyarı kuralına süre koşulu koyun, belirtiye göre uyarın, iki seviyeyle yetinin.
- Her kural için nöbet kılavuzuna ilk adımları yazın ve kuralları test edin.
Eğitim platformu çalışmasında bütün bunları framework ya da dış servis kullanmadan, birkaç yüz satırlık okunabilir kodla kurdum. Başlamak için pahalı bir APM aracına ihtiyacınız yok; neyi ölçeceğinizi bilmeniz yeterli.