LocalAI Prometheus Metrikleri ile İzleme

Üretim ortamında bir LLM servisi çalıştırıyorsunuz ve sabah işe geldiğinizde ekipten birinin “dün gece modeller çok yavaş yanıt verdi” dediğini duyuyorsunuz. Logları karıştırıyorsunuz, systemd journal’a bakıyorsunuz ama somut bir şey bulamıyorsunuz. İşte tam bu noktada Prometheus metrikleri devreye giriyor. LocalAI, yerleşik Prometheus endpoint’i sayesinde ne zaman, ne kadar yük altında çalıştığını, hangi modelin ne kadar sürede yanıt verdiğini ve sistem kaynaklarını nasıl kullandığını size eksiksiz raporlayabiliyor.

Bu yazıda LocalAI’ın Prometheus entegrasyonunu sıfırdan kuracağız, anlamlı dashboard’lar oluşturacağız ve gerçek senaryolarda alarm mekanizmalarını nasıl ayarlayacağınızı göreceğim. Grafana da dahil olacak tabii ki.

LocalAI’da Metrik Sistemi Nasıl Çalışıyor

LocalAI, Go ile yazılmış bir servis ve Go ekosisteminin Prometheus client kütüphanesi olan prometheus/client_golang ile doğrudan entegre geliyor. Bu sayede herhangi bir ek yapılandırma olmaksızın /metrics endpoint’i aktif oluyor.

Varsayılan kurulumda LocalAI 8080 portunda çalışır ve metrikler şu adresten erişilebilir:

http://localhost:8080/metrics

Şimdi şunu doğrulayalım:

curl -s http://localhost:8080/metrics | head -50

Eğer LocalAI çalışıyorsa şuna benzer bir çıktı görürsünüz:

# HELP localai_api_calls_total Total number of API calls
# TYPE localai_api_calls_total counter
localai_api_calls_total{model="llama-2-7b",status="success"} 142
localai_api_calls_total{model="mistral-7b",status="success"} 89
localai_api_calls_total{model="mistral-7b",status="error"} 3
# HELP localai_api_request_duration_seconds Duration of API requests
# TYPE localai_api_request_duration_seconds histogram
localai_api_request_duration_seconds_bucket{model="llama-2-7b",le="1"} 12
localai_api_request_duration_seconds_bucket{model="llama-2-7b",le="5"} 98

LocalAI’ın sunduğu temel metrikler şunlardır:

  • localai_api_calls_total: Model ve durum (success/error) etiketiyle toplam API çağrısı sayısı
  • localai_api_request_duration_seconds: İstek süresi histogramı, model bazında
  • localai_tokens_generated_total: Toplam üretilen token sayısı
  • localai_tokens_per_second: Anlık token üretim hızı
  • localai_active_backends: Şu anda yüklenmiş ve aktif backend sayısı
  • localai_model_load_duration_seconds: Model yükleme süresi (cold start ölçümü için kritik)
  • localai_inference_duration_seconds: Sadece inference kısmının süresi (network overhead hariç)

Bu ayrım önemli: api_request_duration ile inference_duration arasındaki fark size network ve overhead maliyetini gösterir.

Docker Compose ile LocalAI ve Prometheus Kurulumu

Tipik bir üretim senaryosunu simüle edelim. LocalAI, Prometheus ve Grafana’yı birlikte ayağa kaldıracağız.

# docker-compose.yml
version: '3.8'

services:
  localai:
    image: quay.io/go-skynet/local-ai:latest-aio-cpu
    container_name: localai
    ports:
      - "8080:8080"
    volumes:
      - ./models:/build/models
      - ./localai-config:/build/config
    environment:
      - MODELS_PATH=/build/models
      - THREADS=8
      - CONTEXT_SIZE=4096
      - DEBUG=false
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 16G

  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.retention.time=30d'
      - '--web.enable-lifecycle'
    restart: unless-stopped

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    ports:
      - "3000:3000"
    volumes:
      - grafana_data:/var/lib/grafana
      - ./grafana/provisioning:/etc/grafana/provisioning
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=gizlisifre123
      - GF_USERS_ALLOW_SIGN_UP=false
    restart: unless-stopped

volumes:
  prometheus_data:
  grafana_data:

Şimdi Prometheus konfigürasyonunu yazalım:

# prometheus/prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s
  scrape_timeout: 10s

alerting:
  alertmanagers:
    - static_configs:
        - targets:
          - alertmanager:9093

rule_files:
  - "rules/localai_alerts.yml"

