← blog · 7 Eylül 2026

Yük Testi Araçlarında k6 mı Locust mu: Hangisini Ne Zaman Seçmeli

k6 ve Locust, yük testinin en yaygın iki açık kaynak aracı. Aralarındaki fark hız değil; eşzamanlılık modeli, dağıtık çalışma ve CI entegrasyonu. Hangisini ne zaman seçmeniz gerektiği ve düşülen tuzaklar.

Bir servisin üretim trafiğini kaldırıp kaldıramayacağını öğrenmenin tek güvenilir yolu onu gerçekten yüklemektir. Ama "yük testi yapalım" kararından sonra asıl soru genelde atlanır: hangi araçla, nasıl bir yük profiliyle, hangi metriği izleyerek? Araç seçimi burada teknik bir ayrıntı değil; script'i kimin yazacağını, sonucu kimin okuyacağını ve testin CI hattına nasıl bağlanacağını belirliyor. k6 ve Locust bu işin en yaygın kullanılan iki açık kaynak aracı, ve aralarındaki fark "hangisi daha hızlı" sorusundan çok daha derin.

Önce yük testinin türünü netleştirin

Araç seçmeden önce neyi ölçtüğünüzü netleştirmek gerekir, çünkü "yük testi" tek bir şey değildir:

  • Duman testi (smoke test): birkaç sanal kullanıcıyla script'in ve altyapının çalıştığını doğrulama. Genelde CI'da her deploy'da koşar.
  • Yük testi (load test): beklenen üretim trafiğini simüle edip sistemin bu yükte kabul edilebilir gecikmeyle çalıştığını doğrulama.
  • Stres testi (stress test): sistemi kırılma noktasına kadar zorlayıp nerede ve nasıl bozulduğunu görme.
  • Dayanıklılık testi (soak test): orta yükte uzun süre (saatler) koşturup bellek sızıntısı, bağlantı havuzu tükenmesi gibi zamanla biriken sorunları ortaya çıkarma.
  • Ani yük testi (spike test): trafiğin aniden katlanarak arttığı senaryoları (kampanya, haber) simüle etme.

Bu ayrım önemli çünkü araçların güçlü olduğu yer değişir: kısa ve keskin senaryolarda ham verim önemliyken, saatler süren dayanıklılık testinde araç sürecinin kendisinin kararlılığı öne çıkar.

k6: Go çekirdek, JavaScript script

k6, Grafana Labs tarafından geliştirilen, Go dilinde yazılmış bir yük testi aracıdır. Test senaryoları JavaScript ile yazılır ama araç Node.js çalıştırmaz; JS kodu Go runtime'ı içine gömülü bir motorda yorumlanır. Bunun pratik sonucu şudur: her sanal kullanıcı (VU) bir işletim sistemi thread'i ya da process'i değil, hafif bir goroutine olarak koşar. Bu da tek bir makineden, göreceli olarak az kaynakla, yüksek sayıda eşzamanlı sanal kullanıcı üretebilmeniz anlamına gelir.

k6'nın CI odaklı tasarımı öne çıkan tarafıdır. options.thresholds alanına eşik tanımlarsınız (örneğin p95 gecikme 500ms'nin altında kalsın, hata oranı %1'i geçmesin), bu eşik ihlal edilirse k6 process'i sıfırdan farklı bir çıkış koduyla sonlanır; yani pipeline'ı doğal olarak kırabilirsiniz. Yerleşik metrikler (http_req_duration, http_req_failed, vus, iteration_duration) ekstra kod yazmadan gelir.

import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  stages: [
    { duration: '2m', target: 50 },
    { duration: '5m', target: 50 },
    { duration: '2m', target: 0 },
  ],
  thresholds: {
    http_req_duration: ['p(95)<500'],
    http_req_failed: ['rate<0.01'],
  },
};

export default function () {
  const res = http.get('https://example.com/api/products');
  check(res, { 'status 200': (r) => r.status === 200 });
  sleep(1);
}

k6 run script.js ile koşar. Sonuçları terminale özet olarak basar, ama daha zengin gözlemlenebilirlik için Prometheus remote-write veya InfluxDB'ye akıtıp Grafana'da izlemek yaygın kullanım şeklidir.

