← blog · 9 Ekim 2026

Prometheus'ta Kardinalite Patlaması: Etiket Seçimi, Serinin Maliyeti ve Sorunu Kaynağında Durdurmak

Her benzersiz etiket birleşimi ayrı bir seridir ve tek bir sınırsız etiket Prometheus'u belleksiz bırakır. Seri sayısını ölçmek, kapalı küme kuralı, kazıma sınırları, labeldrop çakışması, histogramların maliyeti ve kayıt kurallarının çözmediği şey.

Prometheus'ta bir metrik adı ile etiket değerlerinin her benzersiz birleşimi ayrı bir zaman serisidir. http_requests_total{method="GET", route="/api/orders", status="200"} bir seri, aynı metriğin status="500" hâli başka bir seridir. Bu model ucuz ve hızlıdır, ta ki biri etikete sınırsız bir değer koyana kadar: kullanıcı kimliği, ham URL yolu, hata mesajı. O andan sonra seri sayısı trafikle birlikte büyür, Prometheus'un belleği onunla birlikte şişer, sorgular yavaşlar ve bir gece sunucu bellek yetersizliğinden yeniden başlar. Bu yazı serilerin nereden geldiğini, nasıl ölçüleceğini ve nerede durdurulacağını anlatıyor.

Seri sayısı nasıl çarpılır

Seri sayısı, etiketlerin aldığı değer sayılarının çarpımıdır. 5 yöntem, 40 rota ve 8 durum kodu olan bir uygulama en fazla 1.600 seri üretir; 20 pod üzerinden kazındığında 32.000 olur, çünkü pod ya da instance etiketi de bir çarpandır. Bu hâlâ sorun değildir. Rotanın yerine ham yolu koyduğunuzda (/api/orders/81723) çarpanlardan biri sonsuz olur.

Histogramlar çarpanı büyütür. Bir histogram her etiket birleşimi için kova sayısı kadar _bucket serisi, artı _sum ve _count üretir. Go istemcisinin varsayılan 11 sınırı +Inf ile birlikte 12 kova eder; yani her birleşim 14 seridir. Yukarıdaki örnek bir sayaç değil de histogram olsaydı 32.000 değil 448.000 seri olurdu.

Üçüncü kaynak devingenliktir (churn). Her dağıtımda yeni adlı podlar gelir; eski podların serileri yeni örnek almaz ama hemen silinmez de. Prometheus son birkaç saatin verisini bellekteki baş bloğunda (head block) tutar ve eski seriler orada bir sonraki sıkıştırmaya kadar yaşar. Günde birkaç kez dağıtım yapan bir servis ya da her koşusunda yeni pod açan bir iş, aktif seri sayısını sabit trafikte bile katlar.

Önce ölçün

Toplam aktif seri sayısını Prometheus kendisi verir: prometheus_tsdb_head_series. Hangi metriğin ve hangi etiketin sorumlu olduğunu arayüzdeki Status > TSDB Status sayfası (ya da /api/v1/status/tsdb ucu) listeler: en çok seriye sahip metrik adları, en çok değer alan etiket adları, en çok bellek tutan etiketler ve en çok seriyi taşıyan etiket değer çiftleri. Diskteki veri için promtool tsdb analyze aynı incelemeyi daha ayrıntılı yapar ve devingenliği de gösterir.

Sorgu tarafında:

topk(10, count by (__name__) ({__name__=~".+"}))
count(count by (path) (http_requests_total))
sum by (job) (scrape_series_added)

İlki en çok seriye sahip metrikleri verir, ama bütün serilere dokunduğu için pahalıdır; sık çalıştırmayın. İkincisi bir etiketin kaç farklı değer aldığını sayar. Üçüncüsü her kazımada eklenen yeni seri sayısını iş bazında gösterir; sabit trafikte sürekli yüksek kalıyorsa bir yerde devingen bir etiket saklanıyordur.

Etiket seçimi: kapalı küme kuralı

Bir etiketin değeri önceden bilinen, küçük ve kapalı bir kümeden gelmelidir. Yöntem, durum sınıfı, rota şablonu, bölge ve kuyruk adı uygundur. Kullanıcı kimliği, binlerce olabilecek kiracı kimliği, e-posta adresi, istek ve oturum kimlikleri, ham URL yolu, sorgu dizesi, hata mesajı metni, zaman damgası ve IP adresi uygun değildir.

Rota için çatınızın eşleştirdiği şablonu kullanın (/api/orders/{id}), ham yolu değil. Eşleşmeyen istekleri tek bir sabit değerde toplayın (route="unmatched"): aksi hâlde rastgele yollar deneyen bir tarayıcı bot her denemesi için yeni bir seri açar ve izleme sisteminizi trafikle besleyerek düşürebilir. Durum kodunda 2xx, 4xx, 5xx sınıfı çoğu pano için yeterlidir.

Tekil olayın ayrıntısı (hangi kullanıcı, hangi istek, hangi hata metni) metriğe değil, loga ya da ize aittir. İkisini bağlamak için exemplar kullanılabilir: histogram gözlemine bir iz kimliği iliştirilir ve panodan doğrudan o isteğin izine geçilir. Prometheus'ta exemplar depolama, sürümünüze göre ayrıca açılması gereken bir özellik olabilir.

Kaynağında durdurmak: kazıma sınırları

Uygulamaya hatalı bir etiket girdiğinde siz fark etmeden sunucunun düşmemesi için kazıma yapılandırmasına üst sınır koyun:

scrape_configs:
  - job_name: app
    sample_limit: 20000
    label_limit: 30
    label_value_length_limit: 200
    metric_relabel_configs:
      - source_labels: [__name__]
        regex: "http_client_request_duration_seconds_bucket"
        action: drop
      - regex: "request_id|session_id"
        action: labeldrop

sample_limit aşıldığında o kazıma bütünüyle başarısız sayılır: hedefin hiçbir metriği gelmez ve up sıfıra düşer. Bu bilinçli bir seçimdir, yarım veri yerine gürültülü bir arıza. Ama yalnızca up == 0 için alarmınız varsa işe yarar; nedenini hemen görmek için prometheus_target_scrapes_exceeded_sample_limit_total sayacını da izleyin.

metric_relabel_configs kazımadan sonra, depolamadan önce çalışır; düşürülen seri hiç yazılmaz. Yukarıdaki ilk kural, kimsenin bakmadığı bir histogramın kovalarını atar; _sum ve _count kaldığı için ortalama yine hesaplanabilir. Etiket düşürürken dikkatli olun: yalnızca o etiketle ayrışan iki seri labeldrop sonrasında aynı kimliğe düşer ve Prometheus aynı zaman damgalı ikinci örneği reddeder. Örneklerden biri kaybolur ve bunu yalnızca prometheus_target_scrapes_sample_duplicate_timestamp_total sayacında ve logda görürsünüz. Düşürdüğünüz etiketin hiçbir seriyi ayırt etmediğinden emin değilseniz düzeltmeyi uygulamada yapın.

Kubernetes'te kubelet ve cAdvisor gibi hazır dışa aktarıcılar, panolarınızın hiç kullanmadığı yüzlerce metrik yayar. Panolarda ve alarm kurallarında geçen metriklerin listesini çıkarıp geri kalanını kazıma sırasında düşürmek, çoğu kümede aktif seri sayısını belirgin biçimde azaltır.

Kayıt kuralları neyi çözer, neyi çözmez

Kayıt kuralları (recording rules) pahalı bir toplamayı önceden hesaplar:

groups:
  - name: http
    rules:
      - record: job_route:http_requests:rate5m
        expr: sum by (job, route, code) (rate(http_requests_total[5m]))

Panolar hızlanır, ama ham seriler yine depolanır; yani bellek sorunu çözülmez, üstüne yeni seriler eklenir. Seri sayısını gerçekten azaltan yollar, etiketi uygulamada kaldırmak, kazıma sırasında düşürmek ya da uzak depoya yazmadan önce toplayan ayrı bir katmandır.

Histogramları ayrıca düşünün

Kardinalitenin en hızlı büyüdüğü yer histogramlardır. Varsayılan kova listesini körlemesine kullanmak yerine kovaları SLO eşiklerinize göre seçin: 300 ms hedefiniz varsa o eşiğin çevresinde sık, uzaklarda seyrek kovalar işinizi görür. Histogramlara sayaçlardan daha az etiket verin. Yeni Prometheus sürümleri kova başına ayrı seri yerine tek seri tutan yerel (native) histogramlar sunar; bunlara güvenmeden önce sürümünüzde kararlı olup olmadığına ve istemci kütüphanenizin destekleyip desteklemediğine bakın.

Tuzaklar

Uzak depoda fatura. Yönetilen metrik hizmetleri çoğunlukla aktif seri sayısına göre ücretlendirir. Yerelde bellek sorunu olan bir etiket uzak depoda doğrudan faturaya dönüşür ve biri fark ettiğinde ayın yarısı geçmiş olabilir.

Kısa ömürlü işler. Her koşusunda yeni pod açan zamanlanmış işler ancak birkaç kez kazınır, ama her biri arkasında yeni bir seri kümesi bırakır. Bu tür işlerin metriklerini toplandıkları noktada pod etiketi olmadan tutun.

Kütüphane varsayılanları. Bazı HTTP ara katmanları rota yerine ham yolu etikete koyar ya da istemci tarafında hedef URL'yi etiket yapar. Yeni bir kütüphane eklediğinizde ilk iş onun /metrics çıktısına bakmak olsun.

Sınırsız görünmeyen değerler. Durum kodu kapalı bir küme gibi görünür, ama arka uçtan gelen kodu olduğu gibi geçiren bir vekil yüzlerce standart dışı kod üretebilir. Kuyruklar kiracı başına açılıyorsa kuyruk adı da sınırsızdır.

Prometheus'un doğru araç olmadığı yer

"Hangi kullanıcı kaç istek attı" ya da "hangi müşterinin hangi ucu yavaş" gibi soruların cevabı doğası gereği yüksek kardinaliteli veridir. Bunları Prometheus etiketine sıkıştırmaya çalışmak yerine yapılandırılmış loglara ya da olay verisi için tasarlanmış sütun tabanlı bir depoya yazın. Metrikler bir şeyin ters gittiğini ve ne kadar ters gittiğini söylesin; kimin için ters gittiğini loglar ve izler söylesin.

Kısa kontrol listesi

Her etiketin değer kümesi küçük ve kapalı. Rotalar ham yol değil şablon, eşleşmeyen istekler tek bir değerde toplanıyor. Histogramların kovaları ve etiketleri bilerek seçilmiş. Her kazıma işinde sample_limit ve etiket sınırları tanımlı, aşılmaları alarm üretiyor. prometheus_tsdb_head_series bir panoda duruyor ve beklenmedik artışta alarm veriyor. Yeni bir kütüphane ya da dışa aktarıcı eklendiğinde ilk bakılan şey ürettiği seri sayısı. Bu düzen oturduğunda kardinalite, gece yarısı gelen bir bellek yetersizliği olmaktan çıkar ve bir pull request incelemesinde yakalanan sıradan bir konuya dönüşür.