scrape_configs:
  - job_name: 'localai'
    static_configs:
      - targets: ['localai:8080']
    metrics_path: '/metrics'
    scrape_interval: 10s
    scrape_timeout: 8s
    honor_labels: true

  - job_name: 'node-exporter'
    static_configs:
      - targets: ['node-exporter:9100']
    scrape_interval: 30s

scrape_interval için 10 saniye iyi bir başlangıç noktası. LocalAI gibi inference yük altında çalışan servislerde 5 saniyeye kadar indirip anlık değişimleri daha iyi yakalayabilirsiniz, ama Prometheus’un kendi yüküne de dikkat edin.

Alerting Kuralları: Gerçekten Önemli Olanları Yakalayın

Alarm konusunda çoğu kişi aşırıya kaçıyor. Her şey için alarm kurunca “alarm yorgunluğu” başlıyor ve önemli bildirimler gürültüde kayboluyor. Şu kural setini genellikle yeterli buluyorum:

# prometheus/rules/localai_alerts.yml
groups:
  - name: localai_performance
    interval: 30s
    rules:
      # P95 yanıt süresi 30 saniyeyi geçerse
      - alert: LocalAIHighLatency
        expr: |
          histogram_quantile(0.95,
            rate(localai_api_request_duration_seconds_bucket[5m])
          ) > 30
        for: 5m
        labels:
          severity: warning
          team: platform
        annotations:
          summary: "LocalAI yüksek gecikme süresi"
          description: "{{ $labels.model }} modeli için P95 gecikme {{ $value | humanizeDuration }} seviyesinde, 5 dakikadır 30s üzerinde"

      # Hata oranı %10 üzerine çıkarsa
      - alert: LocalAIHighErrorRate
        expr: |
          rate(localai_api_calls_total{status="error"}[5m])
          /
          rate(localai_api_calls_total[5m])
          > 0.10
        for: 3m
        labels:
          severity: critical
          team: platform
        annotations:
          summary: "LocalAI hata oranı yüksek"
          description: "{{ $labels.model }} modeli hata oranı %{{ $value | humanizePercentage }}"

      # Token üretim hızı aniden düşerse (olası OOM veya model çöküşü)
      - alert: LocalAILowThroughput
        expr: |
          rate(localai_tokens_generated_total[5m]) < 5
          and
          rate(localai_api_calls_total[5m]) > 0
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "Token üretim hızı düşük"
          description: "Aktif istekler var ama token/s oranı {{ $value }} seviyesinde"

      # Hiç aktif backend yoksa
      - alert: LocalAINoActiveBackend
        expr: localai_active_backends == 0
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "LocalAI aktif backend yok"
          description: "Tüm model backend'leri çevrimdışı, servis kullanılamıyor olabilir"

for parametresine dikkat edin. Anlık spike’lar için alarm üretmemek, gerçek problemleri yakalamak için for değeri kritik. 2-5 dakikalık for değerleri çoğu senaryoda yeterli.

Grafana Dashboard Yapılandırması

Grafana’yı provisioning ile otomatik yapılandırmak, “elle kurdum, bir daha nasıl kurarım bilmiyorum” durumundan sizi kurtarır.

# grafana/provisioning/datasources/prometheus.yml
apiVersion: 1

datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    url: http://prometheus:9090
    isDefault: true
    editable: true
    jsonData:
      timeInterval: "10s"
      queryTimeout: "60s"
      httpMethod: POST

Dashboard’ları JSON olarak da provisioning edebilirsiniz ama önce hangi PromQL sorgularının işe yaradığını bilmeniz gerekiyor. Grafana’da kullanacağınız temel sorgular:

İstek başarı oranı (son 5 dakika):

rate(localai_api_calls_total{status="success"}[5m])
/
rate(localai_api_calls_total[5m])
* 100

Model bazında P50, P90, P99 gecikme:

histogram_quantile(0.99,
  sum(rate(localai_api_request_duration_seconds_bucket[5m])) by (le, model)
)

Saatlik token üretim toplamı:

increase(localai_tokens_generated_total[1h])

Model yükleme süreleri (cold start analizi):

localai_model_load_duration_seconds

Bu son sorgu özellikle disk I/O optimizasyonu yaparken değerli. Hangi modelin kaç saniyede yüklendiğini görmek, SSD vs HDD farkını veya model quantization’ın yükleme süresine etkisini ölçmenizi sağlar.

Node Exporter ile Sistem Metriklerini Bağlamak

LocalAI metrikleri tek başına yeterli değil. Bir modelin yavaş yanıt vermesinin sebebi inference algoritması olmayabilir, CPU throttling ya da bellek baskısı da olabilir. Node Exporter’ı da karışıma katmalısınız:

