←Tous les articles
Mer. 12 août 2026·7 min de lecture

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

Chaßne k6 : script JavaScript versionné, exécution par les VUs, métriques exportées vers Prometheus et OpenTelemetry

📚 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/k6

k6 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.js

Et 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.js

Un 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 :

ExecutorCe qu’il pilote
shared-iterationsUn nombre fixe d’itĂ©rations, rĂ©parties entre les VUs
per-vu-iterationsUn nombre fixe d’itĂ©rations, par VU
constant-vusUn nombre fixe de VUs pendant une durée donnée
ramping-vusUn nombre de VUs qui varie par paliers
constant-arrival-rateUn dĂ©bit d’itĂ©rations constant, quel que soit le nombre de VUs
ramping-arrival-rateUn 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*.js

Le 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 assistant

k6 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 status et k6 login ont disparu, ainsi que l’executor externally-controlled et le module k6/experimental/redis. options.ext.loadimpact est remplacĂ© par options.cloud, et la forme k6 cloud script.js cĂšde la place Ă  k6 cloud run script.js. Le serveur d’API HTTP ne dĂ©marre plus tout seul : il faut dĂ©sormais --address ou K6_ADDRESS. Pour les extensions, le chemin du module Go devient go.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.