9 / 95 · 05 Performance & Chaos Engineering · k6 and Locust: Modern Load Testing Tools← prev⊞ allnext →☰ Read as one page
2.1k6 (Grafana k6)
k6 is a developer-centric load testing tool written in Go with JavaScript scripting. It has become the industry standard for teams practicing shift-left performance testing. Its key strengths are low resource footprint, native CI integration, and a scripting model that developers actually enjoy using.
Why k6 Wins for CI/CD
- Single binary. No JVM, no dependencies. Download and run.
- JavaScript scripting. Familiar syntax for most development teams.
- Threshold-based pass/fail. Define SLOs directly in the test -- if they fail, the exit code is non-zero, and CI fails.
- Built-in metrics export. Stream to Grafana, Datadog, Prometheus, or JSON.
- Scenarios engine. Model multiple traffic patterns in a single test file.
Complete k6 Example: E-Commerce Load Test
// k6-load-test.js -- Realistic e-commerce load test with multiple scenarios
import http from 'k6/http';
import { check, sleep, group } from 'k6';
import { Rate, Trend } from 'k6/metrics';
// Custom metrics for business-specific SLOs
const errorRate = new Rate('errors');
const checkoutDuration = new Trend('checkout_duration');
// Traffic shaping: ramp up, sustain, spike, cool down
export const options = {
scenarios: {
// Steady-state browsing traffic (70% of users)
browse: {
executor: 'ramping-vus',
startVUs: 0,
stages: [
{ duration: '2m', target: 100 }, // ramp up
{ duration: '5m', target: 100 }, // steady state
{ duration: '1m', target: 300 }, // spike
{ duration: '5m', target: 100 }, // back to steady
{ duration: '2m', target: 0 }, // ramp down
],
exec: 'browseProducts',
},
// API consumers hitting the search endpoint (30% of traffic)
api_search: {
executor: 'constant-arrival-rate',
rate: 50, // 50 requests per second
timeUnit: '1s',
duration: '15m',
preAllocatedVUs: 20,
maxVUs: 100,
exec: 'searchAPI',
},
},
thresholds: {
http_req_duration: ['p(95)<500', 'p(99)<1500'], // SLO enforcement
errors: ['rate<0.01'], // <1% error rate
checkout_duration: ['p(95)<3000'], // checkout under 3s
},
};
export function browseProducts() {
group('Homepage', () => {
const res = http.get('https://store.example.com/');
check(res, {
'homepage status 200': (r) => r.status === 200,
'homepage loads under 2s': (r) => r.timings.duration < 2000,
});
errorRate.add(res.status >= 400);
});
sleep(Math.random() * 3 + 1); // realistic think time: 1-4 seconds
group('Product Page', () => {
const productId = Math.floor(Math.random() * 1000) + 1;
const res = http.get(`https://store.example.com/products/${productId}`);
check(res, {
'product page status 200': (r) => r.status === 200,
'has product title': (r) => r.body.includes('<h1'),
});
errorRate.add(res.status >= 400);
});
sleep(Math.random() * 5 + 2); // browsing think time: 2-7 seconds
}
export function searchAPI() {
const queries = ['laptop', 'phone', 'headphones', 'keyboard', 'monitor'];
const query = queries[Math.floor(Math.random() * queries.length)];
const res = http.get(`https://api.store.example.com/search?q=${query}`, {
headers: { 'Authorization': `Bearer ${__ENV.API_TOKEN}` },
});
check(res, {
'search returns 200': (r) => r.status === 200,
'search returns results': (r) => JSON.parse(r.body).results.length > 0,
'search under 300ms': (r) => r.timings.duration < 300,
});
errorRate.add(res.status >= 400);
}
Running k6
# Local execution with default output
k6 run k6-load-test.js
# With environment variables for secrets
k6 run -e API_TOKEN=secret123 k6-load-test.js
# Output results to Grafana Cloud k6
k6 cloud k6-load-test.js
# Output to JSON for CI analysis
k6 run --out json=results.json k6-load-test.js
# Run with a specific scenario only
k6 run --scenario browse k6-load-test.js
k6 Executor Types
Understanding executors is essential for modeling realistic traffic:
| Executor | Controls | Best For |
|---|---|---|
shared-iterations |
Total iterations across all VUs | Quick smoke tests |
per-vu-iterations |
Iterations per VU | Ensuring each VU runs N times |
constant-vus |
Fixed VU count | Steady-state testing |
ramping-vus |
VU count over time | Ramp-up/down patterns |
constant-arrival-rate |
Fixed request rate | SLO validation (RPS-based) |
ramping-arrival-rate |
Request rate over time | Finding the breaking point |
externally-controlled |
Via REST API | Dynamic load adjustment |