k6'nın kısıtı JS motorunun sınırlı olması: Node.js paket ekosistemine erişemezsiniz, yalnızca k6'nın sunduğu modülleri (k6/http, k6/ws, k6/grpc, deneysel tarayıcı modülü) ve birkaç kütüphaneyi kullanabilirsiniz. Karmaşık, protokole özgü, çok adımlı bir iş akışını (örneğin özel bir binary protokolle konuşan bir sistem) test etmeniz gerekiyorsa bu kısıt sizi zorlar; Go tarafında xk6 ile özel eklenti yazmak mümkün ama bu artık ayrı bir geliştirme işi. Bir diğer önemli nokta: açık kaynak k6 tek makinede koşar, birden fazla makineye dağıtık yürütme yerleşik değildir; bunu ya kendiniz orkestre edersiniz (birden fazla k6 instance'ı başlatıp sonuçları birleştirmek) ya da Kubernetes üzerinde k6 Operator kullanırsınız ya da ücretli k6 Cloud'a geçersiniz.

Locust: Python ekosistemi, yerleşik dağıtık çalışma

Locust, senaryoları düz Python koduyla yazdırır. Bir kullanıcı davranışı HttpUser sınıfından türeyen bir sınıfla, istekler @task dekoratörlü metodlarla tanımlanır:

from locust import HttpUser, task, between

class ApiUser(HttpUser):
    wait_time = between(1, 3)

    @task(3)
    def list_products(self):
        self.client.get("/api/products")

    @task(1)
    def get_product_detail(self):
        self.client.get("/api/products/42")

locust -f locustfile.py --host https://example.com çalıştırıldığında varsayılan olarak bir web arayüzü açılır: canlı grafiklerle kullanıcı sayısını artırıp azaltabilir, testi durdurup başlatabilirsiniz. CI'da bu arayüze gerek yoksa --headless -u 50 -r 5 -t 5m bayraklarıyla başsız çalıştırılır.

Locust'un asıl gücü Python olması. İstek göndermeden önce bir imza hesaplamak, bir mesaj kuyruğuna yazmak, özel bir SDK çağırmak gerekiyorsa bunu yapan herhangi bir Python kütüphanesini import edip kullanabilirsiniz; bu k6'nın JS sandbox'ında çok daha zahmetli ya da imkansız olabilecek bir esnekliktir. Eşzamanlılık modeli gevent üzerine kurulu: her "kullanıcı" bir işletim sistemi thread'i değil bir greenlet'tir, bu da Python'un GIL'ine rağmen tek process'ten binlerce eşzamanlı kullanıcı simüle edebilmenizi sağlar. Ama gevent'in monkey-patch mekanizmasına dayanır; gevent-uyumlu olmayan, engelleyici (blocking) bir kütüphane kullanırsanız o çağrı tüm worker'ı durdurabilir, bu sessizce olur ve teşhisi zaman alır.

Dağıtık çalışma Locust'ta yerleşiktir ve ek bir orkestrasyon katmanı gerektirmez: locust --master bir koordinatör başlatır, birden fazla makinede locust --worker --master-host=<master-ip> ile worker'lar bağlanır, yük otomatik dağıtılır. Büyük ölçekli, çok makineli testler için bu, k6'nın açık kaynak sürümüne göre belirgin bir avantaj.

Hangi eksende gerçekten ayrışıyorlar

Ham verim tarafında k6 genelde öne çıkar: goroutine'lerin bellek ve zamanlayıcı yükü, greenlet'lere göre daha düşüktür, bu yüzden aynı donanımdan k6 tipik olarak daha yüksek istek/saniye üretebilir. Ama bu fark küçük ve orta ölçekli testlerde çoğu zaman görünmez kalır; asıl belirleyici olan sizin test senaryonuzun CPU-yoğun mu (JSON parse, şifreleme) yoksa çoğunlukla I/O bekleyen mi olduğu.

CI entegrasyonu tarafında k6 daha doğal: eşikler kod içinde tanımlı, çıkış kodu pipeline'a bağlanmaya hazır, ek bir script yazmanıza gerek yok. Locust'ta eşdeğerini elde etmek için ya locust --headless --exit-code-on-error gibi bayrakları ya da event hook'larıyla (request event'i) kendi eşik mantığınızı yazmanız gerekir.

