Teil unserer Performance & Scalability-Serie
Den vollständigen Leitfaden lesenk6-Lasttest: Führen Sie vor dem Start einen Stresstest für Ihre APIs durch
Der Versand eines Produkts ohne Belastungstest ist ein Glücksspiel. Sie kennen Ihren Bruchpunkt erst, wenn Benutzer ihn für Sie finden – normalerweise während einer Produkteinführung, einem viralen Moment oder einem Verkaufsanstieg. k6 ist das moderne Lasttest-Tool, mit dem Sie Tests in JavaScript schreiben, sie über CI ausführen und Ihre Leistungsobergrenze ermitteln können, bevor es Benutzer tun. Es ist entwicklerfreundlich, ressourceneffizient (k6 verwendet Goroutinen, keine Threads) und lässt sich für Echtzeitmetriken sauber in Grafana und Prometheus integrieren.
Dieser Leitfaden behandelt k6 vom ersten Skript bis hin zu komplexen Multi-Szenario-Lasttests, benutzerdefinierten Metriken, Schwellenwerten, CI-Integration und produktionssicheren Testmustern für Node.js/NestJS-APIs.
Wichtige Erkenntnisse
– k6-Skripte sind JavaScript, werden aber in einer Go-Laufzeit ausgeführt – keine Node.js-APIs (kein
require, keinfs, keinsetTimeout) – Virtuelle Benutzer (VUs) sind gleichzeitige simulierte Benutzer; Iterationen sind einzelne Skriptausführungen
- Legen Sie vor der Ausführung immer Schwellenwerte fest. Bei fehlgeschlagenen Schwellenwerten wird der Test abgebrochen und CI nicht bestanden
- Verwenden Sie Szenarien (Konstant-Vus, Rampen-Vus, Konstant-Ankunftsrate), um reale Verkehrsmuster zu modellieren
- Laden Sie niemals die Testproduktion, ohne sich mit dem operativen Geschäft abzustimmen – testen Sie gegen Staging oder einen Produktionsklon – Die
http_req_duration-Metrik ist Ihre primäre SLA-Metrik – p95 und p99 sind wichtiger als der Durchschnitt- Begrenzen Sie die Rate Ihrer k6-Läufer – der Lasttest-Verkehr sollte Ihr Ratenlimit-Budget nicht erschöpfen
- Verwenden Sie k6 Cloud oder Grafana k6 für verteilte Lasttests über geografische Regionen hinweg
Installation und erstes Skript
# macOS
brew install k6
# Ubuntu/Debian
sudo gpg -k && 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-get update && sudo apt-get install k6
# Windows (via Chocolatey)
choco install k6
Dein erstes k6-Skript:
// k6/homepage-load.js
import http from 'k6/http';
import { check, sleep } from 'k6';
import { Rate } from 'k6/metrics';
// Custom metric: error rate
const errorRate = new Rate('error_rate');
export const options = {
// Ramp up to 50 VUs over 1 minute, hold 2 minutes, ramp down
stages: [
{ duration: '1m', target: 50 },
{ duration: '2m', target: 50 },
{ duration: '1m', target: 0 },
],
thresholds: {
http_req_duration: ['p(95)<500', 'p(99)<1000'], // 95th pct < 500ms
http_req_failed: ['rate<0.01'], // Less than 1% errors
error_rate: ['rate<0.01'],
},
};
const BASE_URL = __ENV.BASE_URL || 'http://localhost:3000';
export default function () {
const res = http.get(`${BASE_URL}/`);
const ok = check(res, {
'status is 200': (r) => r.status === 200,
'response time < 500ms': (r) => r.timings.duration < 500,
'body contains ECOSIRE': (r) => r.body.includes('ECOSIRE'),
});
errorRate.add(!ok);
sleep(1); // 1 second think time between iterations
}
Führen Sie es aus:
k6 run k6/homepage-load.js
k6 run --env BASE_URL=https://staging.ecosire.com k6/homepage-load.js
k6 run --vus 100 --duration 30s k6/homepage-load.js # Quick smoke test
API-Auslastungstest mit Authentifizierung
// k6/api-load.js
import http from 'k6/http';
import { check, group, sleep } from 'k6';
import { Counter, Trend } from 'k6/metrics';
const apiCalls = new Counter('api_calls');
const authDuration = new Trend('auth_duration');
export const options = {
scenarios: {
// Constant arrival rate: 100 requests/second regardless of VU count
constant_load: {
executor: 'constant-arrival-rate',
rate: 100,
timeUnit: '1s',
duration: '3m',
preAllocatedVUs: 50,
maxVUs: 200,
},
},
thresholds: {
http_req_duration: ['p(95)<300', 'p(99)<500'],
'http_req_duration{type:api}': ['p(95)<200'],
http_req_failed: ['rate<0.005'], // 0.5% error budget
},
};
const BASE_URL = __ENV.BASE_URL || 'http://localhost:3001';
// setup() runs once before VUs start
export function setup() {
// Authenticate and return tokens for VUs to use
const loginRes = http.post(`${BASE_URL}/auth/login`, JSON.stringify({
email: __ENV.TEST_EMAIL,
password: __ENV.TEST_PASSWORD,
}), {
headers: { 'Content-Type': 'application/json' },
});
check(loginRes, { 'login successful': (r) => r.status === 200 });
const { accessToken } = loginRes.json();
return { accessToken };
}
export default function (data) {
const { accessToken } = data;
const headers = {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
};
group('Contacts API', () => {
// List contacts
const listRes = http.get(`${BASE_URL}/contacts?page=1&limit=20`, {
headers,
tags: { type: 'api', endpoint: 'contacts-list' },
});
check(listRes, {
'list contacts: 200': (r) => r.status === 200,
'list contacts: has items': (r) => r.json('data') !== null,
});
apiCalls.add(1);
sleep(0.5);
// Create a contact
const createRes = http.post(`${BASE_URL}/contacts`, JSON.stringify({
name: `Load Test User ${Date.now()}`,
email: `load-test-${Date.now()}@example.com`,
}), {
headers,
tags: { type: 'api', endpoint: 'contacts-create' },
});
check(createRes, {
'create contact: 201': (r) => r.status === 201,
});
apiCalls.add(1);
});
sleep(1);
}
// teardown() runs once after all VUs finish
export function teardown(data) {
// Clean up test data if needed
console.log(`Test complete. Data: ${JSON.stringify(data)}`);
}
Ramping VU-Szenarien
Modellieren Sie reale Verkehrsmuster mit mehreren Phasen und Szenarien:
// k6/stress-test.js
export const options = {
scenarios: {
// Scenario 1: Normal traffic baseline
normal_traffic: {
executor: 'ramping-vus',
startVUs: 0,
stages: [
{ duration: '2m', target: 20 },
{ duration: '5m', target: 20 },
],
gracefulRampDown: '30s',
},
// Scenario 2: Sudden spike (marketing campaign goes live)
traffic_spike: {
executor: 'ramping-vus',
startVUs: 0,
startTime: '7m', // Starts after baseline is stable
stages: [
{ duration: '30s', target: 200 }, // Rapid spike
{ duration: '2m', target: 200 }, // Hold spike
{ duration: '30s', target: 20 }, // Fall back
],
},
// Scenario 3: Soak test for memory leaks
soak_test: {
executor: 'constant-vus',
vus: 10,
duration: '30m', // Run for 30 minutes
startTime: '10m',
},
},
thresholds: {
'http_req_duration{scenario:normal_traffic}': ['p(95)<300'],
'http_req_duration{scenario:traffic_spike}': ['p(95)<800'],
http_req_failed: ['rate<0.02'],
},
};
Blog-/Sitemap-Crawling-Test
// k6/sitemap-crawl.js
import http from 'k6/http';
import { check, sleep } from 'k6';
import { SharedArray } from 'k6/data';
// Load URLs from sitemap (pre-fetched and saved as JSON)
const urls = new SharedArray('sitemap urls', function () {
// In real usage, fetch sitemap XML and parse URLs beforehand
// SharedArray is loaded once and shared across all VUs (memory efficient)
return JSON.parse(open('./sitemap-urls.json'));
});
export const options = {
vus: 20,
duration: '5m',
thresholds: {
http_req_duration: ['p(95)<2000'], // Public pages can be slower
http_req_failed: ['rate<0.01'],
},
};
export default function () {
// Each VU picks a random URL from the sitemap
const url = urls[Math.floor(Math.random() * urls.length)];
const res = http.get(url, {
headers: {
// Identify as load tester in logs
'User-Agent': 'k6-load-tester/1.0',
},
timeout: '10s',
});
check(res, {
'status is 200': (r) => r.status === 200,
'no error page': (r) => !r.body.includes('Error'),
'has canonical tag': (r) => r.body.includes('rel="canonical"'),
'response time < 2000ms': (r) => r.timings.duration < 2000,
});
sleep(Math.random() * 2 + 1); // Random 1-3 second think time
}
Benutzerdefinierte Metriken und Schwellenwerte
// k6/checkout-load.js
import http from 'k6/http';
import { check, sleep } from 'k6';
import { Counter, Rate, Trend, Gauge } from 'k6/metrics';
// Custom metrics for business-specific tracking
const checkoutSuccesses = new Counter('checkout_successes');
const checkoutFailures = new Counter('checkout_failures');
const checkoutDuration = new Trend('checkout_duration');
const activeCheckouts = new Gauge('active_checkouts');
export const options = {
stages: [
{ duration: '1m', target: 10 },
{ duration: '3m', target: 30 },
{ duration: '1m', target: 0 },
],
thresholds: {
checkout_duration: ['p(95)<3000'], // Checkout < 3s at p95
checkout_failures: ['count<10'], // Max 10 absolute failures
http_req_duration: ['p(99)<2000'],
},
};
export default function (data) {
activeCheckouts.add(1);
const start = Date.now();
// Step 1: Add to cart
const cartRes = http.post('/api/cart/add', JSON.stringify({
productId: 'prod_odoo_customization',
quantity: 1,
}), { headers: { 'Content-Type': 'application/json' } });
if (!check(cartRes, { 'add to cart: 200': (r) => r.status === 200 })) {
checkoutFailures.add(1);
activeCheckouts.add(-1);
return;
}
sleep(2);
// Step 2: Create checkout session
const checkoutRes = http.post('/api/billing/checkout', JSON.stringify({
cartId: cartRes.json('cartId'),
}), { headers: { 'Content-Type': 'application/json' } });
const success = check(checkoutRes, {
'checkout session created': (r) => r.status === 200,
'has session URL': (r) => r.json('url') !== null,
});
if (success) {
checkoutSuccesses.add(1);
checkoutDuration.add(Date.now() - start);
} else {
checkoutFailures.add(1);
}
activeCheckouts.add(-1);
sleep(1);
}
Grafana-Ausgabeintegration
Streamen Sie k6-Metriken an InfluxDB und visualisieren Sie sie während des Tests in Grafana:
# Run with InfluxDB output
k6 run --out influxdb=http://localhost:8086/k6 k6/api-load.js
# Or use the k6 Grafana integration (k6 v0.45+)
k6 run --out experimental-prometheus-rw k6/api-load.js
# docker-compose.monitoring.yml
services:
influxdb:
image: influxdb:2.7
ports: ["8086:8086"]
environment:
INFLUXDB_DB: k6
INFLUXDB_ADMIN_USER: admin
INFLUXDB_ADMIN_PASSWORD: password
grafana:
image: grafana/grafana:latest
ports: ["3030:3000"]
environment:
GF_AUTH_ANONYMOUS_ENABLED: "true"
volumes:
- ./grafana/dashboards:/etc/grafana/provisioning/dashboards
- ./grafana/datasources:/etc/grafana/provisioning/datasources
CI-Integration
# .github/workflows/ci.yml (load test section)
load-tests:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' # Only on main branch
needs: [deploy-staging]
steps:
- uses: actions/checkout@v4
- name: Install k6
run: |
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-get update && sudo apt-get install k6
- name: Run smoke test (30 VUs, 60s)
run: |
k6 run --vus 30 --duration 60s \
--env BASE_URL=${{ secrets.STAGING_URL }} \
--env TEST_EMAIL=${{ secrets.TEST_EMAIL }} \
--env TEST_PASSWORD=${{ secrets.TEST_PASSWORD }} \
apps/api/k6/api-load.js
- name: Upload k6 results
uses: actions/upload-artifact@v4
if: always()
with:
name: k6-results
path: k6-results/
Leistungsbasislinien
Legen Sie Ausgangswerte fest und warnen Sie vor Regressionen:
| Endpunkt | S. 50 | S. 95 | S. 99 | Fehlerbudget |
|---|---|---|---|---|
GET / (Homepage) | 120ms | 400ms | 800ms | 0,1 % |
GET /api/contacts | 50ms | 150ms | 300ms | 0,5 % |
POST /api/contacts | 80ms | 200ms | 400ms | 0,5 % |
POST /auth/login | 200ms | 500ms | 1000ms | 1% |
POST /billing/checkout | 500ms | 1500ms | 3000ms | 1% |
GET /blog/[slug] | 100ms | 300ms | 600ms | 0,1 % |
Häufig gestellte Fragen
Was ist der Unterschied zwischen VUs und Anfragen pro Sekunde?
VUs (Virtuelle Benutzer) sind gleichzeitige simulierte Benutzer. Jede VU führt Ihre Standardfunktion in einer Schleife aus. Die Anzahl der Anfragen pro Sekunde hängt davon ab, wie schnell jede Iteration ausgeführt wird. Wenn jede VU-Iteration 2 Sekunden dauert (einschließlich Ruhezeit) und Sie über 100 VUs verfügen, erhalten Sie ungefähr 50 RPS. Verwenden Sie den Executor constant-arrival-rate, wenn Sie unabhängig von der Iterationsdauer auf einen bestimmten RPS abzielen müssen.
Sollte ich einen Lasttest für die Produktion oder das Staging durchführen?
Bevorzugen Sie immer die Inszenierung. Bei Produktionslasttests besteht die Gefahr, dass sie sich auf echte Benutzer auswirken, Ratenlimits ausschöpfen, echte Bestellungen aus Testdaten erstellen und echte Webhook-Ereignisse auslösen. Wenn Sie die Produktion testen müssen, tun Sie dies in Zeitfenstern mit geringem Datenverkehr, verwenden Sie Testzahlungstoken und verfügen Sie über einen Rollback-Plan. k6 Cloud unterstützt die geoverteilte Lastgenerierung, sodass Sie die Leistung Ihres CDN-Edge testen können, ohne den Ursprung zu berühren.
Wie gehe ich mit dem Ablauf des Authentifizierungstokens bei langen Tests um?
Für kurze Tests (unter 15 Minuten) erhalten Sie ein Token in setup() und geben es über data an alle VUs weiter. Implementieren Sie bei langen Tests (über 15 Minuten) die Token-Aktualisierung im Testskript: Überprüfen Sie das Token-Alter in der Standardfunktion und aktualisieren Sie es, wenn es abläuft. Speichern Sie das Token in einer lokalen JavaScript-Variable für jede VU.
Was ist der Unterschied zwischen stages und scenarios?
stages ist eine Abkürzung für ein einzelnes ramping-vus-Szenario – gut für einfache Hochlauf-/Halte-/Herunterfahrmuster. scenarios gibt Ihnen die volle Kontrolle: mehrere gleichzeitige Verkehrsmuster, unterschiedliche Ausführer pro Szenario, Schwellenwerte pro Szenario und Szenario-Tagging in Metriken. Verwenden Sie scenarios für realistische Tests mit mehreren Mustern (Grundlinie + Spitze + Einweichen gleichzeitig).
Wie viele VUs kann ein einzelner K6-Prozess verarbeiten?
Ein einzelner k6-Prozess auf moderner Hardware kann je nach Skriptkomplexität und Antwortgröße 5.000–10.000 VUs unterstützen und 50.000–100.000 RPS generieren. Verwenden Sie für höhere Lasten k6 cloud oder führen Sie mehrere k6-Instanzen hinter einem Lastverteiler aus. Jede VU ist extrem leichtgewichtig (eine Goroutine) – k6 verbraucht bei gleicher VU-Anzahl weitaus weniger Ressourcen als JMeter oder Gatling.
Nächste Schritte
Lasttests zeigen das wahre Leistungsprofil Ihrer Anwendung, bevor Benutzer es unter Stress entdecken. Die Muster in diesem Leitfaden – Ramping-Szenarien, benutzerdefinierte Schwellenwerte, CI-Integration und Grafana-Dashboards – bieten Ihnen die Infrastruktur, um Leistungsengpässe kontinuierlich zu finden und zu beheben.
ECOSIRE erstellt leistungsvalidierte NestJS-APIs mit k6-Lasttests, die Homepage, API-Endpunkte, Checkout-Abläufe und vollständige Sitemap-Crawls abdecken. [Entdecken Sie unsere Back-End-Engineering-Services] (/services), um zu erfahren, wie wir vom ersten Tag an für Leistung sorgen.
Geschrieben von
ECOSIRE TeamTechnical Writing
The ECOSIRE technical writing team covers Odoo ERP, Shopify eCommerce, AI agents, Power BI analytics, GoHighLevel automation, and enterprise software best practices. Our guides help businesses make informed technology decisions.
ECOSIRE
Erweitern Sie Ihr Geschäft mit ECOSIRE
Unternehmenslösungen in den Bereichen ERP, E-Commerce, KI, Analyse und Automatisierung.
Verwandte Artikel
Hepsiburada API-Integration mit Odoo: Vollständige Einrichtungsanleitung
Vollständiger Leitfaden zur Integration von Hepsiburada mit Odoo ERP über API. Automatisieren Sie Bestellungen, Lagerbestand und Erfüllung auf dem vertrauenswürdigen Marktplatz der Türkei.
Shopify Integration Hub: So verbinden Sie Shopify im Jahr 2026 mit jedem System
Vollständiger Leitfaden für Shopify-Integrationen: API, Webhooks, Middleware, iPaaS-Methoden. Verbinden Sie Shopify mit ERP-, Buchhaltungs-, CRM-, Marktplätzen- und POS-Systemen.
API-Ratenbegrenzung: Muster und Best Practices
Master-API-Ratenbegrenzung mit Token-Bucket, Schiebefenster und festen Zählermustern. Schützen Sie Ihr Backend mit NestJS Throttler, Redis und realen Konfigurationsbeispielen.
Mehr aus Performance & Scalability
Webhook-Debugging und -Überwachung: Der vollständige Leitfaden zur Fehlerbehebung
Beherrschen Sie das Webhook-Debugging mit diesem vollständigen Leitfaden, der Fehlermuster, Debugging-Tools, Wiederholungsstrategien, Überwachungs-Dashboards und Best Practices für die Sicherheit abdeckt.
Nginx-Produktionskonfiguration: SSL, Caching und Sicherheit
Nginx-Produktionskonfigurationsleitfaden: SSL-Terminierung, HTTP/2, Caching-Header, Sicherheits-Header, Ratenbegrenzung, Reverse-Proxy-Einrichtung und Cloudflare-Integrationsmuster.
Odoo Performance Tuning: PostgreSQL und Serveroptimierung
Expertenleitfaden zur Leistungsoptimierung von Odoo 19. Behandelt PostgreSQL-Konfiguration, Indizierung, Abfrageoptimierung, Nginx-Caching und Serverdimensionierung für Unternehmensbereitstellungen.
Odoo vs Acumatica: Cloud ERP für wachsende Unternehmen
Odoo vs. Acumatica im Vergleich für 2026: einzigartige Preismodelle, Skalierbarkeit, Fertigungstiefe und welches Cloud-ERP zu Ihrem Wachstumskurs passt.
Testen und Überwachen von KI-Agenten in der Produktion
Eine vollständige Anleitung zum Testen und Überwachen von KI-Agenten in Produktionsumgebungen. Behandelt Bewertungsrahmen, Beobachtbarkeit, Abweichungserkennung und Reaktion auf Vorfälle für OpenClaw-Bereitstellungen.
Compliance-Überwachungsagenten mit OpenClaw
Setzen Sie OpenClaw-KI-Agenten für eine kontinuierliche Compliance-Überwachung ein. Automatisieren Sie behördliche Prüfungen, die Durchsetzung von Richtlinien, die Erstellung von Audit-Trails und die Compliance-Berichterstellung.