Sistem tasarımı

Metrik ve log hattı (observability pipeline)

Zor~60 dkGoogleAmazonMetaMicrosoftDatadogUber
Cevaplar:

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

SoruNeden önemliVarsayı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 maliyetiPod başına ortalama 50 satır/s, satır ~500 B
Metrik saklama?Downsampling ve katmanlarHam çözünürlük 15 gün, 5 dakikalık özet 1 yıl
Log saklama?Sıcak/soğuk katmanAranabilir 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 izolasyonEvet: ekip başına kota
Trace'ler kapsamda mı?KapsamHayır; sadece log ve metrikle bağlantı (trace id)

Kapasite tahmini

text
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_id etiketi 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ı:

text
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ı:

http
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:

text
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:

text
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

diyagram yükleniyor…
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_write ile 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:

text
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/98213 değil /orders/{id}. Spring Boot'un Micrometer entegrasyonu uri etiketine ş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ükSaklama yeri
0-2 saatHam (15 s)Prometheus belleği ve WAL
2 saat - 15 günHamNesne deposunda bloklar
15 gün - 1 yıl5 dakikalık özetNesne deposu
1 yıl üzeri1 saatlik özet veya silinirNesne 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

  1. Uygulama: Structured JSON log, stdout'a. Dosyaya yazmak, rotasyon ve disk dolması problemlerini uygulamaya taşır. Log seviyesi disiplini (Kitap Bölüm 23): ERROR gerçekten müdahale gerektiren, INFO iş olayları, DEBUG production'da kapalı ya da örneklenmiş.
  2. 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 DEBUG sonra INFO satırlarını düşürür ve kaç satır düşürdüğünü metrik olarak yayınlar.
  3. 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=all ve min.insync.replicas=2 (Kitap Bölüm 12).
  4. İş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.
  5. 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)
İndeksHer kelime için ters indeksSadece etiket kümesi → parça listesi
SorguSerbest metin araması çok hızlıEtiketle daraltılır, sonra parçalar taranır (grep benzeri)
MaliyetYüksek: indeks ham veriye yakın boyutta, hızlı disk ve bellekDüşük: sıkıştırılmış parçalar ucuz nesne deposunda
Yazmaİndeksleme CPU'su yüksek, refresh ve merge maliyetiUcuz: parçaları sıkıştır ve yükle
Uygun olduğu yerSı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.
  • DEBUG logları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

SenaryoEtkiÖnlem
Bir ekip metriğe user_id eklediSeri sayısı patlar, alım ve bellek tükenirKiracı/metrik başına seri limiti, reddetme ve bildirim, kardinalite raporu
Log deposu yavaşladıAlım geride kalırKafka tamponu; ajan disk tamponu; lag alarmı
Kafka erişilemezAjanlar biriktirir, sonra düşürürDisk tamponu, önce düşük seviyeli logları düşürme, düşürme metriği
Incident sırasında sorgu fırtınasıSorgu katmanı çökerSorgu bütçeleri, bölme ve önbellek, okuma/yazma izolasyonu
Merkezi platform çöktüDashboard yokKritik alarmlar kümede yerel; meta-monitoring ile dışarıdan tespit
Loglarda kişisel veriHukuki riskİşleyicide maskeleme, uygulamada log disiplini, erişim denetimi
Prometheus node'u kaybedildiSon birkaç saatin verisi bir kopyada eksikHA çifti, sorgu katmanında tekilleştirme
Özetlenmiş veride sıçrama görünmüyorYanlış 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?"

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?"

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?"

İ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.