Keşif ve canlı gösterim tarafında Locust'un web arayüzü gerçek bir avantaj: bir demo sırasında ya da bir yayına alma penceresinde kullanıcı sayısını canlı artırıp sistemin nasıl tepki verdiğini izlemek isterseniz bunu kutu dışı alırsınız. k6'da eşdeğer bir deneyim için Grafana dashboard'u kurmanız gerekir.

Protokol kapsamı tarafında k6 HTTP/1.1, HTTP/2, WebSocket ve gRPC'yi yerleşik modüllerle destekler; Locust varsayılan olarak HTTP'ye odaklıdır ama "sadece Python kodu" olduğu için herhangi bir protokolü kendi client'ınızı yazarak test edebilirsiniz. Yani Locust'ta protokol desteği araçtan değil sizin yazdığınız koddan gelir.

Tuzaklar

Düşünme süresini (think time) atlamak. Sanal kullanıcıya sleep/wait_time koymadan döngüde istek attırmak, gerçek kullanıcı davranışını değil "mümkün olduğunca hızlı DoS" senaryosunu test eder. Sonuçlar gerçekçi değildir ve genellikle olduğundan kötü çıkar.

Yük üreten makinenin kendisinin darboğaz olması. Test client'ının CPU'su, ağ bant genişliği ya da dosya tanıtıcı (file descriptor) limiti dolarsa ölçtüğünüz şey sunucunun değil, test makinesinin kapasitesidir. Testi çalıştırırken client makinenin kendi kaynak kullanımını da izleyin.

Bağlantı yeniden kullanımı varsayımı. k6 varsayılan olarak TCP bağlantılarını yeniden kullanır (gerçek bir tarayıcı gibi); soğuk kullanıcı (her istek yeni bağlantı açan) senaryosunu simüle etmek istiyorsanız bunu açıkça (noConnectionReuse) belirtmeniz gerekir, aksi halde sonuçlar TLS handshake maliyetini gizler.

Sunucu tarafı metrikle korelasyon kurmamak. İstemci tarafından ölçülen gecikme size "ne kadar yavaş" der ama "neden yavaş" demez. Yük testini veritabanı bağlantı havuzu, CPU, kuyruk derinliği gibi sunucu metrikleriyle aynı zaman ekseninde okumadan yorum yapmak yanıltıcı sonuçlara götürür.

Prod'da habersiz test koşmak. Otomatik ölçeklemeyi tetikleyip beklenmedik maliyet çıkarabilir ya da bir WAF/hız sınırlayıcıyı tetikleyip 429 fırtınasını uygulama hatası sanabilirsiniz. Test öncesi ilgili ekiplere haber verin, mümkünse hız sınırlayıcıyı test kaynağı için geçici olarak gevşetin.

Ne zaman ikisini de kullanmamalısınız

Her iki araç da HTTP/protokol seviyesinde test eder, gerçek bir tarayıcıda sayfa render etmez. Bir tek sayfa uygulamasının JavaScript çalıştırma süresi, CSS render maliyeti ya da üçüncü parti script'lerin gerçek kullanıcı deneyimine etkisini ölçmek istiyorsanız k6'nın deneysel tarayıcı modülü ya da Playwright gibi gerçek tarayıcı otomasyonu daha doğru sonuç verir; ancak bu yaklaşım çok daha ağırdır ve aynı donanımdan çok daha az eşzamanlı "kullanıcı" üretebilirsiniz. Load testing araçları "API ne kadar yük kaldırır" sorusuna, tarayıcı otomasyonu "kullanıcı gerçekte ne yaşar" sorusuna cevap verir; ikisini birbirinin yerine koymayın.

Seçim basit bir kurala indirgenebilir: CI hattına gömülü, eşik bazlı, tekrar eden yük testleri için k6'yı tercih edin. Karmaşık iş mantığı içeren senaryolar, ekip Python'a zaten aşinaysa ya da çok makineli dağıtık koşumu kutudan istiyorsanız Locust'u seçin. İkisini aynı organizasyonda farklı amaçlarla bir arada kullanmak da tamamen makul bir sonuçtur.