đŠ Grafana k6 : les tests de charge Ă©crits comme du code

đ Introduction
Le test de charge a longtemps Ă©tĂ© un projet Ă part : un outil lourd, un plan de test en XML maintenu par une seule personne, une campagne lancĂ©e deux fois par an avant une grosse mise en production. Le rĂ©sultat est prĂ©visible â le test dĂ©rive du code rĂ©el, personne ne le relit, et la rĂ©gression de performance se dĂ©couvre en production.
Grafana k6 prend le problĂšme par lâautre bout. Câest un binaire Go, sans JVM ni interface graphique, qui exĂ©cute des scripts JavaScript. Le test est un fichier .js versionnĂ© Ă cĂŽtĂ© du code, relu en pull request, exĂ©cutĂ© en CI. Le projet est publiĂ© par Grafana Labs sous licence AGPL-3.0, et il couvre HTTP, WebSockets, gRPC et le navigateur via un module dĂ©diĂ©.
Sa version majeure 2.0 est sortie en mai 2026 (la 2.2.0 est la derniĂšre en date au moment oĂč jâĂ©cris ces lignes), avec un nettoyage assumĂ© des vieilles API et un virage net vers lâoutillage dâextensions. Voyons ce que ça donne concrĂštement.
đ Premier test en cinq minutes
Lâinstallation passe par les canaux habituels â dĂ©pĂŽt APT, Homebrew ou image Docker :
# Debian / Ubuntu
curl -fsSL https://dl.k6.io/key.gpg | sudo gpg --dearmor -o /usr/share/keyrings/k6-archive-keyring.gpg
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
# macOS
brew install k6
# Docker
docker pull grafana/k6k6 new gĂ©nĂšre un script de dĂ©part, k6 run lâexĂ©cute :
k6 new
k6 run script.js
k6 run --vus 10 --duration 30s script.jsEt sans rien installer du tout, lâimage officielle lit le script sur lâentrĂ©e standard :
docker run --rm -i grafana/k6 run --vus 10 --duration 30s - <script.jsUn script minimal tient en quelques lignes. La fonction exportĂ©e par dĂ©faut est le corps de lâitĂ©ration : chaque VU (virtual user) la rejoue en boucle pendant toute la durĂ©e du test.
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '30s', target: 20 },
{ duration: '1m30s', target: 10 },
{ duration: '20s', target: 0 }
]
};
export default function () {
const res = http.get('https://test.k6.io');
check(res, { 'status was 200': (r) => r.status === 200 });
sleep(1);
}đŻ ScĂ©narios et executors : modĂ©liser une vraie charge
vus + duration suffit pour un smoke test, pas pour reproduire un trafic réaliste. Les scenarios permettent de faire tourner plusieurs profils de charge en parallÚle, chacun avec son executor. Il y en a six :
| Executor | Ce quâil pilote |
|---|---|
shared-iterations | Un nombre fixe dâitĂ©rations, rĂ©parties entre les VUs |
per-vu-iterations | Un nombre fixe dâitĂ©rations, par VU |
constant-vus | Un nombre fixe de VUs pendant une durée donnée |
ramping-vus | Un nombre de VUs qui varie par paliers |
constant-arrival-rate | Un dĂ©bit dâitĂ©rations constant, quel que soit le nombre de VUs |
ramping-arrival-rate | Un dĂ©bit dâitĂ©rations qui varie par paliers |
La distinction qui compte est celle entre les executors Ă VUs et ceux Ă arrival rate. Avec constant-vus, si lâapplication ralentit, le dĂ©bit de requĂȘtes baisse mĂ©caniquement : le test sâauto-rĂ©gule et masque la dĂ©gradation. Avec constant-arrival-rate, k6 maintient le dĂ©bit demandĂ© et alloue des VUs supplĂ©mentaires si nĂ©cessaire â câest ce quâon veut pour valider un SLO exprimĂ© en requĂȘtes par seconde. Dans le doute, câest le second quâil faut choisir.
đ check, threshold ou expect : lequel utiliser ?
Les trois rĂ©pondent Ă des questions diffĂ©rentes, et câest la source de confusion la plus frĂ©quente chez les gens qui dĂ©couvrent lâoutil.
Un check valide une rĂ©ponse individuelle. Il nâinterrompt rien : un check qui Ă©choue est simplement comptabilisĂ© dans la mĂ©trique checks, et le test continue. Câest un indicateur, pas une assertion bloquante.
Un threshold est un critĂšre de rĂ©ussite du test entier, Ă©valuĂ© sur une mĂ©trique agrĂ©gĂ©e. Câest lui qui dĂ©cide du code de sortie â donc de la couleur du job CI :
export const options = {
thresholds: {
http_req_failed: ['rate<0.01'],
http_req_duration: ['p(95)<200'],
checks: ['rate>0.99']
}
};La forme longue permet en plus dâabandonner le test dĂšs que le seuil est franchi, ce qui Ă©vite de laisser tourner vingt minutes de charge sur un service dĂ©jĂ par terre :
export const options = {
thresholds: {
http_req_duration: [{ threshold: 'p(99) < 10', abortOnFail: true, delayAbortEval: '10s' }]
}
};Enfin, expect vient de la bibliothĂšque k6-testing, dont lâAPI sâinspire de celle de Playwright. Elle apporte des assertions qui font vraiment Ă©chouer lâitĂ©ration, avec des matchers familiers et, cĂŽtĂ© navigateur, des assertions qui rĂ©essaient jusquâau timeout :
import http from 'k6/http';
import { expect } from 'https://jslib.k6.io/k6-testing/0.6.1/index.js';
export default function () {
const res = http.get('https://test-api.k6.io/public/crocodiles/1/');
const data = res.json();
expect(res.status).toBe(200);
expect(res.headers['Content-Type']).toContain('application/json');
expect(data).toHaveProperty('id');
expect(data.age).toBeGreaterThan(0);
}En pratique : check pour observer, threshold pour décider, expect quand le script sert autant de test fonctionnel que de test de charge. Si vous venez de Pest 4 et de son browser testing, le troisiÚme vous parlera immédiatement.
đ§° En CI et dans la stack dâobservabilitĂ©
Deux actions officielles couvrent GitHub Actions â lâune installe le binaire, lâautre lance les tests avec un glob :
jobs:
perf:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: grafana/setup-k6-action@v1
- uses: grafana/run-k6-action@v1
with:
path: |
./tests/api*.jsLe mĂȘme principe sâapplique Ă un pipeline GitLab CI avec lâimage grafana/k6. Le point important nâest pas lâintĂ©gration en elle-mĂȘme mais les thresholds : sans eux, le job est vert quoi quâil arrive.
CĂŽtĂ© rĂ©sultats, --out envoie les mĂ©triques en temps rĂ©el vers une destination externe : json, csv, prometheus-rw, opentelemetry, influxdb, datadog, elasticsearch, et dâautres. Les deux premiers de cette liste sont ceux qui mâintĂ©ressent le plus : ils branchent le test de charge sur lâobservabilitĂ© dĂ©jĂ en place, quâelle repose sur kube-prometheus-stack ou sur SigNoz. On compare alors la charge injectĂ©e et ce que le service en a fait, sur les mĂȘmes dashboards â et on vĂ©rifie ses SLO au lieu de les supposer.
Pour la charge distribuĂ©e, lâopĂ©rateur Kubernetes k6 Operator est passĂ© en 1.0 avec la version 2.0 : il rĂ©partit un mĂȘme script sur plusieurs pods, ce qui se teste trĂšs bien sur un cluster k3s local avant de viser plus gros.
đ§© Extensions et outillage agents
k6 2.0 a beaucoup travaillĂ© lâextensibilitĂ©. Les extensions sont classĂ©es en deux niveaux â officielles, maintenues par Grafana Labs (k6/x/faker, k6/x/sql, k6/x/mqtt, k6/x/dns), et communautaires (k6/x/sse, k6/x/kafka) â et une nouvelle catĂ©gorie dâextensions ajoute des sous-commandes sous le namespace k6 x. Quatre sont livrĂ©es dâoffice :
k6 x explore # parcourir le registre d'extensions
k6 x docs # la doc et les API, en CLI
k6 x mcp # serveur MCP exposant k6 aux agents
k6 x agent init claude-code # cĂąbler k6 dans son assistantk6 x agent init dĂ©pose les fichiers de skill dans lâemplacement attendu par lâĂ©diteur (.claude/skills/, .cursor/rules/âŠ) et fusionne lâentrĂ©e du serveur MCP dans la configuration existante. --all cible tous les Ă©diteurs supportĂ©s, --dry-run montre ce qui serait Ă©crit, k6 x agent status vĂ©rifie lâĂ©tat. La dĂ©marche est la mĂȘme que celle de Laravel Boost : donner Ă lâagent les outils et la doc du projet plutĂŽt que de le laisser inventer une API.
â ïž Quelques prĂ©cautions
- La migration vers la 2.0 casse des choses. Les commandes
k6 pause,k6 resume,k6 scale,k6 statusetk6 loginont disparu, ainsi que lâexecutorexternally-controlledet le modulek6/experimental/redis.options.ext.loadimpactest remplacĂ© paroptions.cloud, et la formek6 cloud script.jscĂšde la place Ăk6 cloud run script.js. Le serveur dâAPI HTTP ne dĂ©marre plus tout seul : il faut dĂ©sormais--addressouK6_ADDRESS. Pour les extensions, le chemin du module Go devientgo.k6.io/k6/v2. - k6 nâest pas un navigateur â sauf quand on utilise explicitement le module
k6/browser. Une requĂȘte HTTP ne dĂ©clenche ni JavaScript, ni chargement dâassets. Un test protocolaire mesure le backend, pas lâexpĂ©rience utilisateur. - Le poste de travail est un facteur limitant. Au-delĂ de quelques milliers de VUs, câest la machine qui sature avant le service testĂ©. Passez sur plusieurs machines (k6 Operator) ou sur lâoffre cloud plutĂŽt que de croire un rĂ©sultat plafonnĂ© par votre laptop.
- Testez ce que vous avez le droit de tester. Un test de charge contre un service tiers, une API SaaS ou un environnement partagĂ© sans accord prĂ©alable, câest un dĂ©ni de service.
đ Conclusion
k6 fait ce que jâattends dâun outil de test de charge en 2026 : un binaire unique, des scripts versionnĂ©s, un code de sortie exploitable en CI, et des mĂ©triques qui partent vers la stack dâobservabilitĂ© existante. Rien de magique â la partie difficile reste de dĂ©finir ce quâon mesure et Ă partir de quand on considĂšre que ça ne va pas.
Si vous dĂ©marrez : un script, un constant-arrival-rate, deux thresholds, et un job CI qui tourne sur la branche principale. Câest peu, mais câest dĂ©jĂ une rĂ©gression de performance dĂ©tectĂ©e avant la mise en production plutĂŽt quâaprĂšs.
đ Liens utiles
/faq
Questions fréquentes
Qu'est-ce que Grafana k6 ?
+
k6 est un outil de test de charge open source (licence AGPL-3.0) développé par Grafana Labs. Le moteur est écrit en Go, mais les tests s'écrivent en JavaScript : on décrit la charge et les assertions dans un script versionné avec le reste du projet.
k6 ou JMeter, lequel choisir ?
+
JMeter se pilote historiquement via une interface graphique et un plan de test XML, k6 via un script JavaScript et une ligne de commande. Si l'objectif est de faire vivre les tests de charge dans un dĂ©pĂŽt Git et de les lancer en CI, k6 s'y prĂȘte mieux ; JMeter garde l'avantage sur l'Ă©tendue des protocoles couverts nativement.
Comment faire échouer un pipeline CI sur une régression de performance ?
+
En déclarant des thresholds dans les options du script, par exemple « http_req_duration: [p(95)<200] ». Si un seuil est dépassé, k6 sort avec un code de retour non nul et le job CI passe au rouge.
Que faut-il retenir de la migration vers k6 2.0 ?
+
Les commandes k6 pause, resume, scale, status et k6 login ont été supprimées, tout comme l'executor externally-controlled et le module k6/experimental/redis. Le serveur HTTP d'API ne démarre plus par défaut : il faut passer --address ou K6_ADDRESS.