Metrik ve log hattı (observability pipeline)
Problem
"Şirketin 5.000 pod'luk Kubernetes ortamı için merkezi bir metrik ve log platformu tasarla. Servisler metrik ve log üretir; mühendisler dashboard'larda son 15 dakikadan son bir yıla kadar metrik sorgular, alarm kurar ve bir hatayı araştırırken logları arar. Sistem, izlediği sistemler sorun yaşarken bile ayakta kalmalı."
Bu soru tipik bir yazma ağırlıklı veri hattıdır, ama iki farklı veri türünün çok farklı ekonomisi vardır: metrikler küçük, sayısal ve tahmin edilebilir; loglar büyük, metinsel ve pahalı. Mülakatçının dinlemek istediği konular: kapasite hesabının maliyeti nasıl belirlediği, zaman serisi veritabanının neden sıkıştırmaya dayandığı, kardinalite patlaması, downsampling ve saklama katmanları, Kafka'nın tampon rolü, log'ları tam metin indekslemek ile sadece etiketlerle indekslemek arasındaki maliyet farkı ve alarmın izlenen sistemden bağımsız kalması.
Kitap Bölüm 15 (RED/USE, Micrometer, Prometheus, SLO alarmları) ve Bölüm 23 (structured logging, trace id, merkezi log toplama) bu sistemin kullanıcısı gözünden anlatıldı. Bu case platformun kendisini tasarlar.
Netleştirme soruları ve varsayımlar
| Soru | Neden önemli | Varsayım |
|---|---|---|
| Kaç kaynak, ne kadar metrik? | Seri sayısı ve örnek hızı | 5.000 pod, pod başına ~2.000 aktif seri, 15 saniyede bir örnek |
| Log hacmi? | Depolama maliyeti | Pod başına ortalama 50 satır/s, satır ~500 B |
| Metrik saklama? | Downsampling ve katmanlar | Ham çözünürlük 15 gün, 5 dakikalık özet 1 yıl |
| Log saklama? | Sıcak/soğuk katman | Aranabilir 7 gün, arşiv 90 gün (denetim logları ayrı ve daha uzun) |
| Sorgu deseni? | İndeks tasarımı | Metrik: etiket filtresi + zaman aralığı. Log: servis, pod, seviye, trace id, bazen serbest metin |
| Alarm gecikmesi? | Alarmın nerede çalışacağı | Kritik alarmlar 1-2 dakika içinde |
| Çok kiracılı mı? | Kota ve izolasyon | Evet: ekip başına kota |
| Trace'ler kapsamda mı? | Kapsam | Hayır; sadece log ve metrikle bağlantı (trace id) |
Kapasite tahmini
Metrik:
aktif seri : 5.000 pod x 2.000 seri = 10M seri
örnek hızı : 10M / 15 s ≈ 667.000 örnek/s
sıkıştırılmış : örnek başına ~1,5 B (Prometheus dokümanı 1-2 B/örnek der)
667.000 x 86.400 x 1,5 B ≈ 86 GB/gün
ham, 15 gün : ≈ 1,3 TB; HA için iki kopya ≈ 2,6 TB
5 dk özet : 15 s -> 5 dk = 20 kat az örnek; ama her özet birkaç değer taşır
(min, max, toplam, sayı, ...) -> ~5 değer
86 GB / 20 x 5 ≈ 22 GB/gün -> 1 yıl ≈ 8 TB
Log:
satır hızı : 5.000 x 50 = 250.000 satır/s
ham hacim : 250.000 x 500 B = 125 MB/s ≈ 10,8 TB/gün
sıkıştırma : log metni tipik olarak ~10 kat sıkışır (varsayım) -> ~1,1 TB/gün
Kafka tamponu : 125 MB/s giriş, üretici sıkıştırmasıyla ~4 kat -> ~31 MB/s
24 saat saklama x replikasyon 3 -> 31 MB/s x 86.400 x 3 ≈ 8 TB
Log depolama, iki yaklaşım (7 gün aranabilir):
tam metin indeks (Elasticsearch benzeri), indeks ≈ ham boyut, 1 replika:
10,8 TB x 7 x 2 ≈ 150 TB sıcak disk
sadece etiket indeksi + nesne deposunda sıkıştırılmış parçalar:
1,1 TB x 7 ≈ 8 TB (90 gün ≈ 100 TB, ucuz nesne deposunda)
Teşhis:
- Metrikler ucuzdur, ama sadece kardinalite kontrol altındaysa. 10 milyon seri ile günde 86 GB yönetilebilir; bir ekibin metriğe
user_idetiketi eklemesi seri sayısını yüz katına çıkarabilir. - Loglar sistemin en büyük maliyet kalemidir. Günde 10 TB ham log, tam metin indekslendiğinde yüzlerce TB sıcak disk demektir. Hangi logun nasıl saklanacağı ve hangisinin hiç üretilmeyeceği bir mühendislik kararı olduğu kadar bir bütçe kararıdır.
- Yazma sürekli ve yüksek, okuma seyrek ve patlamalı: Incident sırasında herkes aynı anda büyük sorgular atar. Okuma yolunun yazma yolunu bozmaması gerekir.
API
Yazma tarafı:
Metrik : Prometheus scrape (pod'un /actuator/prometheus uç noktası, pull)
veya OTLP ile push (kısa ömürlü job'lar, sunucusuz ortamlar)
Log : pod stdout'una JSON satırı -> node üzerindeki toplayıcı ajan
{ "ts": "...", "level": "ERROR", "service": "order-service", "traceId": "4bf9...",
"message": "payment authorization timed out", "orderId": 98213 }
Okuma tarafı:
GET /api/v1/query_range?query=sum by (status) (rate(http_server_requests_seconds_count{service="order-service"}[5m]))&start=...&end=...&step=60
GET /api/v1/logs?selector={service="order-service",level="ERROR"}&contains=timed out&start=...&end=...&limit=500
GET /api/v1/logs?traceId=4bf92f3577b34da6a3ce929d0e0e4736
POST /api/v1/alert-rules
Metrik sorgu dili olarak PromQL, log sorgusu için etiket seçici + metin filtresi yeterlidir. Her sorgunun bir bütçesi vardır: en fazla seri sayısı, en fazla örnek, en fazla süre; bütçeyi aşan sorgu reddedilir.
Veri modeli
Zaman serisi:
seri kimliği = metrik adı + sıralı etiket kümesi
http_server_requests_seconds_count{service="order-service", method="POST", uri="/orders", status="201"}
örnekler = (zaman damgası ms, float64 değer) dizisi
- Ters indeks:
etiket=değer → seri id listesi.service="order-service" AND status="500"sorgusu iki listenin kesişimidir. - Chunk'lar: Her serinin örnekleri zamana göre parçalara (chunk) yazılır ve sıkıştırılır. Prometheus örnekleri iki saatlik bloklarda gruplar, bellekteki veriyi bir write-ahead log ile çökmeye karşı korur ve blokları arka planda daha büyük bloklara birleştirir (compaction).
Log:
akış (stream) = etiket kümesi {cluster, namespace, service, pod, level}
kayıt = (zaman damgası, satır)
saklama = akış başına zamana göre sıralı, sıkıştırılmış parçalar (nesne deposunda)
indeks = etiket kümesi -> parça listesi (+ isteğe bağlı: traceId -> parça)
Etiketler düşük kardinaliteli olmalıdır (servis, pod, seviye). orderId ya da traceId etiket değil, satırın içeriğidir; aranması gerekiyorsa ya metin filtresiyle taranır ya da ayrı, küçük bir indekse alınır.
Yüksek seviye tasarım
flowchart LR
subgraph CL[Her Kubernetes kümesi]
POD[Uygulama pod'ları] -->|stdout JSON| AG[Node log ajanı]
PR[Prometheus HA çifti] -->|scrape| POD
PR --> AM[Yerel alarm değerlendirme]
end
AM --> ALM[Alertmanager, tekilleştirme ve yönlendirme]
ALM --> ONC[On-call, e-posta, chat]
PR -->|remote write| MI[Metrik alım katmanı]
MI --> TS[(Uzun dönem TSDB, nesne deposunda bloklar)]
CMP[Compactor ve downsampling] --> TS
AG --> K[(Kafka log topic'leri)]
K --> LP[Log işleyiciler, ayrıştırma, maskeleme, zenginleştirme]
LP --> LS[(Log deposu, etiket indeksi ve nesne deposu)]
LP --> HOT[(Sıcak arama indeksi, seçili loglar)]
Q[Sorgu katmanı ve önbellek] --> TS
Q --> LS
Q --> HOT
UI[Dashboard ve log arama arayüzü] --> Qİki hat bilinçli olarak ayrıdır:
- Metrik hattı: Her kümede bir Prometheus çifti (aynı hedefleri bağımsız scrape eden iki kopya) son birkaç saati tutar ve alarmları yerelde değerlendirir. Örnekler uzun dönem depoya
remote_writeile akar; uzun dönem depo blokları nesne deposunda saklar, sorgu katmanı iki kopyadan gelen aynı serileri tekilleştirir. - Log hattı: Pod'lar stdout'a yazar, node ajanı (DaemonSet) satırları toplar, Kubernetes meta verisiyle etiketler ve Kafka'ya gönderir. Kafka, üretici ile depo arasında tampondur: depo yavaşlarsa veya bakımdaysa loglar Kafka'da bekler, ajanlar ve uygulamalar etkilenmez.
Derinlemesine
Zaman serisi depolama: neden bu kadar küçük?
Ham bir örnek 16 byte'tır (64 bit zaman damgası + 64 bit float). Facebook'un Gorilla çalışması, zaman serisi verisinin özelliklerinden yararlanan bir sıkıştırmayla ortalama örnek başına 1,37 byte'a indiğini raporlar:
- Zaman damgaları için delta-of-delta: Örnekler düzenli aralıklarla gelir (15 s, 15 s, 15 s). Ardışık farkların farkı neredeyse hep sıfırdır ve tek bit ile kodlanır; çalışmada zaman damgalarının yaklaşık %96'sı tek bite sığar.
- Değerler için XOR: Ardışık değerler genelde aynı ya da birbirine yakındır. Önceki değerle XOR alınır; aynıysa tek bit, değilse sadece anlamlı bitler ve baştaki/sondaki sıfır sayısı saklanır.
Prometheus dokümantasyonu da örnek başına ortalama 1-2 byte verir ve disk ihtiyacını saklama_süresi_sn x saniyedeki_örnek x örnek_başına_byte formülüyle hesaplamayı önerir; yukarıdaki kapasite hesabı bu formüldür. Mülakatta bu sıkıştırma fikrini bilmek, "metrikleri neden PostgreSQL'e yazmıyoruz?" sorusunun cevabıdır: satır başına onlarca byte yük ve index yerine, seri başına tek bir sıkıştırılmış akış.
Bir uyarı: Prometheus'un yerel deposu kümelenmiş ya da replike değildir; tek bir node'un disk veya node arızasına dayanmaz. Bu yüzden HA için iki bağımsız kopya ve uzun dönem saklama için replike, nesne deposu tabanlı bir arka uç kullanılır.
Kardinalite: metrik sistemlerinin bir numaralı düşmanı
Her farklı etiket kombinasyonu ayrı bir seridir ve her aktif seri bellekte indeks ve chunk yapısı tutar. Kardinalite çarpımsaldır:
http_requests_total{service, method, uri, status}
20 uri x 5 method x 10 status = 1.000 seri / pod
+ user_id etiketi (1 milyon aktif kullanıcı) = 1 milyara kadar seri (teorik üst sınır) -> sistem çöker
Kurallar:
- Sınırsız değerli alanlar etiket olmaz: kullanıcı id, sipariş id, e-posta, ham URL (path parametreleriyle), hata mesajı metni. Bunlar log'a ya da trace'e aittir.
- URI şablonla:
/orders/98213değil/orders/{id}. Spring Boot'un Micrometer entegrasyonuurietiketine şablonu yazar; elle oluşturulan metriklerde bu disiplin unutulur. - Alım katmanında koruma: Kiracı başına aktif seri limiti ve metrik başına seri limiti. Limiti aşan yeni seriler reddedilir, ekibe bildirim gider. Tek bir ekibin hatası bütün platformu düşürmemelidir.
- Görünürlük: En çok seri üreten metrik ve etiketlerin listesi; yeni deploy'dan sonra seri sayısında sıçrama alarmı.
Downsampling ve saklama katmanları
Bir yıllık bir grafikte 15 saniyelik çözünürlük gereksizdir; ekranda zaten birkaç yüz piksel vardır. Compactor eski blokları özetler:
| Yaş | Çözünürlük | Saklama yeri |
|---|---|---|
| 0-2 saat | Ham (15 s) | Prometheus belleği ve WAL |
| 2 saat - 15 gün | Ham | Nesne deposunda bloklar |
| 15 gün - 1 yıl | 5 dakikalık özet | Nesne deposu |
| 1 yıl üzeri | 1 saatlik özet veya silinir | Nesne deposu, ucuz katman |
Özet, tek bir ortalama değildir: her pencere için min, max, sum, count (ve sayaçlar için artış) tutulur. Sadece ortalama tutulursa bir yıl önceki bir gecikme sıçraması (max) görünmez olur ve rate() gibi fonksiyonlar yanlış sonuç verir. Sorgu katmanı, istenen zaman aralığına ve adım boyutuna göre doğru çözünürlüğü kendisi seçer.
Recording rule'lar sık kullanılan pahalı sorguları önceden hesaplar: sum by (service) (rate(http_requests_total[5m])) her dashboard yenilemesinde binlerce seriden değil, önceden hesaplanmış birkaç seriden okunur. SLO hesapları ve alarm kuralları da recording rule'lara dayanır (Kitap Bölüm 15).
Log hattı: toplama, tamponlama, işleme
- Uygulama: Structured JSON log, stdout'a. Dosyaya yazmak, rotasyon ve disk dolması problemlerini uygulamaya taşır. Log seviyesi disiplini (Kitap Bölüm 23):
ERRORgerçekten müdahale gerektiren,INFOiş olayları,DEBUGproduction'da kapalı ya da örneklenmiş. - Node ajanı: Konteyner log dosyalarını takip eder, satırlara pod, namespace ve servis etiketlerini ekler, kendi yerel disk tamponu vardır. Kafka geçici olarak erişilemezse diskte biriktirir; tampon da dolarsa önce
DEBUGsonraINFOsatırlarını düşürür ve kaç satır düşürdüğünü metrik olarak yayınlar. - Kafka: Topic'ler kiracı veya log sınıfına göre ayrılır (uygulama logları, denetim logları, erişim logları). Partition anahtarı sıra gerektirmediği için pod ya da rastgele; denetim logları gibi kayıpsız olması gerekenler için
acks=allvemin.insync.replicas=2(Kitap Bölüm 12). - İşleyiciler: JSON olmayan satırları ayrıştırır, kişisel veri ve sırları maskeler (kart numarası, token, e-posta), gürültülü tekrar eden satırları örnekler ya da sayaçla özetler, ve hedef depoya toplu yazar.
- Depo: Aşağıdaki iki yaklaşımdan biri ya da ikisinin karışımı.
Log deposu: tam metin indeks mi, etiket indeksi mi?
| Tam metin indeks (Elasticsearch/OpenSearch benzeri) | Etiket indeksi + nesne deposu (Loki benzeri) | |
|---|---|---|
| İndeks | Her kelime için ters indeks | Sadece etiket kümesi → parça listesi |
| Sorgu | Serbest metin araması çok hızlı | Etiketle daraltılır, sonra parçalar taranır (grep benzeri) |
| Maliyet | Yüksek: indeks ham veriye yakın boyutta, hızlı disk ve bellek | Düşük: sıkıştırılmış parçalar ucuz nesne deposunda |
| Yazma | İndeksleme CPU'su yüksek, refresh ve merge maliyeti | Ucuz: parçaları sıkıştır ve yükle |
| Uygun olduğu yer | Sık ve geniş serbest metin araması, güvenlik analizi | "Şu servisin şu zamandaki hataları", trace id ile arama |
Hesaptaki fark çarpıcıdır: 7 günlük aranabilir saklama için yaklaşık 150 TB sıcak disk ile yaklaşık 8 TB nesne deposu. Pratik bir karışım: bütün loglar etiket indeksli ucuz depoya; ERROR seviyesindeki loglar ve güvenlik/denetim logları ayrıca kısa süreli tam metin indeksine. Incident sırasında en çok sorulan soru "şu servisin son 30 dakikadaki hataları" ve "şu trace id'nin logları"dır; ikisi de etiketle daraltılabilir.
Trace id ile arama: Trace id etiket yapılmaz (her istek için farklı, sınırsız kardinalite). Bunun yerine log satırında durur; arama, servis ve zaman aralığıyla daraltılmış parçaları tarar. Daha hızlısı istenirse işleyici, trace id'den parça referansına küçük bir yan indeks (bloom filter ya da trace id → parça listesi) yazar. Metriklerden trace'e geçiş için de exemplar'lar kullanılır: bir histogram örneği, onu üreten isteğin trace id'sini taşır.
Alarm: izlenen sistemden bağımsız
Merkezi platform bir incident sırasında kendisi de yük altında kalabilir; alarmın ona bağımlı olması "her şey yanarken alarm gelmemesi" demektir. Bu yüzden:
- Kritik alarmlar kümede, yerel Prometheus'ta değerlendirilir. Merkezi hat gecikse bile alarm çalışır. Uzun dönem depo dashboard ve geçmiş analiz içindir.
- Alertmanager aynı alarmın HA çiftinden gelen iki kopyasını tekilleştirir, ilgili alarmları gruplar (100 pod'un aynı alarmı tek bildirim), bakım sırasında susturma (silence) ve bağımlılık bazlı bastırma (inhibition: "küme erişilemez" alarmı varken o kümenin servis alarmlarını bastırmak) uygular.
- Semptom bazlı ve burn rate alarmları: CPU'ya değil kullanıcının hissettiği hata oranı ve gecikmeye; SLO hata bütçesinin tükenme hızına (Kitap Bölüm 15).
- Meta-monitoring: "İzleme sistemini kim izler?" Sürekli tetiklenen bir "watchdog" alarmı, platformun dışındaki bir servise (ayrı bir bölge ya da harici bir sağlayıcı) gider; o alarm gelmeyi keserse dış servis on-call'u uyarır (dead man's switch).
Sorgu yolu ve korumalar
Incident sırasında yüzlerce mühendis aynı anda dashboard açar ve geniş sorgular atar. Koruma olmadan bir "son 30 günün bütün pod'ları" sorgusu sorgu katmanını ve depoyu düşürür:
- Sorgu bütçeleri: Seri sayısı, örnek sayısı, taranan byte ve süre limiti; aşan sorgu açık bir hata mesajıyla reddedilir.
- Sorgu bölme ve önbellek: Uzun aralıklı sorgular günlük parçalara bölünür, paralel çalışır ve tamamlanmış günlerin sonuçları önbelleğe alınır (geçmiş değişmez). Dashboard yenilemelerinde sadece son parça yeniden hesaplanır.
- Okuma ve yazma izolasyonu: Alım ve sorgu bileşenleri ayrı ölçeklenir; ağır sorgular alım yolunu yavaşlatmaz.
- Kiracı adaleti: Bir ekibin sorguları diğerlerinin kapasitesini tüketmemeli; kiracı başına eşzamanlılık sınırı.
Maliyet yönetimi bir tasarım konusudur
Log ve metrik maliyetleri büyüdükçe teknik değil organizasyonel bir problem haline gelir:
- Ekip başına kullanım görünürlüğü (seri sayısı, GB/gün log) ve kota.
DEBUGloglarının production'da kapalı olması; gerektiğinde servis bazında dinamik açılması (ve otomatik kapanması).- Yüksek hacimli ama düşük değerli logların (her başarılı sağlık kontrolü, her 200 erişim logu) örneklenmesi ya da metriğe dönüştürülmesi: "saniyede 10.000 başarılı istek logu" yerine bir sayaç.
- Saklama sürelerinin log sınıfına göre ayrılması: uygulama logları haftalar, denetim logları mevzuatın gerektirdiği süre.
Darboğazlar, hatalar ve trade-off'lar
| Senaryo | Etki | Önlem |
|---|---|---|
Bir ekip metriğe user_id ekledi | Seri sayısı patlar, alım ve bellek tükenir | Kiracı/metrik başına seri limiti, reddetme ve bildirim, kardinalite raporu |
| Log deposu yavaşladı | Alım geride kalır | Kafka tamponu; ajan disk tamponu; lag alarmı |
| Kafka erişilemez | Ajanlar biriktirir, sonra düşürür | Disk tamponu, önce düşük seviyeli logları düşürme, düşürme metriği |
| Incident sırasında sorgu fırtınası | Sorgu katmanı çöker | Sorgu bütçeleri, bölme ve önbellek, okuma/yazma izolasyonu |
| Merkezi platform çöktü | Dashboard yok | Kritik alarmlar kümede yerel; meta-monitoring ile dışarıdan tespit |
| Loglarda kişisel veri | Hukuki risk | İşleyicide maskeleme, uygulamada log disiplini, erişim denetimi |
| Prometheus node'u kaybedildi | Son birkaç saatin verisi bir kopyada eksik | HA çifti, sorgu katmanında tekilleştirme |
| Özetlenmiş veride sıçrama görünmüyor | Yanlış geçmiş analiz | Özetlerde min/max/sum/count tutmak |
Trade-off'lar:
- Pull ve push: Pull (scrape) hedeflerin sağlığını da gösterir ve merkezi kontrol sağlar; kısa ömürlü işler ve ağ kısıtları için push (OTLP) eklenir.
- Tam metin ve etiket indeksi: Serbest metin arama hızı karşılığında yaklaşık 20 kat depolama maliyeti; karışık model ile en sık soruları ucuz yoldan cevapladık.
- Çözünürlük ve süre: Bir yıllık geçmiş için 15 saniyelik detaydan vazgeçtik.
- Yerel alarm ve merkezi görünüm: Alarm güvenilirliği için değerlendirmeyi veriye yakın tuttuk; bedeli kümeler arası alarmlar için ayrı bir katman.
Üç seviyede cevap
Soru: "Bir geliştirici HTTP metriğine user_id etiketi eklemek istiyor. Ne dersin?"
seviye: Orta etiketler: [prometheus, cardinality] kaynak: Tahmini
Ekleyebilir, böylece hangi kullanıcının hata aldığını görürüz. Prometheus bu veriyi tutar.
Her farklı etiket değeri ayrı bir zaman serisi demek; milyonlarca kullanıcı milyonlarca seri yaratır ve metrik sistemi belleği tükenip çöker. Kullanıcı bazında bilgi log'a ya da trace'e aittir. Metrikte düşük kardinaliteli etiketler kullanırım: servis, endpoint şablonu, status.
Hayır derim ama nedenini sayıyla gösteririm: endpoint, method ve status ile pod başına bin civarı seri var; bir milyon aktif kullanıcıyla bu çarpımsal olarak milyarlara çıkar, her aktif seri bellekte indeks ve chunk tutar ve alım katmanı çöker. Geliştiricinin asıl ihtiyacını sorarım: "Belirli bir kullanıcı neden hata aldı?" sorusu trace ve log ile cevaplanır; trace id log satırında, metrikten trace'e geçiş için histogram exemplar'ları kullanırız. "Kaç farklı kullanıcı etkilendi?" sorusu ise log'dan türetilen bir sayı ya da olasılıksal bir yapıyla (HyperLogLog) cevaplanır. Platform tarafında bunu kişilerin dikkatine bırakmam: kiracı ve metrik başına seri limiti, limiti aşan yeni serileri reddetme ve ekibe bildirim, deploy sonrası seri sayısı sıçraması alarmı.
Junior kardinalite kavramını bilmiyor. Mid kavramı ve doğru alternatifi biliyor. Senior etkiyi sayıyla gösteriyor, geliştiricinin gerçek ihtiyacını ayrıştırıp doğru araca yönlendiriyor ve problemi tek kişinin dikkatine değil platform korumalarına bağlıyor. Platform ekiplerinde çalışacak adaylar için ayırt edici bir sorudur.
Takip sorusu: Seri limitine takılan ekip "production incident'ı var, limiti şimdi kaldırın" diyor. Ne yaparsın?
Soru: "Günde 10 TB log üretiyoruz. Nasıl saklar ve aranabilir yaparsın?"
seviye: İleri etiketler: [logging, storage, retention] kaynak: Tahmini
Logları Elasticsearch'e gönderirim, Kibana ile ararım. Disk dolarsa eski indeksleri silerim.
Uygulamalar stdout'a JSON yazar, node'daki ajan toplar, Kafka'ya gönderir, oradan Elasticsearch'e yazılır. Kafka tampon olur. İndeksleri günlük açar, eskileri silerim veya ucuz depoya taşırım. Gereksiz logları azaltırım.
Önce maliyeti hesaplarım: 10,8 TB/gün ham log, tam metin indekslendiğinde ve bir replikayla 7 gün için yaklaşık 150 TB sıcak disk. Sadece etiketlerle indeksleyip sıkıştırılmış parçaları nesne deposunda tutarsam aynı 7 gün yaklaşık 8 TB. Incident'ta en sık sorulan sorular "şu servisin şu aralıktaki hataları" ve "şu trace id'nin logları"; ikisi de etiketle daraltılıp taranabilir. Bu yüzden bütün logları etiket indeksli ucuz depoya, ERROR ve güvenlik loglarını ayrıca kısa süreli tam metin indeksine koyarım. Hattın ortasında Kafka tampon; ajanların disk tamponu var ve tampon dolarsa önce DEBUG'ı düşürüp düşürdüğünü metrikle bildirir. İşleyicide kişisel veriyi maskeler, tekrarlayan gürültüyü örneklerim. Ve en ucuz log üretilmeyen logdur: her başarılı isteğin logu yerine sayaç, production'da kapalı DEBUG ve ekip başına kullanım görünürlüğü.
Junior bir araç adıyla cevap veriyor, maliyeti hiç düşünmüyor. Mid doğru hattı kuruyor. Senior kararı maliyet hesabıyla veriyor, sorgu desenine göre ucuz ve pahalı katmanı ayırıyor, geri basınç ve kayıp politikasını tanımlıyor, maskeleme ve hacim azaltmayı tasarımın parçası yapıyor.
Takip sorusu: Güvenlik ekibi "son 90 günün bütün loglarında şu IP'yi ara" istiyor. Etiket indeksli depoda bu sorgu nasıl çalışır, maliyeti nedir?
Soru: "İzleme platformu çökerse alarmlar ne olur? Bunu nasıl tasarlarsın?"
seviye: İleri etiketler: [alerting, meta-monitoring] kaynak: Tahmini
İzleme sistemini yüksek erişilebilir kurarım, birden fazla kopyası olur, böylece çökmez.
Alarmları merkezi sisteme bağlı bırakmam; her kümede Prometheus alarmları yerelde değerlendirir ve Alertmanager'a gönderir. Merkezi depo sadece dashboard ve geçmiş için. Prometheus'u iki kopya çalıştırırım, Alertmanager aynı alarmı tekilleştirir.
Alarmın güvenilirliği, izlenen sistemlerin sorun yaşadığı anda en önemli; o an merkezi platform da yük altında olabilir. Bu yüzden kritik alarmları veriye en yakın yerde, kümedeki Prometheus HA çiftinde değerlendiririm; Alertmanager iki kopyadan gelen alarmı tekilleştirir, ilişkili alarmları gruplar ve "küme erişilemez" gibi kök alarm varken türev alarmları bastırır ki on-call yüz bildirimle boğulmasın. Alarmları semptom ve SLO burn rate üzerine kurarım. Son halka meta-monitoring: platform sürekli tetiklenen bir watchdog alarmı üretir ve bunu platformun dışındaki bağımsız bir servise gönderir; o alarm gelmeyi keserse dış servis on-call'u uyarır. Böylece "alarm gelmiyor çünkü her şey yolunda" ile "alarm gelmiyor çünkü alarm sistemi öldü" ayrılabilir.
Junior "yüksek erişilebilirlik" diyerek ortak arıza modunu (izleme sistemi ile izlenen sistemin aynı anda bozulması) görmüyor. Mid alarmı veriye yaklaştırıyor. Senior bunu gruplama, bastırma ve SLO tabanlı alarmlarla operasyonel hale getiriyor ve dead man's switch ile sessizliğin anlamını ayırt edilebilir kılıyor.
Takip sorusu: Alertmanager'ın kendisi düşerse watchdog alarmı bunu yakalar mı? Zinciri nasıl test edersin?
Mülakatçı için
- Seri sayısını, örnek hızını, log hacmini ve saklama süresini netleştirip kapasiteyi hesaplıyor.
- Metrik ve log hatlarını farklı ekonomileri nedeniyle ayrı tasarlıyor.
- Zaman serisi verisinin neden özel bir depoda tutulduğunu (sıkıştırma, seri bazlı yapı) açıklıyor.
- Kardinalite patlamasını bir örnekle gösteriyor ve platform seviyesinde koruma öneriyor.
- Downsampling öneriyor ve özetlerde min/max/sum/count tutma gerekliliğini açıklıyor.
- Log hattında tampon (Kafka), geri basınç ve kayıp/düşürme politikasını tanımlıyor.
- Tam metin indeks ile etiket indeksi arasında maliyet hesabına dayalı seçim yapıyor.
- Loglarda kişisel veri maskelemeyi ve hacim azaltmayı tasarımın parçası yapıyor.
- Alarmları merkezi platformdan bağımsız, veriye yakın değerlendiriyor; tekilleştirme ve gruplama öneriyor.
- İzleme sisteminin kendisinin izlenmesini (meta-monitoring) ele alıyor.
- Sorgu yolunu bütçeler ve önbellekle koruyor.
Takip soruları:
- Trace verisini de bu platforma eklesek örnekleme stratejin ne olur? (Kitap Bölüm 23.)
- Çok bölgeli bir şirkette metrikler tek merkezde mi toplanmalı, bölgede mi kalmalı?
- Histogram metriklerinde sabit kovalar ile yerel (native) histogramlar arasındaki fark ve kardinaliteye etkisi?
- Bir dashboard'un yüklenmesi 30 saniye sürüyor. Nereden başlarsın?
Seviye beklentisi: Mid seviyede scrape/remote write, ajan → Kafka → depo hattı, kardinalite kavramı ve yerel alarm beklenir. Senior'dan maliyet hesabına dayalı depolama kararları, downsampling'in doğru yapılması, geri basınç ve düşürme politikası, sorgu korumaları ve meta-monitoring beklenir. Staff seviyesinde aday kiracı kotaları ve maliyet görünürlüğü gibi organizasyonel mekanizmaları da tasarıma katar.
Hızlı tekrar
S: 10M seri, 15 saniyelik scrape ve örnek başına 1,5 byte ile günlük metrik hacmi?
C: 10M / 15 ≈ 667.000 örnek/s; x 86.400 x 1,5 B ≈ 86 GB/gün.
S: Gorilla sıkıştırmasının iki fikri nedir?
C: Zaman damgaları için delta-of-delta (düzenli aralıkta çoğu tek bit), değerler için önceki değerle XOR (aynı değer tek bit). Ortalama ~1,37 byte/örnek.
S: Kardinalite nedir, neden tehlikelidir?
C: Farklı etiket kombinasyonlarının sayısı; her kombinasyon ayrı seridir ve bellek tutar. user_id gibi sınırsız değerli bir etiket seri sayısını çarpımsal olarak patlatır.
S: Downsampling'de neden sadece ortalama tutulmaz?
C: Sıçramalar (max) kaybolur ve sayaç fonksiyonları yanlış hesaplanır. Her pencere için min, max, sum, count tutulur.
S: Log hattında Kafka'nın rolü nedir?
C: Üretici ile depo arasında tampon: depo yavaşlar veya bakımdaysa loglar bekler, ajanlar ve uygulamalar etkilenmez; işleyiciler bağımsız ölçeklenir.
S: Tam metin indeks ile etiket indeksli log deposunun maliyet farkı?
C: Bu case'in hesabında 7 gün için ~150 TB sıcak disk ile ~8 TB nesne deposu. Etiketle daraltılabilen sorgular için ikincisi yeterlidir.
S: Kritik alarmlar neden merkezi platformda değil kümede değerlendirilir?
C: Incident sırasında merkezi platform da yük altında kalabilir; alarmın izlenen sistemlerle ortak arıza modu olmamalı.
S: Dead man's switch nedir?
C: Sürekli tetiklenen bir watchdog alarmı platform dışındaki bir servise gönderilir; gelmeyi keserse alarm sisteminin kendisinin bozulduğu anlaşılır.
Kaynaklar
- Alex Xu, Sahn Lam, System Design Interview: An Insider's Guide (Cilt 2): "Metrics Monitoring and Alerting System" bölümü.
- Prometheus: Storage: İki saatlik bloklar, WAL, varsayılan 15 günlük saklama, örnek başına 1-2 byte, disk kapasitesi formülü, yerel deponun replike olmaması ve remote write.
- Pelkonen ve diğerleri, "Gorilla: A Fast, Scalable, In-Memory Time Series Database", VLDB 2015: Delta-of-delta ve XOR sıkıştırması, örnek başına 1,37 byte.
- Google, Site Reliability Engineering: İzleme ve semptom bazlı alarm bölümleri.
- Martin Kleppmann, Designing Data-Intensive Applications, Bölüm 11: Stream işleme ve log tabanlı mesajlaşma.