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.

10 Aralık 2025 Çarşamba

Ollama, LangChain ve ChromaDB ile Yerel RAG Chatbotu Kurulum Rehberi

Özet

Bu rehberde, internet bağlantısına ihtiyaç duymadan çalışan bir RAG (Retrieval-Augmented Generation) chatbotunu yerelde nasıl kurabileceğinizi adım adım anlatıyorum. Kullandığımız bileşenler: Ollama (yerel LLM çalıştırma), LangChain (zincirleme ve orkestrasyon), ChromaDB (vektör veritabanı) ve yerel gömlemeler. Amaç; PDF, teknik doküman veya notlarınızı indeksleyip, LLM modeline belgelerden alıntı yaparak daha doğru yanıtlar ürettirmek.

Neden Yerel RAG?

RAG yaklaşımı, dil modelinin yanıtlarını belgelerinizle zenginleştirerek halüsinasyonları azaltır ve güncel bilgi sağlar. İşin “yerel” tarafı ise verinin bilgisayarınızdan çıkmaması, maliyetin düşük ve sistemin offline çalışabilmesi demek. Özellikle kurumsal dokümanlar, Ar-Ge notları veya gizli veriler için bu yaklaşım idealdir.

Gereksinimler

- 16 GB RAM önerilir (8 GB ile küçük modeller çalışabilir).
- macOS, Linux veya Windows (WSL destekli).
- Python 3.10+.
- Temel terminal bilgisi.
- Bazı modellerde GPU varsa hız artar (Metal/CUDA desteği).

Adım 1: Ollama ve Modelleri Kurma

Ollama, LLM modellerini tek komutla indirip yerelde çalıştırmanızı sağlar. Sisteminizde kurulu değilse resmi talimatları izleyerek kurun. Linux için tipik kurulum şu şekildedir:

curl -fsSL https://ollama.com/install.sh | sh
# Servisi başlat (Linux)
ollama serve

Şimdi bir sohbet modeli ve bir gömleme modeli çekelim. Küçük ve hızlı bir sohbet modeli için Llama 3.1 8B iyi bir başlangıçtır. Gömlemeler için “nomic-embed-text” yaygın ve hızlıdır:

ollama pull llama3.1:8b
ollama pull nomic-embed-text

Model adlarını ihtiyacınıza göre değiştirebilirsiniz (örn. mistral veya phi-3). Büyük modeller daha iyi kalite, daha fazla RAM/GPU kullanımı demektir. İlk denemelerde 7B–8B aralığı pratik bir seçimdir.

Adım 2: Python Ortamını Hazırlama

Proje klasörünüzü oluşturup sanal ortam açın ve gerekli paketleri yükleyin. LangChain ile Ollama’nın entegrasyonu için “langchain-ollama” paketini kullanalım. Vektör veritabanı için Chroma’yı ekleyelim.

python -m venv .venv
source .venv/bin/activate  # Windows: .venv\Scripts\activate
pip install --upgrade pip
pip install langchain langchain-community langchain-ollama chromadb pydantic

Basit metin dosyalarıyla başlayacağız. PDF’ler için ekstra dönüştürücüler (örn. pypdf, unstructured) ekleyebilirsiniz.

Adım 3: Belgeleri İndeksleme ve RAG Zinciri

Aşağıdaki örnekte, “docs/” klasöründeki metin dosyalarını parçalara ayırıp Chroma’ya yükleyeceğiz. Ardından bir sorgu geldiğinde ilgili parçaları geri çağırıp modelin bağlamıyla birlikte yanıtlamasını sağlayacağız.

# rag_app.py
from langchain_community.document_loaders import DirectoryLoader, TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma
from langchain_ollama import ChatOllama, OllamaEmbeddings
from langchain.prompts import PromptTemplate
from langchain.schema import Document

# 1) Belgeleri yükle
loader = DirectoryLoader("docs", glob="**/*.txt", loader_cls=TextLoader, show_progress=True, use_multithreading=True)
docs = loader.load()

# 2) Parçala
splitter = RecursiveCharacterTextSplitter(chunk_size=800, chunk_overlap=120)
chunks = splitter.split_documents(docs)

# 3) Gömleme ve vektör veritabanı
embeddings = OllamaEmbeddings(model="nomic-embed-text")
vectordb = Chroma.from_documents(chunks, embedding=embeddings, collection_name="yerel_rag")

# 4) Retriever ve LLM
retriever = vectordb.as_retriever(search_kwargs={"k": 4})
llm = ChatOllama(model="llama3.1:8b", temperature=0.2)

# 5) İstem şablonu
template = """Aşağıdaki bağlamı KESİNLİKLE dikkate alarak soruyu yanıtla.
Bağlam:
{context}

Soru: {question}
Cevap: """
prompt = PromptTemplate.from_template(template)

def ask(question: str):
    # İlgili parçaları getir
    rel_docs = retriever.get_relevant_documents(question)
    context = "\n\n".join([d.page_content for d in rel_docs])
    # İstemi oluştur ve yanıtı al
    final_prompt = prompt.format(context=context, question=question)
    return llm.invoke(final_prompt).content

if __name__ == "__main__":
    print(ask("Bu belgelerde performans optimizasyonu için öneriler neler?"))

Uygulamayı çalıştırmadan önce “docs/” klasörüne örnek metinler koymayı unutmayın. Eğer PDF kullanacaksanız “pypdf” ile yükleyebilir veya metne dönüştürüp aynı adımları uygulayabilirsiniz.

Performans ve Kalite İpuçları

Gömleme modeli seçimi: “nomic-embed-text” hızlı ve yeterli doğruluk sağlıyor. Çok dilli dokümanlarda “bge-m3” gibi çok dilli gömlemelere bakabilirsiniz.

Parça boyutu ve örtüşme: chunk_size=600–1200, chunk_overlap=80–200 aralığı pratik. Metin türüne göre ayarlayın. Uzun form teknik belgelerde daha büyük parçalar, soru-cevap notlarında daha küçük parçalar işe yarar.

Arama türü: Chroma varsayılan kosinüs benzerliği ile iyi çalışır. Farklı “k” değerlerini deneyin. Çok sayıda belge olduğunda metadata filtreleri eklemek doğruluğu artırır.

İstem mühendisliği: Şablona “Cevabın sonunda kaynak alıntısı ver” gibi kurallar eklemek kullanıcı güvenini artırır. Yanıt formatını (madde işaretleri, kısa özet + detaylar) açıkça belirtin.

Model ve nicemleme: Ollama üzerinde “:q4_0” gibi nicemlenmiş sürümler daha az RAM kullanır ve hızlanır; kalite az da olsa düşebilir. GPU mevcutsa GPU kullanımını etkinleştirmek büyük fark yaratır.

Güvenlik ve Gizlilik

Yerel RAG’in en önemli avantajı verilerin cihazınızda kalmasıdır. Ancak indeks dizininizi şifreli disk üzerinde tutmak, yedekleri güvenli almak ve işletim sistemi düzeyinde erişim izinlerini doğru ayarlamak gerekir. Paylaşılan makinelerde servis portlarını dışa açmayın.

Sorun Giderme

- “Connection refused”: Ollama servisi çalışmıyor olabilir. “ollama serve” komutunu kontrol edin.
- “CUDA/Metal hataları”: Sürücü/güncelleme gerekli olabilir. Geçici olarak CPU ile deneyin.
- “Bellek yetersiz”: Daha küçük model deneyin (ör. 3B–7B) veya nicemlenmiş varyant çekin.
- “Cevaplar ilgisiz”: chunk_size ve k değerlerini ayarlayın, gömleme modelini değiştirin, istemi netleştirin.

Sonuç

Bu kurulumla, Ollama + LangChain + Chroma üçlüsü sayesinde tamamen yerel çalışan bir RAG chatbotu elde ettiniz. Küçük boyutlu bir modelle bile belgeye dayalı arama ve yanıt üretiminde tatmin edici sonuçlar alabilirsiniz. Zamanla daha iyi gömlemeler, gelişmiş istem şablonları ve daha hızlı modellerle doğruluğu ve hızı artırabilirsiniz. Sonraki adım olarak bir REST API ekleyip (ör. FastAPI) web arayüzü veya Slack/Teams entegrasyonu kurmayı düşünebilirsiniz.

9 Aralık 2025 Salı

İnca Gaming Klavye Fabrika Ayarlarına Döndürme

 Bir süredir klavyemin tüm ışıkları gitmişti ve nasıl geri getirebileceğimi merak ediyordum. FN + ESC tuş kombinasyonuna birkaç saniye basılı tuttuktan sonra ışıklar yeniden açılmaya başladı ve renk modlarını istediğim gibi değiştirmeye başladım. Bu harika bir haber benim için, teşekkürler :)

Docker Buildx ile Çok Mimarili Container İmajı Oluşturma ve GitHub Actions ile Yayınlama

Giriş

Docker Buildx, aynı Dockerfile’dan tek komutla AMD64 ve ARM64 gibi birden fazla mimari için imaj üretebilen güçlü bir eklentidir. Özellikle Apple Silicon (M1/M2) makinelere geçiş, edge cihazları ve farklı donanımlara sahip Kubernetes kümeleri göz önüne alındığında, çok mimarili (multi-arch) imaj oluşturmak hem performans hem de dağıtım kolaylığı açısından kritik hale geldi. Bu yazıda, Docker Buildx ile çok mimarili container imajı nasıl oluşturulacağını ve GitHub Actions kullanarak Docker Hub’a otomatik olarak nasıl yayınlanacağını adım adım anlatıyorum.

Neden Çok Mimarili İmaj?

Tek mimariye göre paketlenmiş bir imaj, farklı donanımlarda çalıştırılmak istendiğinde "no matching manifest for linux/arm64" gibi hatalara yol açar. Çok mimarili bir imaj ise manifest listesi sayesinde doğru platformu otomatik seçer. Bu sayede aynı etiketi (ör. latest veya 1.2.0) hem AMD64 sunucularda hem de ARM64 tabanlı cihazlarda sorunsuz kullanabilirsiniz. Sonuç: daha az bakım yükü, daha az sürpriz ve daha yüksek taşınabilirlik.

Önkoşullar

Gereksinimler: Docker 20.10+ (tercihen güncel), Buildx etkin, registry hesabı (Docker Hub veya GHCR). Apple Silicon kullanıyorsanız, emülasyon için QEMU desteği faydalıdır. Buildx çoğu modern Docker kurulumunda hazır gelse de aşağıdaki komutlarla kontrol edebilirsiniz.
docker buildx version
docker buildx ls

Yerelde Buildx ile Başlangıç

Önce bir builder oluşturup onu aktif kullanıma alalım. QEMU emülasyonunu mümkün kılmak için binfmt kurulumunu da yapabiliriz (Linux ortamlarında gerekebilir).
docker run --privileged --rm tonistiigi/binfmt --install all
docker buildx create --use --name multiarch-builder
docker buildx inspect --bootstrap
Builder hazır olduğunda, tek komutla AMD64 ve ARM64 için imaj inşa edip registry’ye push edebiliriz.

Örnek Dockerfile ve Build Komutu

Aşağıdaki Node.js tabanlı örnek Dockerfile, BUILDPLATFORM/TARGETPLATFORM değişkenleriyle platforma duyarlı, küçük ve üretime uygun bir imaj üretir.
# syntax=docker/dockerfile:1.6
FROM --platform=$BUILDPLATFORM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build

FROM --platform=$TARGETPLATFORM node:20-alpine
WORKDIR /app
COPY --from=build /app /app
EXPOSE 3000
CMD ["node", "dist/index.js"]

İmajı çok mimarili olarak inşa edip push etmek için:
docker login
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t KULLANICI_ADI/uygulama:1.0.0 \
-t KULLANICI_ADI/uygulama:latest \
--push .

Önbellek ve SBOM/Provenance

Build sürelerini kısaltmak için registry tabanlı önbellek kullanabilirsiniz. Ayrıca tedarik zinciri güvenliği bağlamında SBOM ve provenance meta verileri üretmek iyi bir pratiktir.
docker buildx build \
--platform linux/amd64,linux/arm64 \
--cache-to=type=registry,ref=KULLANICI_ADI/uygulama:buildcache,mode=max \
--cache-from=type=registry,ref=KULLANICI_ADI/uygulama:buildcache \
--sbom=true --provenance=true \
-t KULLANICI_ADI/uygulama:1.0.0 \
--push .

GitHub Actions ile Otomasyon

CI/CD hattında her push veya tag için otomatik çok mimarili build almak için GitHub Actions iş akışı aşağıdaki gibi olabilir. Docker Hub kullanıcı adı ve token’ınızı repo Secrets bölümüne DOCKERHUB_USERNAME ve DOCKERHUB_TOKEN olarak eklemeyi unutmayın.
name: build-and-push
on:
push:
tags: ["v*"]
jobs:
docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
push: true
platforms: linux/amd64,linux/arm64
tags: ${{ secrets.DOCKERHUB_USERNAME }}/uygulama:latest
cache-from: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/uygulama:buildcache
cache-to: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/uygulama:buildcache,mode=max

Etiketleme ve Sürümleme Stratejisi

Semantik versiyonlama (örn. 1.2.3) ile birlikte major, minor ve latest etiketlerini birlikte yayınlamak yaygın bir pratik. Örneğin v1.2.3 için şu

8 Aralık 2025 Pazartesi

Kubernetes Gateway API ile Ingress’ten Modern Trafik Yönetimine Geçiş (Adım Adım Rehber)

Kubernetes üzerinde uygulama trafiğini yönetirken yıllardır Ingress nesnelerine güvendik. Ancak mikro servisler büyüdükçe, L7/L4 kombinasyonları, çok kiracılı mimariler ve gelişmiş yönlendirme ihtiyaçları için Ingress’in sınırları daha görünür hale geldi. Gateway API, bu boşluğu kapatmak için tasarlanmış, açık standartlara dayanan ve birden fazla sağlayıcı tarafından desteklenen modern bir trafik yönetim modelidir. Bu yazıda, Gateway API’nin ne sunduğunu, nasıl kurulacağını ve Ingress’ten geçişi adım adım anlatıyorum.

Gateway API nedir? Kısa ve net

Gateway API; GatewayClass, Gateway, HTTPRoute, GRPCRoute, TCPRoute gibi özelleştirilmiş kaynak türleri (CRD) sunar. İki ana fark öne çıkar: Birincisi, rollerin ayrımı. Platform ekibi Gateway’leri tanımlayıp kimin kullanacağını belirlerken, uygulama ekipleri yalnızca Route nesneleriyle trafik kurallarını yönetir. İkincisi, zengin eşleşme ve filtre seti sayesinde başlık, yol, hostname, mTLS, oran sınırlama ve header manipülasyonu gibi gelişmiş senaryolar standartlaştırılmıştır.

Önkoşullar ve kurulum

Başlamadan önce Kubernetes 1.25+ sürümünde bir kümeniz ve bir Gateway API uygulaması (NGINX, Istio, Traefik, Cilium vb.) kurulu olmalı. CRD’leri yüklemek için yönetilen küme kullanıyorsanız çoğu sağlayıcıda ön tanımlı gelebilir; değilse CRD’leri kendiniz uygulayabilirsiniz. Örnek: kubectl apply -k "github.com/kubernetes-sigs/gateway-api/config/crd?ref=v1.1.0". Ardından seçtiğiniz kontrolcünün Gateway API desteğini etkinleştirin; örneğin NGINX için GatewayClass genelde nginx veya nginx-experimental olarak gelir, Istio’da istio adıyla görünür.

Adım 1: GatewayClass ve Gateway oluşturma

GatewayClass, kümede hangi kontrolcünün dinleyici oluşturacağını belirler. Platform ekibi tek bir GatewayClass atayıp çoklu Gateway’lere hizmet verebilir. Basitçe söylemek gerekirse, Ingress’teki ingressClassName’in yerini daha güçlü bir kavram alıyor. Gateway ise yük dengeleyicinizin gerçekte nasıl dışa açılacağını tanımlar: portlar, TLS, SNI, HTTP/2, hatta (destek varsa) HTTP/3. Örnek bir yaklaşım: gatewayclass.name: nginx olan bir prod-gateway’i 80 ve 443 portlarında dinleyecek şekilde oluşturun ve AllowedRoutes politikasıyla yalnızca belirli isim alanlarından route kabul edin.

Adım 2: HTTPRoute ile trafiği yönlendirme

Uygulama ekipleri için asıl iş HTTPRoute’ta başlar. Burada hostnames, path eşleşmeleri ve header filtreleriyle kuralları tanımlarsınız. Örneğin api.ornek.com alan adındaki /v1/* yollarını demo-svc:80 servisine yönlendirin, X-Canary: true başlığı geldiğinde trafiğin %10’unu canary sürümüne gönderin. Ek olarak RequestHeaderModifier filtresi ile giden isteklere bir X-Trace-Id eklemek mümkün. Bu tür politikalar, Ingress anotasyonlarına bağımlı olmanıza gerek kalmadan, standart ve taşınabilir bir şekilde tanımlanır.

Adım 3: TLS sonlandırma ve güvenlik

TLS sonlandırmayı Gateway nesnesi üzerinde yönetmek daha tutarlı bir model sunar. certificateRefs ile bir Secret referansı vererek SNI üzerinden birden fazla alan adı için sertifika tanımlayabilirsiniz. mTLS gerekiyorsa, destekleyen uygulamalarda TLSMode: Terminate yerine TLSMode: Passthrough veya karşılıklı TLS doğrulaması yapılandırılabilir. Güvenlik tarafında iki güçlü özellik daha var: AllowedRoutes ile hangi isim alanlarının Gateway’e bağlanabileceği, ReferencePolicy ile hangi kaynakların birbirini görebileceği belirlenir. Böylece çok kiracılı ortamlarda izolasyon korunur.

Ingress’ten Gateway API’ye geçiş

Geçiş için basit bir eşleştirme stratejisi işe yarar: Ingress’teki ingressClassNameGatewayClass, rules.host ve pathsHTTPRoute.rules, tlsGateway.listeners.tls. İlk aşamada mevcut Ingress’i koruyup paralelde bir Gateway + Route tanımı oluşturun. ExternalDNS kullanıyorsanız, yeni yük dengeleyicinin dış IP’si oluşunca DNS’i aşamalı olarak Gateway’e yönlendirin. Canlı geçişte hata oranını düşürmek için HTTPRoute üzerinde ağırlıklandırılmış backend’lerle kademeli trafik geçişi (canary) uygulayın. Son adımda Ingress’i kapatıp gereksiz anotasyonları temizleyebilirsiniz.

Gözlem ve hata giderme

Gateway API, durum ve koşul (conditions) alanlarını zenginleştirerek sorunları teşhis etmeyi kolaylaştırır. kubectl get gatewayclass ile sınıfın Accepted olup olmadığını, kubectl describe gateway ile Programmed ve atanan Addresses değerini kontrol edin. kubectl describe httproute çıktısında Accepted ve ResolvedRefs koşullarını görün; bir kural kabul edilmediyse nedenini burada bulursunuz (ör. izin verilmeyen hostname veya eşleşen Gateway yok). Olaylar (events) sıklıkla eksik Secret, yanlış referans veya port çakışmalarını ele verir.

Performans, uyumluluk ve farklar

Gateway API standarttır fakat her uygulamanın destek seviyesi farklı olabilir. Üretime çıkmadan önce seçtiğiniz kontrolcünün Conformance matrisini inceleyin. HTTP/2 ve (destek varsa) HTTP/3 kullanımında bağlantı ömrü ve yeniden deneme politikalarını gözden geçirin. Varsayılan timeout ve retry davranışları uygulamadan uygulamaya değişebilir; uzun süreli akışlar (streaming) için uygun ayarları HTTPRoute üzerindeki timeouts ve filtrelerle netleştirin. Ayrıca, L4 trafiği için TCPRoute/UDPRoute kullanarak veritabanı veya özel protokoller için aynı yönetişim modelinden faydalanabilirsiniz.

Yaygın hatalar ve pratik ipuçları

En yaygın hatalardan biri, Route’ların Gateway’e bağlanmasına izin vermeyi unutmak. Gateway üzerinde AllowedRoutes.namespaces ayarını netleştirin. İkinci hata, eski Ingress anotasyonlarının aynısını Gateway API’de aramak; çoğu senaryo artık filters ile standartlaştırıldı. Eğer dışa açık bir LoadBalancer bekliyorsanız ancak adres gelmiyorsa, kontrolcünün bulut sağlayıcısı entegrasyonunu ve servis tiplerini doğrulayın. Son olarak, üretim öncesi bir staging Gateway’inde tüm host ve TLS kombinasyonlarını duman testi (smoke test) ile deneyin; SNI yanlışlıkları genelde burada yakalanır.

Sonuç

Gateway API, Kubernetes’te trafik yönetimini bir üst seviyeye taşıyor: net rol ayrımı, güçlü politika dili ve çok sağlayıcılı ekosistem. Ingress’ten görece zahmetsiz bir geçişle başlamak ve ilk kazanımları (daha iyi görünürlük, canary, header bazlı yönlendirme) kısa sürede görmek mümkün. Kümenizde standart, taşınabilir ve güvenli bir ağ katmanı istiyorsanız, Gateway API’yi denemenin tam zamanı.