# docker-compose.yml'e ekleyin
  node-exporter:
    image: prom/node-exporter:latest
    container_name: node-exporter
    ports:
      - "9100:9100"
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    command:
      - '--path.procfs=/host/proc'
      - '--path.rootfs=/rootfs'
      - '--path.sysfs=/host/sys'
      - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
    restart: unless-stopped
    network_mode: host

Node Exporter’ı network_mode: host ile çalıştırmanın nedeni, container network’ü yerine gerçek host metriklerini görmek. Özellikle GPU sunucularda bu önemli çünkü container içinden bazı sistem metrikleri doğru görünmeyebilir.

GPU kullanıyorsanız nvidia-smi çıktısını Prometheus’a aktaran nvidia-gpu-exporter veya dcgm-exporter da kurmanız gerekiyor. Bu olmadan GPU inference senaryolarında yarım kalmış bir görüntünüz olur.

Gerçek Dünya Senaryosu: Yük Testi ve Metrik Analizi

Bir müşteri projesinde 3 farklı model servis ediyorduk: mistral-7b-instruct, llama2-13b-chat ve küçük bir phi-2. Akşam 18:00-22:00 arası kullanım pikini göstermek için basit bir yük testi kurdum:

#!/bin/bash
# load_test.sh - Basit bir LocalAI yük test scripti

ENDPOINT="http://localhost:8080/v1/chat/completions"
MODELS=("mistral-7b-instruct" "llama2-13b-chat" "phi-2")
CONCURRENT=5

test_model() {
    local model=$1
    local payload=$(cat <<EOF
{
  "model": "$model",
  "messages": [
    {"role": "user", "content": "Linux sistem yönetiminde en kritik 5 güvenlik önlemini listele"}
  ],
  "max_tokens": 200,
  "temperature": 0.7
}
EOF
)
    
    start_time=$(date +%s%N)
    response=$(curl -s -w "n%{http_code}" -X POST "$ENDPOINT" 
        -H "Content-Type: application/json" 
        -d "$payload")
    end_time=$(date +%s%N)
    
    http_code=$(echo "$response" | tail -1)
    duration=$(( (end_time - start_time) / 1000000 ))
    
    echo "Model: $model | Status: $http_code | Süre: ${duration}ms"
}

# Paralel test
for i in $(seq 1 $CONCURRENT); do
    for model in "${MODELS[@]}"; do
        test_model "$model" &
    done
done

wait
echo "Yük testi tamamlandı"

Bu test çalışırken Prometheus’tan şu sorguyu izleyin:

# Anlık aktif istek sayısını kontrol et
curl -s "http://localhost:9090/api/v1/query?query=rate(localai_api_calls_total[1m])" | 
  python3 -m json.tool | grep value

Yük testi sonrası fark ettiğimiz şey ilginçti: llama2-13b-chat için P95 gecikme 45 saniyenin üzerine çıkıyordu ama phi-2 3 saniyenin altında kalıyordu. Prometheus’taki histogram verileri bunu net gösterdi ve modeli GGUF Q4_K_M formatından Q5_K_M formatına geçirme kararını bu veriye dayanarak verdik.

Metrik Verilerini Script ile Sorgulamak

Bazen Grafana’yı açmak yerine terminal’den hızlıca bir kontrol yapmak istersiniz. Prometheus HTTP API’sini doğrudan kullanabilirsiniz:

#!/bin/bash
# localai_health_check.sh

PROM_URL="http://localhost:9090"

echo "=== LocalAI Sağlık Durumu Raporu ==="
echo "Tarih: $(date)"
echo ""

# Toplam başarılı istek sayısı (son 1 saat)
echo "--- Son 1 Saatlik İstatistikler ---"
SUCCESS=$(curl -sg "${PROM_URL}/api/v1/query?query=increase(localai_api_calls_total{status='success'}[1h])" | 
  python3 -c "import sys,json; data=json.load(sys.stdin); print(sum(float(r['value'][1]) for r in data['data']['result']))")
echo "Başarılı istek: $SUCCESS"

# Hata sayısı
ERROR=$(curl -sg "${PROM_URL}/api/v1/query?query=increase(localai_api_calls_total{status='error'}[1h])" | 
  python3 -c "import sys,json; data=json.load(sys.stdin); print(sum(float(r['value'][1]) for r in data['data']['result']))")
echo "Hatalı istek: $ERROR"

# P95 gecikme
P95=$(curl -sg "${PROM_URL}/api/v1/query?query=histogram_quantile(0.95,rate(localai_api_request_duration_seconds_bucket[5m]))" | 
  python3 -c "import sys,json; data=json.load(sys.stdin); results=data['data']['result']; print(f'{float(results[0]["value"][1]):.2f}s' if results else 'Veri yok')")
