18 Aralık 2025 Perşembe

Web Sitelerinde Core Web Vitals İyileştirme Rehberi: INP, LCP ve CLS’yi Gerçekten Düşürmek

Core Web Vitals neden hâlâ önemli?

Google’ın Core Web Vitals metrikleri (LCP, CLS ve güncel olarak FID yerine geçen INP) sadece “SEO puanı” için değil, ziyaretçinin sayfayı akıcı kullanabilmesi için de kritik. Özellikle mobil trafikte, birkaç yüz milisaniyelik gecikme bile etkileşimi düşürüyor. Bu rehberde, geliştirici araçlarıyla sorunu bulup, kod seviyesinde uygulanabilir optimizasyonları adım adım ele alacağım.

1) Ölçmeden iyileştirme olmaz: Doğru metrik kaynağını seçin

İlk hata, yalnızca laboratuvar testlerine bakıp (Lighthouse gibi) gerçek kullanıcı verisini (field data) göz ardı etmek. Başlangıç için şu kaynakları birlikte kullanın: PageSpeed Insights (CrUX saha verisi + lab), Chrome DevTools Performance (iz sürme), WebPageTest (farklı cihaz/ağ senaryoları) ve mümkünse RUM (Real User Monitoring) ile gerçek ziyaretçiden telemetri toplama. Hedefiniz net olmalı: LCP < 2,5 sn, CLS < 0,1 ve INP < 200 ms.

2) LCP (Largest Contentful Paint): “En büyük” içeriği hızlandırın

LCP çoğu sitede “hero” görseli, başlık alanı veya üstteki büyük bir kart bileşeni olur. LCP’yi iyileştirmenin ana fikri şudur: kritik içeriğe giden yolu kısaltın. Önce DevTools’ta Performance kaydı alın, “Timings” ve “LCP” işaretini bulun; ardından LCP öğesinin ne olduğunu tespit edin.

Uygulanabilir adımlar: 1) Sunucu yanıt süresini düşürün (TTFB). CDN kullanın, HTML’i önbelleğe alın, dinamik sorguları azaltın. 2) Kritik CSS’i öne alın; sayfanın üst kısmını boyamak için gereken CSS’i geciktirmeyin. 3) Render-blocking kaynakları azaltın: büyük JS paketlerini başlangıçta yüklemek LCP’yi uzatır. 4) Web fontları için doğru stratejiyi seçin: font-display: swap gibi ayarlar, metnin beklemesini azaltır.

3) CLS (Cumulative Layout Shift): Kaymaların kaynağını kapatın

CLS çoğu zaman “reklam alanı sonradan geldi”, “görsel boyutu belirtilmedi”, “font geç yüklendi ve satırlar oynadı” gibi sebeplerle yükselir. Sorunun güzelliği şu: Genellikle birkaç net düzeltmeyle ciddi düşer.

Uygulanabilir adımlar: 1) Görseller ve iframe’ler için genişlik/yükseklik belirtin ya da CSS ile en-boy oranını sabitleyin. 2) Üst kısma dinamik banner ekliyorsanız yer tutucu alan ayırın. 3) Geç yüklenen fontlarda satır kaymasını azaltmak için benzer metriklere sahip font fallback’leri seçin. 4) “Yukarıdan açılan” çerez bildirimi gibi bileşenleri sayfa içeriğini itmek yerine, sabit konumda (overlay) tasarlamayı değerlendirin.

4) INP (Interaction to Next Paint): Etkileşim gecikmesini azaltmanın kısa yolu

INP, kullanıcının tıklama/dokunma/klavye gibi etkileşiminden sonra arayüzün bir sonraki çizimine kadar geçen süreyi ölçer. Kısaca: JS ana iş parçacığını (main thread) tıkamayın. Büyük framework projelerinde bile INP’yi düşüren en etkili hamle, “gereksiz iş”i doğru zamana yaymaktır.

Uygulanabilir adımlar: 1) Uzun süren görevleri bölün: 200–300 ms’yi aşan işlemleri küçük parçalara ayırın. 2) Olay dinleyicilerini sadeleştirin; her tıklamada ağır DOM işlemleri yapmayın. 3) “Passive event listener” kullanın (özellikle scroll/touch). 4) Üçüncü taraf script’leri (chat, analytics, tag manager) denetleyin; INP’yi en çok bunlar bozabilir. Mümkünse gecikmeli yükleyin veya daha hafif alternatiflere geçin.

5) Pratik bir kontrol listesi: Hızlı kazanımlar

Kontrol listesi: (1) Ana sayfa ve en çok trafik alan 3 şablonda ölçüm alın. (2) LCP öğesini belirleyip kritik yolu kısaltın. (3) CLS için boyut belirtmeyen medya alanlarını tespit edin. (4) INP için Performance kaydında “Long Task” avına çıkın. (5) Üçüncü taraf script’leri tek tek devre dışı bırakıp etkisini ölçün. (6) Değişiklik sonrası PageSpeed Insights + gerçek kullanıcı verisiyle doğrulayın.

6) Sık yapılan hatalar ve doğru yaklaşım

Sadece Lighthouse skorunu yükseltmeye odaklanmak çoğu zaman yanıltıcıdır; çünkü kullanıcılar farklı cihaz ve ağlarda geziyor. Diğer hata, her şeyi aynı anda optimize etmeye çalışmak. En iyi yöntem: önce en çok etkileyen metriği seçin (örneğin LCP), en büyük darboğazı giderin, sonra diğerine geçin. Küçük ama doğrulanmış iyileştirmeler, bir “büyük refactor”dan daha güvenli sonuç verir.

Sonuç

Core Web Vitals optimizasyonu, “tek seferlik” değil, ölçüm-iyileştirme döngüsüdür. LCP’de kritik içeriği hızlandırın, CLS’de düzen kaymalarını kökten engelleyin, INP’de ana iş parçacığını rahatlatın. Bu üçlüde doğru teşhis ve küçük ama hedefli dokunuşlarla hem kullanıcı deneyimini hem de organik görünürlüğü aynı anda güçlendirebilirsiniz.

Docker Compose ile Yerel Geliştirme Ortamı Kurma: PostgreSQL + Redis + Uygulama Servisi (İleri Seviye How-To)

Docker Compose neden hâlâ en pratik seçenek?

Yerel geliştirme ortamı kurarken en çok vakit yediren şey, bağımlılıkların (veritabanı, cache, mesaj kuyruğu, reverse proxy vb.) makineden makineye farklı davranmasıdır. Docker Compose, birden fazla servisi tek bir dosyada tanımlayıp ayağa kaldırarak “bende çalıştı” problemini ciddi ölçüde azaltır. Bu yazıda PostgreSQL ve Redis’i, bir uygulama servisiyle birlikte aynı ağda koşturacak; kalıcı veri, healthcheck, profil ve ortam değişkeni yönetimi gibi ileri seviye pratiklere değineceğiz.

1) Proje yapısı: düzenli başlamak önemli

Örnek bir dizin yapısı oluşturalım. Terminalde bir klasör açıp içerisine aşağıdaki gibi dosyalar koymanız yeterli: docker-compose.yml, .env ve isterseniz uygulamanız için bir Dockerfile. Uygulama diliniz önemli değil; Compose tarafında servislerin birbirini görmesi ve konfigürasyonun taşınabilir olması hedef.

2) .env ile gizli bilgileri yönetmek

Compose dosyasına gömmek yerine şifreleri ve sık değişen değerleri .env içine almak daha temizdir. Proje köküne bir .env dosyası oluşturun ve örnek olarak şunları ekleyin: POSTGRES_DB=appdb, POSTGRES_USER=appuser, POSTGRES_PASSWORD=gucluSifre123, REDIS_PASSWORD=redisSifre123. Bu dosyayı versiyon kontrolüne koymamayı unutmayın; gerekirse örnek bir .env.example

3) docker-compose.yml: PostgreSQL + Redis + app

Aşağıdaki Compose tanımı, üç servisi aynı network üzerinde çalıştırır. PostgreSQL için kalıcı volume, Redis için parola ve iki servis için healthcheck ekledik. Uygulama servisini ise örnek bir “app” konteyneri olarak tanımlıyoruz; burada kendi Dockerfile’ınızı kullanabilir ya da hazır bir imajla test edebilirsiniz.

docker-compose.yml içeriği:

version: "3.9"
services:
  db:
    image: postgres:16-alpine
    container_name: local-postgres
    restart: unless-stopped
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    ports:
      - "5432:5432"
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
      interval: 5s
      timeout: 3s
      retries: 20

  redis:
    image: redis:7-alpine
    container_name: local-redis
    restart: unless-stopped
    command: ["sh", "-c", "redis-server --requirepass ${REDIS_PASSWORD}"]
    ports:
      - "6379:6379"
    healthcheck:
      test: ["CMD-SHELL", "redis-cli -a ${REDIS_PASSWORD} ping | grep PONG"]
      interval: 5s
      timeout: 3s
      retries: 20

  app:
    build: .
    container_name: local-app
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
    environment:
      DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
      REDIS_URL: redis://:${REDIS_PASSWORD}@redis:6379/0
    ports:
      - "8080:8080"

volumes:
  pgdata:

4) Uygulama servisi için minimal Dockerfile fikri

“app” servisi için örnek bir Dockerfile ihtiyacınız olacak. Kendi stack’inize göre değişir; ancak temel prensip aynıdır: bağımlılıkları kur, kaynak kodu kopyala, uygulamayı çalıştır. Örneğin Node.js kullanıyorsanız node:20-alpine ile başlayabilir; Python’da python:3.12-slim tercih edebilirsiniz. Önemli nokta, Compose içindeki bağlantı adreslerinin db ve redis servis adları üzerinden verilmesidir; çünkü konteynerlar aynı Compose ağı içinde DNS ile birbirini bu isimlerle bulur.

5) Çalıştırma, log takibi ve durdurma

Kurulumdan sonra proje kökünde şu komut yeterlidir: docker compose up -d. İlk çalıştırmada imajlar indirileceği için birkaç dakika sürebilir. Servislerin durumunu görmek için docker compose ps, logları takip etmek için docker compose logs -f kullanın. Her şeyi kapatmak için docker compose down işinizi görür; veriyi de silmek istiyorsanız (PostgreSQL volume) docker compose down -v uygulayabilirsiniz.

6) Sık yapılan hatalar ve pratik ipuçları

En yaygın hata, uygulama içinde veritabanına localhost ile bağlanmaya çalışmaktır. Konteyner içinden “localhost”, o konteynerın kendisini ifade eder; bu yüzden bağlantı adresi db:5432 olmalı. İkinci hata, servisler hazır olmadan uygulamanın ayağa kalkmasıdır; burada kullandığımız healthcheck ve depends_on condition: service_healthy yaklaşımı bu sorunu büyük ölçüde azaltır. Son olarak, geliştirme sürecinde dosya değişikliklerini anında görmek istiyorsanız volumes ile proje klasörünü konteynere mount edebilir, fakat bu ayarı işletim sisteminize ve kullandığınız dile göre dikkatle yapmalısınız.

Sonuç: taşınabilir, tekrarlanabilir bir yerel ortam

Bu yapı sayesinde PostgreSQL ve Redis’i tek komutla ayağa kaldırıp uygulamanızla aynı ağda konuşturabilirsiniz. Üstelik ekipteki herkes aynı Compose dosyasını kullandığı için ortam farklılıkları azalır, hata ayıklama hızlanır. İsterseniz bir sonraki adım olarak profil ekleyip (ör. test/prod), ya da ters proxy (Nginx/Traefik) ile TLS ve routing katmanı ekleyerek daha da gerçekçi bir geliştirme ortamı kurabilirsiniz.

17 Aralık 2025 Çarşamba

Kubernetes’te Argo Rollouts ile Canary Dağıtımı: Adım Adım Kurulum ve Ölçüme Dayalı Otomasyon

Özet

Canary dağıtımı, üretimde yeni sürümü küçük bir trafik yüzdesiyle deneyerek hataları erken yakalamayı hedefleyen, modern ve güvenli bir yayınlama tekniğidir. Bu yazıda, Kubernetes üzerinde Argo Rollouts kullanarak canary dağıtımı nasıl kurulur, NGINX Ingress ile trafik nasıl paylaştırılır ve Prometheus ile ölçüme dayalı otomatik promosyon/geri alma (rollback) nasıl yapılır adım adım anlatıyorum.

Neden Argo Rollouts?

Argo Rollouts, Kubernetes’in Deployment nesnesine gelişmiş progressive delivery yetenekleri ekleyen bir CRD (Custom Resource Definition) çözümüdür. Canary, Blue/Green, A/B testleri, metric tabanlı onaylar, otomatik rollback ve NGINX/ALB/Istio gibi çoklu trafik yöneticileri desteği sunar. Tek komutla izleme ve görselleştirme sağlayan kubectl argo rollouts eklentisi ile süreç oldukça şeffaftır.

Gereksinimler

- Kubernetes 1.21+ bir küme (yerelde kind/minikube veya bulutta managed).
- kubectl ve yönettiğiniz namespace üzerinde yetki.
- NGINX Ingress Controller (canary anotasyonlarını destekleyen sürüm).
- Prometheus (temel HTTP başarı oranı ölçümü için).

Kurulum

1) Argo Rollouts CRD ve kontrol düzlemini kurun:
kubectl apply -f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml

2) Komut satırı eklentisini yükleyin (macOS örneği):
brew install argoproj/tap/kubectl-argo-rollouts
Linux/Windows için ikili dosyayı Argo Rollouts sürüm sayfasından indirip PATH’e ekleyebilirsiniz.

3) NGINX Ingress Controller kurulu değilse resmi manifest ya da Helm chart ile kurun. Trafik yönlendirme için Rollout nesnesinde trafficRouting: nginx kullanacağız.

Manifest Tasarımı

Canary stratejisinde iki Service kullanılır: stable ve canary. Ingress, Argo Rollouts tarafından ağırlıklandırılmış şekilde bu Service’lere trafik dağıtır. Aşağıdaki örnekler özlü tutulmuştur; isimleri ve etiketleri kendi uygulamanıza göre uyarlayın.

Stable ve Canary Service (aynı selector’ı paylaşır, Rollouts yönetir):
apiVersion: v1
kind: Service
metadata:
  name: myapp-stable
spec:
  selector:
   app: myapp
  ports:
  - port: 80
    targetPort: 8080

apiVersion: v1
kind: Service
metadata:
  name: myapp-canary
spec:
  selector:
   app: myapp
  ports:
  - port: 80
    targetPort: 8080

Ingress (Argo Rollouts, bu Ingress’i kullanarak NGINX canary anotasyonlarıyla ağırlık verir):
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myapp-ingress
spec:
  rules:
 - host: myapp.example.com
   http:
    paths:
    - path: /
     pathType: Prefix
     backend:
      service:
       name: myapp-stable
       port:
        number: 80

AnalysisTemplate (Prometheus ile başarı oranı ölçümü):
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: success-rate
spec:
  args:
  - name: service
  metrics:
  - name: http-success-rate
   interval: 30s
   count: 3
   successCondition: result[0] >= 0.99
   failureLimit: 1
   provider:
    prometheus:
     address: http://prometheus-server.monitoring.svc.cluster.local:9090
     query: sum(rate(http_requests_total{service="{{args.service}}",code=~"2.."}[1m])) / sum(rate(http_requests_total{service="{{args.service}}"}[1m]))

Rollout (canary adımları, NGINX trafik yönlendirme ve metrik analizi ile):
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: myapp
spec:
  replicas: 4
  selector:
   matchLabels:
    app: myapp
  template:
   metadata:
    labels:
     app: myapp
   spec:
    containers:
    - name: web
     image: ghcr.io/org/myapp:1.1.0
     ports:
     - containerPort: 8080
  strategy:
   canary:
    canaryService: myapp-canary
    stableService: myapp-stable
    trafficRouting:
     nginx:
      stableIngress: myapp-ingress
    steps:
    - setWeight: 20
    - pause: { duration: 120 }
    - analysis:
      templates:
      - templateName: success-rate
       args:
       - name: service
        value: myapp
    - setWeight: 50
    - pause: { duration: 120 }

Dağıtımı Başlatma ve İzleme

Manifestleri uygulayın: kubectl apply -f . Ardından rollout durumunu canlı izleyin: kubectl argo rollouts get rollout myapp -w. Eklenti, hangi adımda olunduğunu, mevcut ağırlığı ve analiz sonuçlarını gösterir. Metrikler eşik altında kalırsa Argo Rollouts otomatik rollback yapar; eşik üstünde ise bir sonraki adıma geçer ve sonunda sürüm terfi eder.

İpuçları ve En Sık Yapılan Hatalar

- Prometheus sorgunuzun doğru label’ları kullandığından emin olun. Bazı exporter’larda HTTP durum etiketi code yerine status olabilir.
- NGINX Ingress sürümünüzün canary anotasyonlarını desteklediğini doğrulayın; aksi halde ağırlıklandırma çalışmaz.
- Düşük trafik ortamlarında oran metrikleri dalgalıdır. interval ve count değerlerini artırmak yalancı negatifleri azaltır.
- Rollout öncesi readinessProbe ve livenessProbe ayarlarını düzgün yapılandırın; aksi halde analiz başlamadan pod’lar çakılabilir.

Performans ve Maliyet Değerlendirmesi

Canary adımları önemlidir çünkü ağ ve uygulama gecikmelerindeki küçük oynamaları yakalar. Ancak her adımda fazladan pod ve ölçüm maliyeti oluşur. Kritik servisler için 20%→50%→100% gibi 2-3 adıma bölmek çoğu senaryoda yeterli olur. Trafik çok yüksekse daha fazla adım ve daha uzun duraklatmalarla güvenlik marjı artırılabilir.

Sonuç

Argo Rollouts, Kubernetes üzerinde canary dağıtımını pratik ve güvenli hale getiriyor. NGINX Ingress ile trafik ağırlıklandırma ve Prometheus ile metrik bazlı doğrulama birleştiğinde, üretimde hataları kullanıcıların küçük bir yüzdesiyle sınırlayıp otomatik geri alma yapabilirsiniz. Küçükten başlayın, metriklerinizi doğrulayın ve ihtiyaçlarınıza göre adım sayısı ile eşikleri düzenleyerek olgun bir progressive delivery akışı elde edin.

16 Aralık 2025 Salı

Yerelde RAG Asistanı Kurulumu: Python, FAISS ve FastAPI ile Adım Adım

RAG nedir ve neden yerelde kurmalısınız?

RAG (Retrieval-Augmented Generation), bir büyük dil modelinin (LLM) yanıt üretmeden önce kendi veritabanınızdan veya belgeliklerinizden ilgili parçaları çekmesini sağlayan bir yaklaşım. Böylece model, güncel ve doğrulanabilir bilgiye dayanarak cevap verir, halüsinasyon oranı düşer ve alanınıza özgü bir asistan elde edersiniz. Bu yazıda, internete kapalı bir ortamda, Python, FAISS ve FastAPI kullanarak hızlı ve hafif bir RAG asistanını nasıl kurabileceğinizi adım adım anlatıyorum.

Mimari bileşenler ve gereksinimler

Minimal bir RAG sistemi dört temel bileşenden oluşur: metin ön işleme ve parçalara ayırma, embedding üretimi, vektör dizin (FAISS) ve yanıt üretici (LLM). Yerel kurulum için Python 3.10+ bir ortam, sentence-transformers ile çok dilli bir embedding modeli, faiss-cpu dizini ve HTTP üzerinden istek alıp yanıtlamak için FastAPI yeterli. İsterseniz yanıt üretici olarak Ollama üzerinden Llama 3 ailesi gibi bir yerel modeli kullanabilir, ya da sisteminize uygun bir başka LLM’i bağlayabilirsiniz.

Kurulum adımları: ortam, paketler ve veri hazırlığı

Önce izole bir Python sanal ortamı oluşturun ve temel paketleri kurun. Gerekli çekirdek paketler tipik olarak fastapi, uvicorn, faiss-cpu, sentence-transformers, pydantic ve metin işleme için regex/unidecode gibi küçük yardımcılar olur. Eğer yerel bir model kullanacaksanız Ollama’yı yükleyip örneğin “llama3” veya “mistral” gibi bir modeli indirin. Belgelerinizi tek bir klasörde toplayın; PDF, DOCX ve düz metinleri mümkünse metne dönüştürüp temizleyin. Tekrarlayan başlıklar, sayfa dipnotları ve tablolar metni kirlettiği için önceden sadeleştirme yapmak ileride kaliteyi ciddi biçimde artırır.

Embedding üretimi ve FAISS ile dizinleme

RAG’in çekirdeği, metni anlam uzayına taşıyan embedding’lerdir. Türkçe ağırlıklı bir kurulum için çok dilli bir model seçmek pratik olur. intfloat/multilingual-e5-base veya sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2 iyi başlangıç seçenekleridir. Belgeleri 500–800 karakter aralığında, 50–100 karakter örtüşmeli parçalara bölerseniz hem hatırlama oranı hem bağlam bütünlüğü dengelenir. Her parçayı embedding modelinden geçirip bir vektör matrisi üretin; ardından FAISS’te IndexFlatIP (cosine benzeri iç çarpım) veya daha büyük veri için HNSW/IVF tabanlı bir dizin kurun. Dizin ve meta verileri (ör. belge kimliği, başlık, sayfa numarası) disk üzerinde saklayarak soğuk başlatma sürelerini azaltabilirsiniz.

Sorgu işleme hattı: geri getirme, yeniden sıralama ve yanıt üretimi

İstemci bir soru sorduğunda önce soruyu embedding modellerinden geçirip FAISS’e benzerlik araması yapın. İlk aşamada genellikle top-k=5–8 yeterlidir. Sonuçları kalite için hafif bir yeniden sıralama (re-rank) aşamasından geçirmek özellikle uzun ve benzer parçalarda fark yaratır; isterseniz küçük bir cross-encoder veya basit bir skor birleştirme (benzerlik + anahtar kelime çakışması) yaklaşımı kullanın. Son olarak en iyi 3–5 parçayı bir prompt şablonunda LLM’e vererek yanıt üretin. İyi bir şablon, modele “yalnızca verilen bağlama dayanması”, “kaynakları listelemesi” ve “emin olmadığı noktalarda netçe belirtmesi” yönünde talimatlar içerir. Bu aşamada streaming yanıt (chunk bazlı) hem his olarak daha hızlıdır hem de istemci tarafında kullanıcı deneyimini iyileştirir.

FastAPI ile uç noktalar ve basit bir API tasarımı

Sunucu tarafında iki temel uç nokta yeterlidir: /index ve /query. İlk uç nokta yeni belge eklemeyi, embedding ve dizine yazmayı üstlenir. İkincisi kullanıcı sorusunu alır, geri getirme ve yeniden sıralama yapar, LLM’den gelen yanıtı döndürür. Yanıta kaynak parçaların kimliklerini ve güven puanlarını dahil ederseniz istemci tarafında “bu cevabın kanıtı” şeritleri gösterebilirsiniz. Üretim ortamında CORS ayarlarını, basit bir oran sınırlama (rate limit) ve temel kimlik doğrulamayı eklemeyi unutmayın.

Performans, kalite ve bakım ipuçları

İlk indekslemeyi hızlandırmak için toplu gömme (batch) boyutunu donanımınıza göre ayarlayın; CPU’da 16–64 arası genelde güvenlidir. Dizin aramasında hız/kalite dengesini sağlamak üzere IVF ya da HNSW gibi yaklaşık yakın komşu (ANN) yapılarını deneyin ve geri getirme anında efSearch benzeri parametreleri kademeli artırın. Kalite için chunk boyutu ve örtüşmesini alanınıza göre yeniden ayarlayın; teknik dökümanlarda daha uzun, SSS türü içerikte ise daha kısa parçalar daha iyi sonuç verir. Ayrıca yinelenen metinleri, versiyonlanmış kopyaları ve tarih geçmişlerini indeks dışında tutmak, cevapların tekrara düşmesini engeller.

Düzenli bir değerlendirme seti oluşturun: 30–50 soruluk, doğru cevap ve kaynak eşleşmesi içeren bir küçük benchmark ile sürümler arasında regresyon olup olmadığını görebilirsiniz. Ölçüt olarak geri getirme için recall@k, cevap üretimi için ise doğruluk, faydalılık ve kaynak isabetini manuel etiketleme ile kontrol etmek etkilidir. Basit bir geri bildirim mekanizması (kullanıcı oylaması, “yanlıştı/düzelt” düğmesi) gerçek kullanımda iyileştirme fırsatlarını hızla ortaya çıkarır.

Güvenlik, gizlilik ve dağıtım

Yerel RAG sistemleri çoğu zaman hassas verilerle çalışır. Bu nedenle PII tespiti ve maskeleme, uçtan uca TLS, erişim loglarının minimal ve anonim tutulması önemlidir. Küçük bir ek katman olarak belirli sorgulara kural tabanlı filtre uygulayabilir, gizli etiketli içerikleri yanıt bağlamına girmeden önce temizleyebilirsiniz. Dağıtım için hafif bir konteyner imajı, tek bir health-check uç noktası ve kalıcı depolama bağlanmış bir FAISS dizini çoğu ekip için yeterli, ölçeklenebilir ve sürdürülebilir bir başlangıç sunar.

Sonuç

Python, FAISS ve FastAPI ile yerelde çalışan bir RAG asistanı kurmak sanıldığından daha erişilebilir. Doğru embedding modeli, düzenli bir parçalara ayırma stratejisi ve iyi tasarlanmış bir prompt ile, alanınıza özel, hızlı ve güvenilir bir bilgi asistanına sahip olursunuz. İlk sürümü basit tutun, kullanım verisi geldikçe yeniden sıralama ve değerlendirme mekanizmasını güçlendirin; böylece hem maliyeti hem karmaşıklığı kontrol altında tutarken yüksek kaliteli cevaplar üreten bir sistem inşa edebilirsiniz.

15 Aralık 2025 Pazartesi

WebAuthn ve Passkeys ile Parolasız Giriş: Node.js Üzerinde Adım Adım Entegrasyon Rehberi

Giriş

Parolalar hem kullanıcılar hem de geliştiriciler için yıllardır en zayıf halka. Güvenlik ihlalleri, kimlik avı ve kullanım zorluğu derken şifrelerin yerini alan yeni standartlar artık olgunlaştı: WebAuthn ve Passkeys. Apple, Google ve Microsoft ekosistemlerinde yerleşik olan Passkey’ler; biyometrik doğrulama (Face ID, Touch ID), cihaz PIN’i veya güvenlik anahtarlarıyla kullanıcıyı güvenli ve hızlı şekilde doğrular. Bu yazıda, Node.js üzerinde modern bir Passkey (FIDO2/WebAuthn) entegrasyonunu adım adım ele alacağız.

Neden Passkey?

Passkey, kullanıcı tarafında kriptografik anahtar çifti üretir ve yalnızca kayıt olduğunuz alan adıyla (relying party) çalışır. Bu sayede kimlik avı saldırılarına karşı dirençlidir, veri sızıntılarında şifre yeniden kullanımı riski yoktur ve kullanıcı deneyimi önemli ölçüde iyileşir. iCloud Anahtar Zinciri ve Google Password Manager gibi yöneticiler, passkey’leri cihazlar arasında senkronize edebilir; güvenlik anahtarları (YubiKey, Titan Key) ise kurumsal ortamda donanımsal güvence sağlar.

Mimari ve Akış

WebAuthn entegrasyonu iki ana adımdan oluşur: Kayıt ve Giriş. Kayıtta sunucu bir challenge üretir, tarayıcı üzerinden kullanıcının cihazında anahtar çifti oluşturulur ve sunucuya attestation verisiyle beraber kimlik bilgisi (credential) kaydedilir. Girişte sunucu yine bir challenge üretir, cihaz özel anahtarla imzalar ve sunucu imzayı, sayacı (signCount) ve relying party değerlerini doğrular.

Ön Koşullar ve Güvenlik

Üretimde mutlaka HTTPS kullanın; WebAuthn tarayıcılar tarafından güvenli bağlam gerektirir. rpID alan adınız (ör. example.com), origin ise tam köken (ör. https://example.com) olmalıdır. Oturum yönetimi için HttpOnly, Secure ve SameSite cookie ayarlarına dikkat edin. Kullanıcı deneyimini iyileştirmek için mümkünse User Verification değerini required seçin; böylece biyometrik veya PIN doğrulaması zorunlu olur.

Sunucu Kurulumu (Node.js)

Sunucuda Express veya benzeri bir framework kullanabilirsiniz. WebAuthn doğrulama ve dönüşüm adımlarında kendiniz sıfırdan implementasyon yazmak yerine toplulukta yaygın kütüphanelerden yararlanmak geliştirme hızını artırır. Örneğin @simplewebauthn/server ve @simplewebauthn/browser ikilisi, kurulum ve doğrulama mantığını açık biçimde sunar. Kayıt için iki uç nokta tanımlayın: /register/options (kayıt seçenekleri üretir) ve /register/verify (kayıt yanıtını doğrular). Giriş için benzer şekilde /login/options ve /login/verify oluşturun.

Kayıt seçeneklerini üretirken rpID ve rpName (marka adınız), user.id (stabil benzersiz kimlik), user.name (kullanıcı adı/e‑posta), user.displayName, authenticatorSelection ve attestationType gibi alanları doldurun. Çoğu senaryo için attestationType = none, residentKey = preferred veya passkey zorunlu ise required, userVerification = required iyi varsayılanlardır. Challenge değerini kriptografik olarak güçlü bir rastgelelik ile üretin ve oturumda kısa süreli saklayın.

Kullanıcı tarayıcıda kayıt yanıtı döndüğünde sunucu tarafında doğrulama yapın. Başarılı doğrulamada şu bilgileri veritabanında tutun: credentialID (base64url), credentialPublicKey, counter (signCount), transports ve tercihen cihaz adına ilişkin meta. Bu kayıtlar kullanıcı hesabıyla ilişkilendirilir ve çoklu cihaz/passkey senaryolarında birden fazla credential desteklenir.

İstemci (Tarayıcı) Entegrasyonu

Tarayıcı tarafında navigator.credentials.create() çağrısı kayıt için, navigator.credentials.get() ise giriş için kullanılır. Modern tarayıcılarda Conditional UI desteği ile kullanıcı “Parolayı yazmadan” doğrudan giriş önerisi alabilir. Özellikle mobilde bu deneyim, geleneksel şifre formuna göre çok daha hızlıdır. İstemci kodunda sunucudan gelen PublicKeyCredentialCreationOptions veya PublicKeyCredentialRequestOptions nesnelerini doğru şekilde dönüştürmeyi (ArrayBuffer <-> base64url) unutmayın.

Passkey’lerin bulut senkronizasyonu açıksa (iCloud/Google Password Manager), kullanıcı yeni cihazında da sorunsuz giriş yapabilir. Kurumsal katmanda donanımsal güvenlik anahtarlarıyla (USB/NFC/BLE) aynı akışı takip edersiniz; sadece transports alanı farklılık gösterir.

Giriş Doğrulaması ve Sayaç Yönetimi

Girişte sunucu challenge üretir ve tarayıcı assertion döndürür. Sunucu, imzayı credentialPublicKey ile doğrular, rpIDHash ve origin kontrolünü yapar ve signCount değerini karşılaştırır. Donanım bazı senaryolarda sayacı artıramayabilir; bu durumda kütüphanenin “signCount kludge” dokümantasyonuna bakın. Başarılı doğrulamada kullanıcı oturumunu açın ve sayacı güncelleyin.

UX, Erişilebilirlik ve Geri Dönüş Planı

Kullanıcıya net talimatlar verin: “Cihazınızın biyometrisini kullanın” gibi. Geri dönüş planı olarak e‑posta tabanlı sihirli bağlantı veya tek kullanımlık kurtarma kodları tanımlayın. Güvenlik anahtarlarını tercih eden kullanıcılar için “harici anahtar ekle” seçeneği sunun. Çoklu cihaz desteğinde, kullanıcı hesabı ayarlarına eklenen/çıkarılan tüm kimlik bilgilerini listelemeniz önemlidir.

Uyumluluk ve Test

Geliştirme sürecinde webauthn.io veya tarayıcı geliştirici araçlarının “Security” ve “WebAuthn” panellerinden yararlanın. iOS Safari, Android Chrome, macOS ve Windows ortamlarında çapraz test yapın. Alan adı ve köken uyuşmazlığı en yaygın hatadır; yerelde test ederken localhost için HTTPS kullanmayı veya tünel servisleriyle geçici bir alan adı oluşturmayı düşünün.

Sonuç

Passkey ve WebAuthn, parolasız geleceğin pratik ve güvenli yolu. Node.js üzerinde birkaç uç nokta ve doğru yapılandırma ile güncel tarayıcılar ve işletim sistemleri arasında kusursuz bir deneyim sunabilirsiniz. Doğru varsayılanlar (attestation none, userVerification required), güçlü bir TLS yapılandırması, sağlam oturum yönetimi ve iyi bir geri dönüş planı ile projenizi bugün üretime taşımanız mümkün.

14 Aralık 2025 Pazar

Next.js ile Passkey (WebAuthn) Entegrasyonu: Şifresiz Giriş İçin Adım Adım Rehber

Giriş

Şifresiz kimlik doğrulama, web uygulamalarında güvenliği yükseltirken kullanıcı deneyimini de ciddi biçimde iyileştiriyor. Passkey (FIDO2/WebAuthn) tabanlı giriş, parolaları ortadan kaldırarak kimlik doğrulamayı cihazın güvenli donanımına emanet eder. Bu yazıda, Next.js ile Passkey entegrasyonunu adım adım ele alacak; sunucu ve istemci akışlarını, doğru yapılandırmayı, veri modelini ve canlıya alma ipuçlarını paylaşacağım.

Passkey ve WebAuthn Nedir?

WebAuthn, tarayıcı ile sunucu arasında kriptografik anahtarlar kullanarak kullanıcı doğrulamayı standartlaştırır. Passkey ise bu standardın son kullanıcıya yansıyan yüzü: Apple, Google ve Microsoft ekosistemlerinde anahtarlar güvenli biçimde saklanır ve cihazlar arasında senkronize olabilir. Kullanıcı, biyometri veya cihaz kilidi ile yetkilendirme yapar; sunucu tarafında hiçbir parola tutulmaz.

Neden Passkey?

- Kimlik avı (phishing) direncine sahiptir: Anahtarlar alan adına (RP ID) bağlıdır, başka bir sitede kullanılamaz.
- Kullanıcı deneyimi yüksektir: Parola üretme, hatırlama ve reset akışları büyük ölçüde ortadan kalkar.
- Uygulama bakım maliyeti azalır: Parola karma algoritmaları, karmaşık parola politikaları gibi işler minimize olur.

Önkoşullar ve Mimari

- Next.js 13/14 (App Router) veya 12 (Pages) ile Node.js 18+.
- Üretimde HTTPS zorunludur; geliştirmede localhost desteklenir.
- RP ID, alan adınız (ör. example.com) olmalı; origin tam URL’nizdir (ör. https://example.com).
- Sunucuda kriptografik doğrulama için Node runtime kullanın; Edge runtime, bazı kütüphanelerde sınırlı kalabilir.

Kütüphaneler ve Temel Akış

Node.js ekosisteminde @simplewebauthn/server ve tarayıcı tarafında @simplewebauthn/browser ile stabil bir deneyim elde edebilirsiniz. Kayıt (registration) ve giriş (authentication) iki ayrı fakat benzer akışla ilerler: Sunucu bir challenge üretir, tarayıcı navigator.credentials üzerinden güvenli cihaz akışını tetikler ve yanıt sunucuda doğrulanır.

Kayıt (Registration) Akışı

1) Sunucu uç noktası: /api/passkey/register/options
Sunucu generateRegistrationOptions ile kullanıcıya özgü bir challenge üretir. Parametrelerde rpID (alan adınız), rpName, user.id (kalıcı ve benzersiz), user.name, attestation (çoğunlukla none) ve discoverable credentials için authenticatorSelection.residentKey="preferred", userVerification="preferred" kullanabilirsiniz. Challenge’ı ve kullanıcı kimliğini kısa süreli (örn. 5 dk) Redis gibi bir depoda saklayın.

2) İstemci adımı:
Tarafınıza dönen PublicKeyCredentialCreationOptions ile @simplewebauthn/browser kütüphanesindeki startRegistration çağrılır. Tarayıcı biyometri/donanım akışını yönetir ve yanıt üretir.

3) Sunucu doğrulaması: /api/passkey/register/verify
verifyRegistrationResponse ile gelen veriyi doğrulayın. Başarılıysa veritabanına credentialId (Base64URL), publicKey, counter, transports, backedUp gibi alanları ekleyin. Aynı kullanıcı için birden fazla cihaz desteği sağlamak üzere bir “kullanıcı-kimlik bilgisi” tablosu kullanın.

Giriş (Authentication) Akışı

1) Sunucu uç noktası: /api/passkey/login/options
generateAuthenticationOptions ile challenge üretin. Kullanıcının e-posta veya kullanıcı adı verdiği senaryoda allowCredentials ile ilgili kimlik bilgilerini sınırlayabilir; tamamen kullanıcı adı girmeden “sinyalsiz” giriş için discoverable credentials’ı destekleyebilirsiniz.

2) İstemci adımı:
startAuthentication çağrısı tarayıcıda WebAuthn akışını tetikler; kullanıcı cihaz kilidi veya biyometri ile onaylar.

3) Sunucu doğrulaması:
verifyAuthenticationResponse ile yanıtı doğrulayın. Counter değeri artmıyorsa veya geriye gidiyorsa potansiyel klonlama şüphesi doğar; kontrol edip güncelleyin. Başarılı doğrulamada oturum belirtecini (HttpOnly, Secure, SameSite=Lax) ayarlayın.

Veri Modeli ve Saklama

Veri tabanı şeması örneği: user_credentials(id, user_id, credential_id, public_key, counter, transports, backed_up, created_at). credential_id ve public_key için Base64URL normalize edin. Kullanıcı silme ve cihaz yönetimi (ör. “Bu cihazı kaldır”) arayüzü sağlayın.

Yapılandırma Ayrıntıları ve İpuçları

- Origin/RP ID eşleşmesi kritik: https://app.example.com için RP ID example.com veya alt alan adınızın köküne uygun olmalı. 127.0.0.1 yerine localhost kullanın.
- HTTPS zorunlu; yalnızca http://localhost istisnadır.
- Attestation çoğu senaryoda “none”; kurumsal güven zinciri gereksiniminde metadata doğrulamayı (MDS) değerlendirin.
- userVerification: “required”, uygulamanızın risk profiline göre zorunlu biyometri sağlar.
- Challenge tek kullanımlık ve kısa ömürlü olmalı, tekrar kullanımda reddedilmeli.
- Edge vs Node: Doğrulama kütüphanesinin Node kriptosuna ihtiyacı olabilir; Next.js API Routes’ı Node runtime’da çalıştırın.

UX Önerileri

- Yeni kullanıcı kayıt akışında parolasız varsayılanı sunun; uygun cihaz yoksa e-posta sihirli bağlantı veya tek seferlik kod ile yedek akış bırakın.
- Giriş sayfasında “Passkey ile devam et” butonu tek tıklama ile süreci başlatmalı; başarısızlıkta otomatik olarak yedek akışa yönlendirin.
- Cihaz yönetimi sayfasında kayıtlı cihaz adlarını, eklenme tarihini ve kaldırma seçeneklerini gösterin.

Hata Ayıklama ve Sık Karşılaşılan Sorunlar

- “The relying party ID is not a registrable domain suffix” uyarısı: RP ID yanlış. Geliştirmede localhost kullanın; üretimde çıplak alan adı veya uygun alt alan.
- “NotAllowedError” hatası: Kullanıcı onaylamadı veya tarayıcı akışı zaman aşımına uğradı. Zaman aşımı süresini makul tutun ve net geri bildirim verin.
- “This device doesn’t support passkeys”: Eski OS/tarayıcı olabilir; güncellemeyi önerin ve yedek kimlik doğrulamasını aktif tutun.

Güvenlik En İyi Uygulamaları

- Oturum çerezlerini HttpOnly ve Secure olarak ayarlayın; CSRF riskine karşı SameSite=Lax veya CSRF belirteci kullanın.
- Rate limiting ve IP/cihaz parmak iziyle kötüye kullanımı azaltın.
- Kök alan adından başka ortamlara (staging) dağıtırken RP ID ve origin’i doğru güncelleyin.
- Yedek kurtarma (ör. e-posta + ek doğrulama) ve cihaz kaybı senaryolarını önceden tasarlayın.

Sonuç

Passkey (WebAuthn), modern web uygulamalarında güvenlik ve kullanılabilirliği aynı anda yükselten bir yapı taşı. Next.js ekosisteminde @simplewebauthn ile kısa sürede kayıt ve giriş akışlarını hayata geçirebilir, phishing’e dirençli, hızlı ve kullanıcı dostu bir kimlik doğrulama deneyimi sunabilirsiniz. Doğru RP ID/origin eşleşmesi, kısa ömürlü challenge’lar ve sağlam bir cihaz yönetimi arayüzü ile üretime hazır bir çözüm elde etmek zor değil.

13 Aralık 2025 Cumartesi

OpenTelemetry ile Mikroservislerde Dağıtık İzleme: Grafana Tempo ve Loki ile Uçtan Uca Kılavuz

Giriş

Mikroservis mimarisine geçtikçe uygulamaların davranışını anlamak ve sorunları hızla tespit etmek, basit log takibinin çok ötesine geçmeyi gerektirir. OpenTelemetry (OTel), ölçümleme (metrics), izleme (traces) ve günlük (logs) verilerini satıcıdan bağımsız bir standartla üretip toplamanıza izin veren modern bir çatı sunar. Bu yazıda, OpenTelemetry ile Node.js ve Python (FastAPI) servislerinin enstrümantasyonunu kavramsal olarak ele alacak, verileri Grafana Tempo (tracing) ve Loki (logging) ile toplayıp Grafana’da görselleştirmenin pratik adımlarını anlatacağım.

Mimari: Hangi bileşenler bir araya geliyor?

OpenTelemetry iki ana katmandan oluşur: uygulama içine eklediğiniz SDK/Enstrümantasyon katmanı ve veriyi toplayıp yöneten Collector. Uygulamalarınız OTLP protokolü üzerinden Collector’a veri gönderir; Collector da bu veriyi işleyip farklı hedeflere (Tempo, Loki, Prometheus, vs.) dağıtır. Bu yapının avantajı, uygulama kodunuza dokunmadan hedefleri, örneğin Jaeger’den Tempo’ya veya bir APM’e, değiştirebilmenizdir.

Bu kılavuzda şu akışı hedefliyoruz: Uygulamalar (Node.js/FastAPI) → OTLP (gRPC/HTTP) → OTel Collector → Tempo (traces) + Loki (logs) + Prometheus (metrics, opsiyonel) → Grafana ile görselleştirme.

Collector: Konfigürasyon mantığı

Collector konfigürasyonu üç ana bloktan oluşur: receivers (hangi protokolden veri alacağını belirtir, örn. otlp), processors (batch, attributes, memory_limiter gibi akış içi işlemler), exporters (veriyi nereye göndereceği, örn. tempo/loki). Tipik bir senaryoda, otlp receiver’dan gelen tüm izleri batch işlemcisinden geçirir, servis adı gibi resource etiketleri ekler ve tempo exporter’a yönlendirirsiniz. Loglar için benzer akış loki exporter’a gider. Metrics verisini kullanacaksanız prometheus veya otlp exporter ekleyebilirsiniz.

Üretimde şunları eklemek iyi bir pratiktir: memory_limiter (Collector’ın stabil kalması için), batch (veri paketleme ve throughput optimizasyonu), tail_sampling (yüksek trafik altında daha anlamlı izleri seçmek için kuyruk sonu örnekleme), attributes/resource (ortam, sürüm, ekip etiketleri).

Uygulama tarafı: Node.js servisini enstrümante etmek

Node.js tarafında çekirdek bileşenler: @opentelemetry/sdk-node, otomatik enstrümantasyon paketleri (HTTP, Express, MySQL/Postgres istemcileri vb.) ve OTLP exporter. Amaç; servisiniz başlarken bir tracer sağlayıcısı başlatmak, service.name, deployment.environment, service.version gibi resource etiketlerini tanımlamak ve Collector’a OTLP üzerinden veri göndermektir. Bağlantı için OTEL_EXPORTER_OTLP_ENDPOINT gibi çevre değişkenlerini kullanabilirsiniz. Üretimde gRPC tercih etmek genellikle daha performanslıdır.

Log korelasyonu için Node.js logger’ınız (ör. pino veya winston) ile trace_id ve span_id alanlarını yapılandırılmış loglara enjekte edin. Böylece Grafana’da bir trace’i incelerken ilgili loglara tek tıkla geçebilirsiniz. Eğer logs sinyalini de OTel üzerinden gönderecekseniz OTEL_LOGS_EXPORTER=otlp ve Collector’da loki exporter’ı etkinleştirerek aynı akış içinde korelasyon sağlayabilirsiniz.

Uygulama tarafı: Python FastAPI servisinde OTel

Python’da opentelemetry-sdk, opentelemetry-instrumentation-fastapi ve opentelemetry-exporter-otlp paketleriyle hızlıca başlarsınız. Çoğu durumda opentelemetry-instrument komutu ile otomatik enstrümantasyon yeterli olur. Yine OTEL_SERVICE_NAME, OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_RESOURCE_ATTRIBUTES (örn. deployment.environment=prod) gibi değişkenlerle konfigürasyon yapın. Asenkron çağrılarda bağlamın (context) düzgün taşındığından emin olmak için FastAPI ve HTTPX/Requests enstrümantasyonlarının etkin olduğuna dikkat edin.

Log korelasyonunda Python logging veya structlog ile trace_id’yi log kaydına dahil ederek Loki tarafında aynı etiketlerle sorgulanabilir hale getirin. Bu, “Trace → İlgili logları göster” yolculuğunu saniyelere indirir.

Grafana: Tempo ve Loki ile görselleştirme

Grafana’da veri kaynakları olarak Tempo ve Loki’yi ekleyin. Tracing için Tempo Explore sekmesinde service.name veya http.method, http.route gibi etiketlerle arama yapabilirsiniz. Eğer Collector’da exemplar desteğini ve histogram metriklerini açtıysanız, metrik panellerinde örnek izlere (trace exemplars) tıklayarak doğrudan ilgili trace’e atlayabilirsiniz.

Loki’de, log etiketlerinizi (level, service, env gibi) sade ve anlamlı tutun. Trace kimlikleri log mesajlarına etiketsiz gömülmek yerine alan olarak eklenirse sorgu performansı ve filtreleme kolaylaşır. Amacınız; “Belirli bir isteğin izini aç, aynı trace_id ile logları getir, botleneck’i gör” akışını akıcı hale getirmektir.

Gelişmiş konular: Örnekleme, maliyet ve güvenlik

Örnekleme (sampling) stratejisi maliyet ve görünürlük dengesini belirler. Giriş seviyesinde head-based (SDK tarafında sabit oranlı) örnekleme iş görür; ileri seviye üretim senaryolarında tail-based (Collector içinde karar veren), hata veya yüksek gecikmeli izlere öncelik veren kurallarla çok daha anlamlı veri tutulur. Örneğin “5xx veya p95 gecikmesi yüksek istekleri sakla, diğerlerini %5 örnekle.”

Maliyet açısından batch işlemcilerini, gzip sıkıştırmayı, histogram metriklerini (DDSketch/OTLP temporalları) ve yüksek kardinaliteli etiketlerden kaçınmayı düşünün. Güvenlik tarafında ise PII içeren alanları Collector’da attributes/transform işlemcileriyle maskeleme veya atma kuralları tanımlayarak uyumluluğu sağlayın.

Sorun giderme ve ipuçları

Çok sık görülen problem, Collector veya uygulama tarafında OTLP uç noktasının hatalı olmasıdır. Sağlık kontrolleri ve debug logları ile endpoint’in erişilebilir olduğundan emin olun. Trace zincirinin kopması genellikle bağlam yayılımı (W3C traceparent) eksikliğinden kaynaklanır; gateway/ingress, mesaj kuyrukları ve aracı servislerde propagation başlıklarını ilettiğinizden emin olun. Son olarak, container’larda saat senkronizasyonu (NTP) bozuksa iz süreleri anlamsız görünür; zaman senkronizasyonunu mutlaka doğrulayın.

Sonuç

OpenTelemetry; iz, metrik ve logları ortak bir çatı altında toplayarak mikroservislerinizi gözlemlenebilir kılar. Collector ile satıcı bağımsız mimari kurabilir, Tempo ve Loki ile uygun maliyetli ve güçlü bir görünürlük katmanı elde edebilirsiniz. Node.js ve FastAPI örnekleri üzerinden özetlediğimiz yaklaşım; üretimde örnekleme, maskeleme ve korelasyon stratejileriyle birleştirildiğinde, kök neden analizi ve performans iyileştirmelerini ciddi biçimde hızlandıracaktır.

12 Aralık 2025 Cuma

Docker ile Yerel LLM Kurulumu: Ollama + Open WebUI (GPU Hızlandırmalı Adım Adım Rehber)

Giriş

Yerel büyük dil modeli (LLM) çalıştırmak; gizliliği korumak, gecikmeyi düşürmek ve kullanım maliyetini kontrol etmek için harika bir yöntem. Son dönemde Ollama ve Open WebUI, Docker ile birlikte kullanıldığında zahmetsiz bir kurulum ve modern bir arayüz sunuyor. Bu rehberde, Ollama + Open WebUI ikilisini Docker üzerinde kurup, mümkünse GPU hızlandırma ile performansı nasıl katlayacağınızı adım adım anlatıyorum.

Neler Kuracağız?

Ollama, Llama 3, Mistral, Qwen gibi açık modelleri kolayca indirip çalıştırmanızı sağlar. Open WebUI ise sohbet, dosya yükleme, RAG eklentileri ve çoklu model yönetimi gibi özelliklerle modern bir ara yüz sunar. Docker sayesinde bu iki bileşen birbirinden izole, tekrar üretilebilir ve kolay güncellenebilir hale gelir.

Gereksinimler

- İşletim sistemi: Linux (Ubuntu 22.04+ önerilir), Windows 11 (WSL2 ile), veya macOS. CPU ile çalışır; GPU hızlandırma için donanım ve sürücü şarttır.

- Donanım: En az 16 GB RAM öneririm. 8B sınıfı modeller için 6–8 GB VRAM ile kuantize sürümler rahatlar; daha büyük modeller için 12 GB+ VRAM faydalıdır.

- Docker: Linux’ta Docker Engine + Docker Compose plugin; Windows/macOS’ta Docker Desktop.

Hızlı Başlangıç: Docker Compose ile Ollama + Open WebUI

Önce iki kalıcı disk alanı (volume) ve ortak bir ağ üzerinde iki servis ayağa kaldıracağız. Aşağıdaki dosyayı docker-compose.yml adıyla kaydedin:

version: "3.8"
services:
  ollama:
    image: ollama/ollama:latest
    container_name: ollama
    restart: unless-stopped
    ports:
      - "11434:11434"
    volumes:
      - ollama:/root/.ollama
    environment:
      - OLLAMA_KEEP_ALIVE=24h
    # GPU için not: Aşağıdaki 'GPU Hızlandırma' bölümüne bakın.

  openwebui:
    image: ghcr.io/open-webui/open-webui:latest
    container_name: openwebui
    restart: unless-stopped
    ports:
      - "3000:8080"
    environment:
      - OLLAMA_BASE_URL=http://ollama:11434
    volumes:
      - openwebui:/app/backend/data
    depends_on:
      - ollama

volumes:
  ollama:
  openwebui:

Komutlar: Bulunduğunuz klasörde docker compose up -d deyin. Ardından tarayıcıdan http://localhost:3000 adresine giderek Open WebUI arayüzünü açın.

Model İndirme ve Test

Open WebUI içinden model menüsünde “Pull” diyerek llama3:8b, qwen2:7b veya mistral:7b gibi kuantize seçenekleri indirebilirsiniz. Terminal tercih ederseniz:

docker exec -it ollama ollama pull llama3:8b
docker exec -it ollama ollama run llama3:8b "Merhaba, nasılsın?"

Her şey yolundaysa, modelden metin çıktısı almaya hemen başlayabilirsiniz.

GPU Hızlandırma (NVIDIA, AMD, Apple Silicon)

NVIDIA (Linux/WSL2): Sunucuda uyumlu sürücü ve nvidia-container-toolkit kurulu olmalı. Özet akış:

# Sürücü kurulumu sonrası:
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | \
 sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -fsSL https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | \
 sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt-get update
sudo apt-get install -y nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker

Ardından Ollama’yı GPU ile başlatın:

docker run -d --name ollama --gpus all -p 11434:11434 \
  -v ollama:/root/.ollama ollama/ollama:latest

Compose kullanacaksanız, mevcut servisi durdurup bu komutla GPU’lu Ollama kapsayıcısını başlatabilir veya Docker Compose V2’de desteklenen GPU ayarlarını kullanabilirsiniz. Sürüm farklılıkları nedeniyle en sorunsuz yöntem genellikle yukarıdaki docker run satırıdır.

AMD ROCm (Linux): ROCm destekli kart ve sürücülerle Ollama GPU hızlandırma çalışır. Konteyner içinde ROCm erişimi için ROCm runtime ve /dev/kfd, /dev/dri cihazlarına erişim gerekebilir. Pratikte, GPU hızlandırma istiyorsanız ROCm tarafında host’a kurulu Ollama’yı, Open WebUI’yi ise Docker’da tutmak çoğu kullanıcı için daha az sorun çıkarır.

Apple Silicon (macOS): Ollama, Metal ile GPU hızlandırmayı yerel kurulumda otomatik kullanır. Docker konteyneri macOS’ta Metal’e doğrudan erişemeyeceğinden, Mac kullanıcılarına Ollama’yı host’a, Open WebUI’yi Docker’a kurmalarını öneririm. Open WebUI’nin OLLAMA_BASE_URL değerini host’taki Ollama adresine işaret edecek şekilde değiştirin.

Performans İpuçları

- Kuantizasyon: q4_k_m gibi kuantize modeller VRAM/RAM kullanımını dramatik biçimde azaltır. İlk denemelerde 7–8B modellerin kuantize sürümleri idealdir.

- Eşzamanlılık: OLLAMA_NUM_PARALLEL ve OLLAMA_BATCH gibi değişkenlerle paralellik ve batch boyutunu ayarlayabilirsiniz. Makinenizin sınırlarını test ederek en iyi kombinasyonu bulun.

- Disk I/O: Modeller büyük dosyalardır; SSD kullanımı ve yeterli boş alan önemlidir.

Open WebUI Özelliklerine Kısa Bakış

Open WebUI, konuşma geçmişi, istem (prompt) şablonları, çoklu model seçimi, rol tanımları ve RAG için belge yükleme gibi pratik özellikler sunar. Takım içinde kullanacaksanız, kullanıcı oturumları ve rol bazlı ayarlarla basit bir “self-hosted” sohbet platformu oluşturabilirsiniz.

Güvenlik ve Ağ

Bu kurulumu internetten erişilebilir hale getirecekseniz, ters proxy arkasında HTTPS zorunludur. Open WebUI’de dahili oturum açma mekanizmasını etkin tutun. Ollama API’sini (11434) doğrudan dış dünyaya açmayın; sadece iç ağda veya reverse proxy üzerinden eriştirin.

Sorun Giderme

- CUDA hatası: Sürücü ve nvidia-container-toolkit sürüm uyumsuz olabilir. nvidia-smi çıktısını kontrol edin, Docker daemon’ı yeniden başlatın.

- Model çekilemiyor: Ağ veya DNS sorunları; tekrar deneyin, mümkünse farklı bir ağa geçin.

- Open WebUI, Ollama’ya bağlanamıyor: OLLAMA_BASE_URL’in doğru olduğundan emin olun. Compose içinde servis adları (ollama) DNS ile çözülür; dışarıdan erişiliyorsa host IP’sini kullanın.

Sonuç

Docker ile Ollama + Open WebUI kurmak, yerel LLM deneyimini dakikalar içinde erişilebilir kılıyor. GPU hızlandırma ile yanıt süresi ve akıcılık ciddi ölçüde iyileşiyor. Küçük bir masaüstünde bile kuantize modellerle harika sonuçlar alabilir, belgelerinizi yerelde işleyebilir ve verinizi bulut servislerine göndermeden üretkenlik sağlayabilirsiniz. İhtiyacınıza göre 7–8B modellerle başlayıp, daha güçlü bir GPU’ya geçtiğinizde 13B/70B sınıfına doğru kademeli büyüyebilirsiniz.

11 Aralık 2025 Perşembe

Next.js ile WebAuthn (Passkey) Entegrasyonu: Parolasız Giriş Adım Adım

Parolasız kimlik doğrulama, kullanıcı deneyimini iyileştirirken hesap güvenliğini artırmanın en pratik yolu haline geldi. WebAuthn ve Passkey teknolojileri sayesinde kullanıcılar artık SMS kodları veya karmaşık parolalar yerine cihazlarındaki biyometrik sensörlerle giriş yapabiliyor. Bu yazıda, Next.js üzerinde WebAuthn tabanlı Passkey entegrasyonunu adım adım anlatarak, hem teknik detayları hem de üretim ortamı için ipuçlarını paylaşacağım.

Kısaca özetlemek gerekirse, WebAuthn FIDO2 standardının bir parçasıdır ve tarayıcılar aracılığıyla donanımsal (Güvenli Anahtarlar, Touch ID, Windows Hello vb.) veya platform tabanlı kimlik doğrulayıcılarla anahtar çifti üretip doğrulamayı mümkün kılar. RP (Relying Party) ID, origin ve challenge mekanizmaları doğru kurgulandığında, hem phishing dirençli hem de kullanıcı dostu bir giriş deneyimi elde edersiniz.

Önkoşullar ve Mimari

Gerekenler: Node.js 18+, Next.js 13+ (App Router tercihen), HTTPS (yerelde self-signed veya localhost), ve bir sunucu tarafı doğrulama paketi. Toplulukta yaygın kullanılan kütüphanelerden biri @simplewebauthn. Akış şu şekilde işler: Sunucu, kayıt veya giriş için tek kullanımlık bir challenge üretir ve istemciye döner. İstemci, tarayıcıdaki navigator.credentials API’sini (veya sarmalayıcı kütüphane) çağırarak kimlik doğrulayıcıyla işlem yapar ve imzalı yanıtı sunucuya gönderir. Sunucu, public key ve imza doğrulamasıyla işlemi sonuçlandırır.

Kurulum

Yeni bir Next.js projesi oluşturarak başlayalım:

npx create-next-app@latest passkey-demo --typescript
cd passkey-demo
npm i @simplewebauthn/server @simplewebauthn/browser cookie uuid

Basit bir yapı için .env dosyanıza aşağıdaki değerleri ekleyin (yerel geliştirme için):

RP_ID=localhost
RP_NAME=Passkey Demo
ORIGIN=http://localhost:3000

Kayıt (Registration) Akışı

Kullanıcı bir Passkey oluşturmak istediğinde önce sunucudan registration options alır. Bu seçenekler, kimlik doğrulayıcıya hangi parametrelerle anahtar üretmesi gerektiğini söyler. Next.js App Router kullandığınızı varsayarak endpoint’leri şu şekilde düşünebilirsiniz:

// app/api/webauthn/registration/options/route.ts
import { generateRegistrationOptions } from '@simplewebauthn/server';
import { cookies } from 'next/headers';
import { v4 as uuid } from 'uuid';

export async function GET() {
  const userId = uuid(); // Demo için rastgele. Gerçekte gerçek kullanıcı ID'si olmalı.
  const opts = await generateRegistrationOptions({
    rpName: process.env.RP_NAME!,
    rpID: process.env.RP_ID!,
    userID: userId,
    userName: '[email protected]',
    attestationType: 'none',
    authenticatorSelection: {
      residentKey: 'preferred',
      userVerification: 'preferred',
    },
  });

  // Challenge'ı oturumda saklayın
  cookies().set('regChallenge', opts.challenge, { httpOnly: true });

  return new Response(JSON.stringify({ userId, options: opts }), { status: 200 });
}

İstemci tarafında, tarayıcı API’si ile uğraşmayı kolaylaştırmak için @simplewebauthn/browser paketini kullanabiliriz:

// app/register/page.tsx
'use client';
import { startRegistration } from '@simplewebauthn/browser';

export default function Register() {
  const onRegister = async () => {
    const res = await fetch('/api/webauthn/registration/options');
    const { userId, options } = await res.json();
    const attRes = await startRegistration(options);
    await fetch('/api/webauthn/registration/verify', {
      method: 'POST',
      body: JSON.stringify({ userId, attRes }),
    });
    alert('Passkey oluşturuldu!');
  };
  return <button onClick={onRegister}>Passkey Oluştur</button>;
}

Sunucu, istemciden gelen yanıtı doğrulamalıdır. Doğrulama sonunda public key’i ve ilgili metadata’yı kullanıcı profiline kalıcı olarak kaydedersiniz.

// app/api/webauthn/registration/verify/route.ts
import { verifyRegistrationResponse } from '@simplewebauthn/server';
import { cookies } from 'next/headers';

export async function POST(req: Request) {
  const { userId, attRes } = await req.json();
  const expectedChallenge = cookies().get('regChallenge')?.value;

  const verification = await verifyRegistrationResponse({
    response: attRes,
    expectedChallenge,
    expectedOrigin: process.env.ORIGIN!,
    expectedRPID: process.env.RP_ID!,
  });

  if (!verification.verified) return new Response('Doğrulanamadı', { status: 400 });

  // verification.registrationInfo'dan publicKey ve credentialID'yi alıp DB'ye kaydedin
  return new Response('OK', { status: 200 });
}

Giriş (Authentication) Akışı

Girişte benzer şekilde bir challenge üretip istemciye gönderirsiniz. Kullanıcı cihazındaki Passkey ile imzalanan yanıtı sunucu doğrular. Burada kullanıcıya ait kayıtlı credentialID’lerinizi ve public key’lerinizi DB’den çekmeniz gerekir.

// app/api/webauthn/authentication/options/route.ts
import { generateAuthenticationOptions } from '@simplewebauthn/server';
import { cookies } from 'next/headers';

export async function GET() {
  const opts = await generateAuthenticationOptions({
    rpID: process.env.RP_ID!,
    userVerification: 'preferred',
  });
  cookies().set('authChallenge', opts.challenge, { httpOnly: true });
  return new Response(JSON.stringify({ options: opts }), { status: 200 });
}
// app/login/page.tsx
'use client';
import { startAuthentication } from '@simplewebauthn/browser';

export default function Login() {
  const onLogin = async () => {
    const res = await fetch('/api/webauthn/authentication/options');
    const { options } = await res.json();
    const assertion = await startAuthentication(options);
    await fetch('/api/webauthn/authentication/verify', {
      method: 'POST',
      body: JSON.stringify({ assertion }),
    });
    alert('Giriş başarılı!');
  };
  return <button onClick={onLogin}>Passkey ile Giriş</button>;
}
// app/api/webauthn/authentication/verify/route.ts
import { verifyAuthenticationResponse } from '@simplewebauthn/server';
import { cookies } from 'next/headers';

// Not: Burada kullanıcıya ait credential'ları DB'den çekmelisiniz.
export async function POST(req: Request) {
  const { assertion } = await req.json();
  const expectedChallenge = cookies().get('authChallenge')?.value;

  const verification = await verifyAuthenticationResponse({
    response: assertion,
    expectedChallenge,
    expectedOrigin: process.env.ORIGIN!,
    expectedRPID: process.env.RP_ID!,
    authenticator: {
      credentialID: Buffer.from('...'), // DB'den
      credentialPublicKey: Buffer.from('...'), // DB'den
      counter: 0,
    },
  });

  if (!verification.verified) return new Response('Hatalı kimlik doğrulama', { status: 401 });
  return new Response('OK', { status: 200 });
}

Üretime Hazırlık: İpuçları ve En İyi Uygulamalar

- HTTPS zorunlu: WebAuthn, localhost harici kökenlerde güvenli bağlam ister. Prod ortamda gerçek sertifika kullanın.

- RP ID ve Origin uyumu: RP_ID alan adınızla, ORIGIN ise protokol ve host’la birebir eşleşmeli. Alt alan adları ve çoklu çevrelerde yanlış yapılandırma en yaygın hatadır.

- Challenge ve oturum: Challenge değerini kısa ömürlü ve kullanıcıya bağlı saklayın. CSRF ve yeniden oynatma saldırılarına karşı dikkatli olun.

- Kullanıcı deneyimi: Platform authenticator (cihaz içi) ve cross-platform (USB/NFC) anahtarları desteklediğinizden emin olun. Cihaz değişimlerinde çoklu Passkey veya hesap kurtarma akışları tasarlayın.

- Günlükleme ve ölçüm: Başarısız doğrulama nedenlerini (origin uyuşmuyor, RP ID yanlış, kullanıcı doğrulaması başarısız vb.) log’layın; destek taleplerini azaltır.

Sonuç

WebAuthn ve Passkey ile parolasız kimlik doğrulama, hem saldırı yüzeyini küçültür hem de kullanıcı memnuniyetini artırır. Next.js üzerinde @simplewebauthn ile kurulum oldukça hızlıdır: kayıt ve giriş için challenge kurgusu, doğru RP/Origin ayarı ve güvenli oturum yönetimi yapı taşlarını oluşturur. Bu mimariyi uygulayarak uygulamanızda modern, phishing dirençli ve yüksek dönüşüm oranına sahip bir giriş deneyimine geçiş yapabilirsiniz.