Site Hızı & Performans

    k6 ile Yük Testi Yapma

    k6 kurulumu, gerçekçi yük senaryosu yazma ve sonuçları p95 üzerinden yorumlama.

    11 dk okuma Güncellendi: 25 Ağustos 2026

    Sitenizi tarayıcıda açıyorsunuz, 300 milisaniyede geliyor, "hızlı" diyorsunuz. Sonra bir kampanya duyurusu yapıyorsunuz, aynı anda beş yüz kişi giriyor ve site çöküyor. Tek kullanıcıyla yaptığınız ölçüm size kapasiteniz hakkında hiçbir şey söylemez; çünkü darboğazlar tek istekte görünmez, eşzamanlılık arttıkça ortaya çıkar. Yük testi tam olarak bu boşluğu doldurur: "kaç eşzamanlı kullanıcıya kadar kabul edilebilir sürede cevap veriyorum" sorusunun cevabını verir.

    k6, bu iş için tasarlanmış modern bir açık kaynak araçtır. Senaryolarınızı JavaScript ile yazarsınız ama motoru Go ile yazılmış olduğu için tek makineden binlerce sanal kullanıcı üretebilir; tarayıcı çalıştırmaz, bu yüzden kaynak tüketimi düşüktür. Bu rehberde k6'yı kurmayı, gerçekçi bir senaryo yazmayı, eşikler tanımlayıp testi başarısız saymayı ve sonuçları p95 üzerinden yorumlamayı anlatacağım. Ayrıca en sık yapılan ölçüm hatalarını ve testten çıkan sonucu neyle eşleştireceğinizi göstereceğim.

    Yük Testi Türleri ve Ne Zaman Hangisi#

    Her test aynı soruyu sormaz. Neyi öğrenmek istediğinize göre yükün şeklini değiştirirsiniz:

    Test türüYük profiliCevapladığı soru
    Duman testi (smoke)1–5 kullanıcı, kısaSenaryom doğru çalışıyor mu
    Yük testi (load)Beklenen trafik, sabitNormal yükte kabul edilebilir miyim
    Stres testi (stress)Kademeli artanKırılma noktam nerede
    Ani yük (spike)Aniden zirveye çıkanKampanya anına dayanır mıyım
    Dayanıklılık (soak)Orta yük, saatlerceBellek sızıntısı var mı

    Sıralama önemlidir. Önce duman testi yapın; senaryonuz hatalıysa (yanlış URL, eksik başlık, oturum açılmıyor) diğer testlerin hepsi çöp veri üretir. Sonra beklenen trafikle yük testi yapın. Kapasitenizi merak ediyorsanız stres testine geçin. Kampanya öncesi ani yük, uzun süreli kararlılık için dayanıklılık testi yapılır.

    Bu ayrım pratikte şuna karşılık gelir: yük testi "bugünkü trafiğimi rahat taşıyor muyum" sorusunu, stres testi ise "kaç kişide kırılıyorum" sorusunu cevaplar. İkisini karıştırmak, sistemi gereksiz yere büyütmenize ya da tam tersine yanlış bir güven duygusuyla kampanyaya girmenize yol açar. Dayanıklılık testi ise farklı bir hastalığı yakalar: tek seferde görünmeyen ama saatler içinde biriken bellek sızıntılarını ve dolan bağlantı havuzlarını. Bu üçünü aynı hafta içinde bir kez çalıştırmak, sisteminiz hakkında aylarca süren tahminlerden daha fazla şey öğretir.

    Bir uyarı: stres testini canlı sunucuda yapmayın. Gerçek ziyaretçilere hizmet veren bir sistemi bilerek kırma noktasına götürmek, testin kendisini bir kesintiye dönüştürür. Hazırlık (staging) ortamı kullanın; mümkün değilse en düşük trafikli saatte ve dikkatle sınırlı yükle çalışın.

    Kurulum ve İlk Test#

    k6 tek bir çalıştırılabilir dosyadır, bağımlılığı yoktur.

    # Debian / Ubuntu
    sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg \
      --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
    echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" \
      | sudo tee /etc/apt/sources.list.d/k6.list
    sudo apt update && sudo apt install k6
    
    # macOS
    brew install k6
    
    # Kurulumu doğrula
    k6 version
    

    En basit senaryo bir dosya ve bir fonksiyondan ibarettir:

    // duman-testi.js
    import http from 'k6/http';
    import { check, sleep } from 'k6';
    
    export const options = {
      vus: 1,           // sanal kullanıcı sayısı
      duration: '30s',  // test süresi
    };
    
    export default function () {
      const res = http.get('https://firmaniz.com/');
    
      check(res, {
        'durum 200': (r) => r.status === 200,
        'sure < 800ms': (r) => r.timings.duration < 800,
        'icerik geldi': (r) => r.body.includes('</html>'),
      });
    
      sleep(1);   // gerçek kullanıcı gibi bekle
    }
    
    k6 run duman-testi.js
    

    sleep(1) satırı ihmal edilmemesi gereken bir detaydır. Onsuz her sanal kullanıcı bir isteği bitirir bitirmez yenisini gönderir; bu, gerçek kullanıcı davranışı değil bir sel saldırısıdır. Gerçek ziyaretçi sayfayı okur, düşünür, sonra tıklar. Bekleme süresi olmayan bir test, kapasiteyi olduğundan çok daha düşük gösterir.

    Gerçekçi Bir Senaryo Yazmak#

    Tek bir ana sayfa isteği gerçek yükü temsil etmez. Gerçek ziyaretçi ana sayfaya girer, bir kategoriye tıklar, bir ürüne bakar, belki arama yapar. Bu akışı group ile modelleyin ve kademeli yük profili tanımlayın:

    // yuk-testi.js
    import http from 'k6/http';
    import { check, group, sleep } from 'k6';
    import { Rate, Trend } from 'k6/metrics';
    
    // Kendi metriklerimiz
    const hataOrani = new Rate('ozel_hata_orani');
    const urunSuresi = new Trend('urun_sayfasi_suresi');
    
    export const options = {
      stages: [
        { duration: '1m', target: 20 },   // 1 dakikada 20 kullanıcıya çık
        { duration: '3m', target: 20 },   // 3 dakika 20 kullanıcıda kal
        { duration: '1m', target: 50 },   // 50'ye çık
        { duration: '3m', target: 50 },   // 3 dakika koru
        { duration: '1m', target: 0 },    // yavaşça indir
      ],
      thresholds: {
        http_req_duration: ['p(95)<1200', 'p(99)<2500'],
        http_req_failed: ['rate<0.01'],       // %1'den az hata
        ozel_hata_orani: ['rate<0.02'],
      },
    };
    
    const BASE = 'https://firmaniz.com';
    
    export default function () {
      group('ana sayfa', () => {
        const res = http.get(`${BASE}/`);
        const ok = check(res, { 'ana sayfa 200': (r) => r.status === 200 });
        hataOrani.add(!ok);
        sleep(Math.random() * 3 + 2);   // 2-5 sn arası düşünme süresi
      });
    
      group('kategori', () => {
        const res = http.get(`${BASE}/urunler?kategori=sunucu`);
        check(res, { 'kategori 200': (r) => r.status === 200 });
        sleep(Math.random() * 3 + 1);
      });
    
      group('urun detay', () => {
        const res = http.get(`${BASE}/urun/ornek-urun`);
        check(res, { 'urun 200': (r) => r.status === 200 });
        urunSuresi.add(res.timings.duration);
        sleep(Math.random() * 4 + 2);
      });
    }
    

    Burada üç şey doğru yapılmıştır. Birincisi kademeli artış: yükü bir anda 50'ye fırlatmak yerine rampa kullanmak, sistemin nerede bozulmaya başladığını görmenizi sağlar. İkincisi rastgele bekleme: Math.random() ile değişen düşünme süreleri, tüm sanal kullanıcıların aynı anda istek göndermesini (senkronizasyon dalgası) engeller. Üçüncüsü eşikler: thresholds tanımlandığında k6, eşik aşılırsa çıkış kodunu sıfırdan farklı döner; böylece testi otomasyona bağlayabilirsiniz.

    Oturum gerektiren bir akış test edecekseniz giriş işlemini setup() içinde bir kez yapıp çerezi paylaşmak yerine, her sanal kullanıcının kendi oturumunu açması daha gerçekçidir:

    export default function () {
      const giris = http.post(`${BASE}/giris`, {
        eposta: `test${__VU}@ornek.com`,
        sifre: __ENV.TEST_SIFRE,
      });
      check(giris, { 'giris basarili': (r) => r.status === 200 || r.status === 302 });
      // k6 çerezleri sanal kullanıcı başına otomatik saklar
    }
    

    Parolayı senaryoya yazmayın; __ENV ile ortam değişkeninden okuyun ve testi TEST_SIFRE=... k6 run yuk-testi.js şeklinde çalıştırın.

    Sonuçları Okumak: Ortalamaya Değil p95'e Bakın#

    Test bitince k6 bir özet tablo basar. Önemli satırlar şunlardır:

         http_req_duration..............: avg=412ms min=88ms med=298ms max=8.42s p(90)=901ms p(95)=1.34s
         http_req_failed................: 0.42%  ✓ 31    ✗ 7328
         http_reqs......................: 7359   40.87/s
         iterations.....................: 2453   13.62/s
         vus............................: 50     min=1  max=50
         ✗ http_req_duration.............: p(95)<1200 -> p(95)=1340ms
    

    avg=412ms görünce rahatlamayın. Ortalama, çok sayıda hızlı isteğin arasında birkaç felaket isteği gizler. Karar verirken p95 ve p99 değerlerine bakın: p95 = 1,34 saniye demek, her yirmi kullanıcıdan birinin 1,34 saniyeden uzun beklediği anlamına gelir. Yüz bin ziyaretli bir günde bu, beş bin kötü deneyim demektir.

    max=8.42s değeri de anlamlıdır; bir yerde ciddi bir takılma yaşanmıştır. Bu genellikle bir kaynağın tükendiği (PHP-FPM süreçleri, veritabanı bağlantı havuzu) ya da bir kuyruğun dolduğu andır.

    Metriklerin anlamları:

    MetrikNe ölçer
    http_req_durationİsteğin toplam süresi (bağlantı hariç bekleme + indirme)
    http_req_waitingİlk bayt süresi — sunucunun düşünme süresi
    http_req_connectingTCP bağlantısı kurma süresi
    http_req_tls_handshakingTLS el sıkışması süresi
    http_req_failedBaşarısız istek oranı
    http_reqsToplam istek ve saniyedeki istek sayısı
    iterationsSenaryonun baştan sona kaç kez çalıştığı

    Teşhis için en değerli ayrım http_req_waiting ile http_req_connecting arasındadır. waiting yüksekse sunucu yavaş düşünüyordur — PHP, veritabanı ya da önbellek işidir. connecting ve tls_handshaking yüksekse mesafe ya da bağlantı kurma maliyeti öndedir; bu tarafı sunucu lokasyonu ve gecikme yazısında ele aldım.

    Sonuçları makine tarafından okunabilir biçimde saklamak isterseniz:

    # JSON özet çıktısı (CI'da karşılaştırmak için)
    k6 run --summary-export=sonuc.json yuk-testi.js
    
    # Her isteği ayrı satır olarak dök (ayrıntılı analiz)
    k6 run --out json=ham-veri.json yuk-testi.js
    

    Test Sırasında Sunucuyu İzlemek#

    Yük testinin asıl değeri, test sırasında sunucuda neyin tükendiğini görmenizdir. Sadece k6 çıktısına bakmak "yavaşladı" der; sebebi söylemez. Test devam ederken sunucuda paralel olarak şunları izleyin:

    # Genel yük ve CPU dağılımı
    top -b -n 1 | head -20
    
    # PHP-FPM süreçleri tavana çarpıyor mu
    watch -n 2 "curl -s http://127.0.0.1/fpm-status | grep -E 'active processes|listen queue|max children'"
    
    # MySQL'de o an ne çalışıyor
    watch -n 2 "mysql -e \"SELECT id,time,state,LEFT(info,80) FROM information_schema.processlist WHERE command!='Sleep' ORDER BY time DESC LIMIT 8\""
    
    # Web sunucusu hata günlüğü
    tail -f /var/log/nginx/error.log
    

    Aradığınız işaretler nettir. max children reached sayacı artıyorsa PHP-FPM havuzunuz dar demektir; hesabı PHP-FPM havuz ayarları yazısında bulabilirsiniz. MySQL süreç listesinde aynı sorgu tekrar tekrar uzun süre görünüyorsa darboğaz veritabanındadır; veritabanı sorguları sayfa hızını nasıl etkiler yazısındaki EXPLAIN yöntemine geçin. CPU %100'e yapışmışsa ve us (user) payı yüksekse işlem gücü sınırındasınız; wa (iowait) yüksekse disk darboğazı vardır.

    En öğretici deney şudur: aynı senaryoyu önce önbellek kapalıyken, sonra tam sayfa önbelleği açıkken çalıştırın. Aradaki fark genellikle şaşırtıcıdır ve önbelleğin neden diğer tüm optimizasyonlardan önce geldiğini gösterir. Katmanların kurulumunu sunucu tarafı önbellekleme katmanları yazısında adım adım anlattım.

    Sık Yapılan Ölçüm Hataları#

    Birincisi, testi hedefle aynı makineden çalıştırmak. k6 de CPU harcar; sunucunun üzerinde çalıştırırsanız test aracı ile uygulama aynı kaynağı paylaşır ve ölçtüğünüz sayı gerçeği yansıtmaz. Testi ayrı bir makineden, tercihen ziyaretçilerinizin bulunduğu ağa benzer bir konumdan çalıştırın.

    İkincisi, düşünme süresi koymamak. sleep() olmayan bir senaryo, her sanal kullanıcıyı sürekli istek gönderen bir bota çevirir. On sanal kullanıcı gerçekte yüzlerce eşzamanlı ziyaretçiye denk gelen bir yük üretir ve "sunucum sadece 10 kullanıcı kaldırıyor" gibi tamamen yanlış bir sonuca varırsınız.

    Üçüncüsü, CDN ya da önbelleğin arkasını ölçmek. Önünde CDN olan bir siteye yük bindirdiğinizde büyük ihtimalle CDN'in uç sunucusunu test edersiniz, kendi sunucunuzu değil. Kaynağı ölçmek istiyorsanız doğrudan origin IP'sine istek gönderin ve Host başlığını elle verin:

    const res = http.get('https://185.12.34.56/', {
      headers: { 'Host': 'firmaniz.com' },
    });
    

    Dördüncüsü, tek bir URL'yi dövmek. Aynı sayfayı defalarca istemek, tam sayfa önbelleğinin sürekli isabet etmesine yol açar ve sistem gerçekte olduğundan çok daha güçlü görünür. Senaryoda farklı ürün, kategori ve arama sayfalarını dolaşın; gerçek trafikte de öyle olur.

    Beşincisi, ısınma süresini sonuca katmak. Testin ilk saniyelerinde OPcache dolmamış, bağlantı havuzları açılmamış, önbellek soğuktur. Bu dönemin yüksek değerleri gerçek performansı temsil etmez. Kademeli rampa kullanmak bunu doğal olarak çözer; sonucu değerlendirirken de rampa dönemini ayrı düşünün.

    Altıncısı, tek koşuya güvenmek. Ağ koşulları, arka plan işleri ve komşu yük ölçümü etkiler. Her testi en az iki kez çalıştırın ve sonuçlar birbirine yakın değilse üçüncüyü çalıştırıp neden değiştiğini araştırın.

    Testi Otomasyona Bağlamak#

    thresholds tanımladığınızda k6 bir kalite kapısına dönüşür: eşik aşılırsa çıkış kodu sıfırdan farklı olur ve otomasyon adımınız kırılır. Bu, performans gerilemelerini yayına almadan yakalamanın en pratik yoludur.

    # Örnek bir iş akışı adımı
    - name: Performans testi
      run: |
        k6 run --quiet \
          --summary-export=k6-sonuc.json \
          tests/yuk-testi.js
      env:
        BASE_URL: https://hazirlik.firmaniz.com
    

    Eşikleri belirlerken gerçekçi olun. Hedefinizi "p95 < 1200 ms" gibi ölçülebilir bir cümleye çevirin ve bu değeri mevcut performansınıza göre değil, kullanıcı deneyimine göre seçin. Sonra her sürümde aynı senaryoyu çalıştırıp p95 değerinin nasıl kaydığını takip edin; asıl değer tek bir ölçümde değil, zaman içindeki eğilimdedir.

    Otomasyonda kullanacağınız senaryo, canlıya benzeyen ama canlı olmayan bir ortama vurmalıdır. Hazırlık ortamınızın donanımı canlıdan farklıysa mutlak sayılar karşılaştırılamaz; o durumda eşiği "geçen sürüme göre %20'den fazla kötüleşme" gibi göreli bir kurala çevirmek daha anlamlı olur.

    Sıkça Sorulan Sorular#

    k6 ücretsiz mi#

    k6'nın komut satırı aracı açık kaynaktır ve ücretsizdir; kendi sunucunuzda sınırsız test çalıştırabilirsiniz. Ücretli olan, sonuçları bulutta saklayan, dağıtık yük üreten ve görselleştiren yönetilen hizmettir. Tek bir makineden yapılan testler için ücretsiz sürüm çoğu senaryoya fazlasıyla yeter.

    Kaç sanal kullanıcı ile test etmeliyim#

    Hedefinizi gerçek verinizden türetin. Analitik aracınızdan en yoğun saatteki eşzamanlı oturum sayısına bakın ve önce onun 1–2 katıyla test edin. Kapasite sınırınızı öğrenmek istiyorsanız kademeli artan bir stres testi yapın ve p95 değerinin kabul edilebilir eşiği aştığı noktayı kırılma noktası olarak not edin. Rastgele büyük sayılarla başlamak yanıltıcıdır.

    Yük testini canlı sitede yapabilir miyim#

    Hafif bir duman testi ya da düşük yoğunluklu bir yük testi dikkatle yapılabilir, ama stres testini canlıda yapmayın; kırılma noktasına kadar zorlamak gerçek ziyaretçiler için kesinti demektir. Mümkünse canlıyla aynı yapılandırmaya sahip bir hazırlık ortamı kullanın. Zorunluysa en düşük trafikli saati seçin, yükü sınırlı tutun ve testi durdurabileceğinizden emin olun.

    Ortalama süre iyi görünüyor ama kullanıcılar şikâyetçi, neden#

    Çünkü ortalama, birkaç çok yavaş isteği çok sayıda hızlı istekle gizler. Karar verirken p95 ve p99 değerlerine bakın: p95 = 1,5 saniye, her yirmi kullanıcıdan birinin bir buçuk saniyeden uzun beklediği anlamına gelir. Kullanıcı deneyimini belirleyen bu uçtaki yüzdedir, ortalama değil.

    JMeter yerine k6 kullanmalı mıyım#

    İkisi de işi yapar; tercih ekibinize bağlıdır. k6 senaryoları JavaScript ile yazılır, sürüm kontrolünde okunaklı durur ve otomasyona kolay bağlanır; kaynak tüketimi düşüktür. JMeter'ın grafik arayüzü ve çok geniş bir eklenti ekosistemi vardır, protokol çeşitliliği daha fazladır. Kodla çalışmayı ve testleri depoda tutmayı seviyorsanız k6 daha rahat gelir.

    Test sonucunda darboğazın nerede olduğunu nasıl anlarım#

    k6'nın http_req_waiting metriği sunucunun düşünme süresidir; bu yüksekse sorun uygulama tarafındadır. Test devam ederken sunucuda paralel izleme yapın: PHP-FPM durum sayfasında max children reached artıyorsa süreç havuzu, MySQL süreç listesinde uzun sorgular varsa veritabanı, top çıktısında iowait yüksekse disk darboğazdır. k6 semptomu, sunucu izleme sebebi söyler.

    k6 tarayıcı çalıştırıyor mu, JavaScript'i işliyor mu#

    Varsayılan olarak hayır. k6 HTTP isteklerini doğrudan gönderir; tarayıcı motoru çalıştırmaz, sayfadaki JavaScript'i işlemez ve alt kaynakları otomatik indirmez. Bu, çok daha az kaynakla yüksek yük üretmesinin sebebidir. Sunucu tarafı kapasitesini ölçmek için doğru davranış budur; tarayıcı tarafı deneyimini ölçmek istiyorsanız ayrı bir gerçek kullanıcı ölçümü yöntemine ihtiyacınız olur.

    Kapanış#

    Yük testi, tahmin ile bilgi arasındaki farktır: kapasitenizi kampanya gününde öğrenmek yerine bugün kontrollü biçimde öğrenirsiniz. Aklınızda kalması gereken dört alışkanlık şudur: senaryoya gerçekçi düşünme süreleri koymak, kararı ortalamaya değil p95 ve p99 değerlerine bakarak vermek, test sırasında sunucuyu paralel izleyip semptomun değil sebebin peşine düşmek, ve eşikleri (thresholds) tanımlayarak testi her sürümde çalışan bir kalite kapısına çevirmek. Bu dördü uygulandığında yük testi tek seferlik bir merak değil, sürekli bir güvence hâline gelir.

    Testin sonucunda kapasite artırmanız gerektiğini görürseniz Clou.TR tarafında ölçeklenebilir seçenekler var. Kaynaklarını ihtiyaca göre büyütebileceğiniz bulut sunucu ve tam performanslı VDS çözümlerimizi inceleyebilir, yoğun trafikli projeler için kurumsal hosting paketlerimize bakabilirsiniz. Yük testi sonrası ayar ve optimizasyon çalışmasını bize bırakmak isterseniz sunucu yönetimi hizmetimiz bu kapsamda destek verir.

    k6Yük TestiPerformans

    Uygulamaya geçmeye hazır mısınız?

    NVMe SSD, ücretsiz SSL ve %99.9 uptime garantisiyle Clou.TR hosting ve sunucu çözümleriyle projenizi hayata geçirin.