echo "P95 Gecikme: $P95"

# Aktif backend sayısı
BACKENDS=$(curl -sg "${PROM_URL}/api/v1/query?query=localai_active_backends" | 
  python3 -c "import sys,json; data=json.load(sys.stdin); results=data['data']['result']; print(results[0]['value'][1] if results else '0')")
echo "Aktif Backend: $BACKENDS"

echo ""
echo "=== Rapor Sonu ==="

Bu scripti cron’a ekleyip sabahları mail ile raporlayabilirsiniz ya da monitoring sistemlerinizle entegre edebilirsiniz:

# Crontab - her sabah 08:00'de rapor gönder
0 8 * * * /opt/scripts/localai_health_check.sh | mail -s "LocalAI Günlük Rapor" [email protected]

Uzun Süreli Veri Saklama ve Thanos Entegrasyonu

Prometheus varsayılan olarak 15 günlük veri saklar (yapılandırmanızda 30 gün yaptık). Eğer aylık trend analizi yapmak istiyorsanız Thanos veya Cortex gibi uzun süreli depolama çözümlerine bakmanız gerekiyor.

Basit Thanos Sidecar kurulumu için:

# docker-compose.yml'e eklenecek Thanos sidecar
  thanos-sidecar:
    image: thanosio/thanos:latest
    container_name: thanos-sidecar
    command:
      - sidecar
      - --tsdb.path=/prometheus
      - --prometheus.url=http://prometheus:9090
      - --grpc-address=0.0.0.0:10901
      - --http-address=0.0.0.0:10902
      - --objstore.config-file=/etc/thanos/objstore.yml
    volumes:
      - prometheus_data:/prometheus
      - ./thanos/objstore.yml:/etc/thanos/objstore.yml
    depends_on:
      - prometheus
# thanos/objstore.yml - S3 uyumlu depolama için
type: S3
config:
  bucket: localai-metrics
  endpoint: s3.amazonaws.com
  region: eu-central-1
  access_key: ${AWS_ACCESS_KEY}
  secret_key: ${AWS_SECRET_KEY}

Küçük ekipler için MinIO ile kendi S3 uyumlu depolamanızı kurabilirsiniz. Bu kombinasyon, LocalAI metriklerini yıllarca saklamanızı ve mevsimsel kullanım örüntülerini analiz etmenizi sağlar.

Dikkat Edilmesi Gereken Noktalar

Birkaç pratik uyarı ile bitirelim:

  • Cardinality kontrolü: LocalAI her model için ayrı etiket oluşturuyor. Çok sayıda model çalıştırıyorsanız (özellikle fine-tuned versiyonlar) Prometheus’un bellek kullanımı artabilir. prometheus_tsdb_symbol_table_size_bytes metriğini takip edin.
  • Scrape timeout ayarı: LocalAI yoğun inference yaparken /metrics endpoint’i yanıt vermekte gecikmez ama ihtiyatlı olmak için scrape_timeout değerini scrape_interval‘dan biraz düşük tutun.
  • Güvenlik: /metrics endpoint’i varsayılan olarak herkese açık. Üretimde Nginx ile basic auth veya network policy ile sadece Prometheus’un erişmesine izin verin.
  • Model label’larını standartlaştırın: LocalAI konfigürasyon dosyasındaki model adları ile Prometheus’ta görülen etiketler birebir eşleşiyor. Ekip içinde tutarlı bir isimlendirme politikası olmadan metrikler karmaşıklaşıyor.

Sonuç

LocalAI’ın Prometheus entegrasyonu, “acaba model çalışıyor mu?” sorusunu “model şu an 12 token/saniye üretiyor, P95 gecikme 8 saniye, son 1 saatte 3 hata aldı” düzeyine taşıyor. Bu fark, reaktif sorun çözmekten proaktif kapasite planlamasına geçmenizi sağlıyor.

Kurulumun karmaşık bir yanı yok, gerçekten. 30 dakikada temel stack’i ayağa kaldırabilir, birkaç saatte anlamlı alarm kuralları yazabilirsiniz. Zor olan kısım, hangi metriklerin sizin senaryonuzda önemli olduğunu anlamak. Bunu da ancak veriyle oynayarak, yük testleri yaparak ve “o gece ne olmuştu” sorularını metrik verileriyle cevaplayarak öğreniyorsunuz.

Eğer GPU inference yapıyorsanız DCGM Exporter’ı da bu stack’e ekleyin ve GPU utilization ile inference süresini birlikte grafikleyin. O görüntü, donanım yatırımı kararlarını çok daha kolay gerekçelendirmenizi sağlıyor.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir