Project Oxygen & Ideo-LabIDEO LAB Dashboard 2026

🚀 k6 – Test de Charge "Developer-Centric"

Guide complet IDEO-Lab sur l'outil de test de performance (Go/JS) de Grafana Labs.

1.1

Concept : "Tests as Code"

Go (backend), JS (scripts). Open source (Grafana Labs).

k6 Test de Charge Go
1.2

vs. JMeter / Gatling

Code (JS) vs GUI (XML). Event-Loop vs Thread-per-User.

JMeter Gatling Event-Loop
1.3

Installation

Binaire natif (apt, brew), Docker (grafana/k6).

Install Docker
2.1

Le Script (script.js)

Structure : Contexte init vs Contexte VU.

script.js init VU
2.2

VUs (Virtual Users)

Utilisateurs virtuels. Goroutines (légers).

VU Goroutine
2.3

options (Configuration)

Définition de la charge (vus, duration) dans le script.

options vus duration
3.1

http.get() / http.post()

Le "Sampler" HTTP (Params, Body, Headers).

http Sampler
3.2

check() (Assertion)

Vérification de la réponse (Status 200, Body JSON).

check() Assertion
3.3

thresholds (SLO/SLA)

Critères de réussite (Pass/Fail) du test (p(95), rate).

Thresholds SLO p(95)
4.1

scenarios (Exécuteurs)

options.scenarios. Gestion avancée des VUs.

Scenarios Executors
4.2

Exécuteur : ramping-vus

stages (Ramp-up, Hold, Ramp-down).

ramping-vus stages
4.3

Cycle de Vie (Setup/Teardown)

setup() (1 fois, avant), teardown() (1 fois, après).

setup teardown
5.1

Métriques (Built-in)

http_req_duration, http_req_failed, checks.

Metrics http_req_duration
5.2

Métriques (Custom)

Counter, Gauge, Rate, Trend (le plus utile).

Custom Metrics Trend
5.3

group() (Transactions)

Grouper des requêtes (ex: "Login").

group() Transaction
6.1

Exécution (CLI)

k6 run, -u, -d, -e (Variables d'env).

k6 run CLI -e
6.2

Sorties (Outputs)

--out json, Prometheus, InfluxDB, Datadog.

Output Prometheus
6.3

k6 Cloud (SaaS)

k6 cloud script.js. Scaling distribué, Dashboards.

k6 Cloud SaaS
7.1

k6-browser

Test Front-End (Chromium, Playwright). Core Web Vitals.

k6-browser Frontend
7.2

Extensions (xk6)

Builder (Go). k6-sql (BDD), k6-kafka (Events).

xk6 Go k6-sql
7.3

Script Modèle

Structure d'un bon script (Options, Thresholds, Check).

Cheatsheet Script
1.1 Concept : "Tests as Code"
Qu'est-ce que k6 ?

k6 est un outil de test de charge (load testing) open-source, moderne, acquis par Grafana Labs. Il est conçu pour être "Developer-Centric" (axé sur les développeurs).

Philosophie "Tests as Code"

Contrairement à JMeter (basé GUI), k6 adopte une approche "Tests as Code" :

  1. Les scénarios de test sont écrits en JavaScript (JS) (ES2015+), ce qui les rend faciles à lire, à versionner (Git) et à intégrer.
  2. Le moteur (le "cœur") de k6 est écrit en Go (Golang), le rendant extrêmement performant (binaire unique, faible consommation mémoire).
  3. Il est conçu pour le CI/CD (Intégration Continue).
Haute Performance (Event-Loop)

k6 (comme Gatling ou Node.js) utilise un modèle asynchrone (event-loop). Un seul thread peut gérer des centaines d'utilisateurs virtuels (VUs). (JMeter, à l'inverse, utilise un modèle "thread-per-user", beaucoup plus lourd).

1.2 Comparaison : k6 vs. JMeter
Critèrek6 (Grafana Labs)Apache JMeter
Langage (Cœur)Go (Golang)Java (JVM)
Langage (Script)JavaScript (ES2015+)GUI (Drag & Drop) -> XML (.jmx)
PhilosophieTests as Code (CI/CD natif)GUI-driven (Focus sur l'UI pour la création)
Modèle (VUs)Event-Loop / Asynchrone (Goroutines)Thread-per-User (Bloquant)
Performance (Injecteur)Très Élevée (Faible conso RAM/CPU)Limitée (Haute conso RAM/CPU)
ProtocolesHTTP/S, WebSocket, gRPC, (Browser, SQL via xk6)Extrême (HTTP, JDBC, FTP, TCP, LDAP, JMS...)
Pass/Fail (SLA)Thresholds (Intégré, puissant)Assertions (par requête), Listeners (basique)
RapportsRésumé CLI, JSON/CSV, Intégration (Prometheus, Cloud)Rapport HTML (-e -o), Listeners GUI (lourds)
1.3 Installation (Binaire ou Docker)
Installation Native (Binaire)

k6 est un binaire unique sans dépendance (sauf glibc).

# macOS (via Homebrew)
brew install k6

# Linux (Debian/Ubuntu)
sudo gpg -k ... (Ajouter la clé GPG k6)
echo "deb https://dl.k6.io/deb stable main" | sudo tee ...
sudo apt-get update
sudo apt-get install k6

# Windows (via winget ou Chocolatey)
winget install k6

# Vérifier l'installation
k6 version
Docker (Utilisation CI/CD)

C'est la méthode préférée pour l'intégration continue (GitLab CI, Jenkins, GitHub Actions).

# Syntaxe
docker run -i --rm -v [DOSSIER_LOCAL]:/scripts grafana/k6 [commande]

# Exemple d'exécution
# 1. 'pwd' (Linux/Mac) monte le dossier courant
# 2. '--rm' détruit le conteneur après l'exécution
# 3. 'run /scripts/mon_test.js' exécute le script

docker run -i --rm -v "$(pwd):/scripts" grafana/k6 run /scripts/mon_test.js
2.1 Le Script k6 (init vs VU)

Le script (.js) est le cœur de k6. Il a deux "contextes" d'exécution :

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

// ---------------------------------------------
// 1. CONTEXTE "INIT"
// ---------------------------------------------
// (Exécuté 1 SEULE fois, au début)
// (Utilisé pour importer, charger les fichiers,
//  définir les options)
console.log("Contexte INIT (1 fois)");

export const options = {
  vus: 10,
  duration: '30s',
};

const data = open('./payload.json');

// ---------------------------------------------
// 2. CONTEXTE "VU" (Default Function)
// ---------------------------------------------
// (Exécuté en BOUCLE par chaque VU)
export default function () {
  console.log("Contexte VU (en boucle)");
  
  // La requête (Sampler)
  http.post('https://api.test.com/login', data, {
    headers: { 'Content-Type': 'application/json' },
  });

  // "Think time" (Pause)
  sleep(1); // Pause de 1 seconde
}
2.2 VUs (Virtual Users)

Les VUs (Utilisateurs Virtuels) sont l'équivalent des "Threads" dans JMeter. C'est l'unité de concurrence.

Chaque VU est une goroutine (un thread "léger" Go) qui exécute votre export default function () en boucle, de manière indépendante et parallèle.

Modèle Asynchrone

k6 étant basé sur un event-loop Go, les VUs sont très légers en RAM/CPU. Une seule machine (injecteur) peut simuler des dizaines de milliers de VUs (là où JMeter s'effondrerait à 1000-2000 threads).

2.3 L'objet options (Configuration)

L'objet options (exporté depuis le contexte init) définit la configuration du test de charge (le "comment").

Options Simples (vus / duration)

Pour un test simple (charge fixe).

// Fait un "Ramp-up" de 10 VUs en 5 sec
// Maintient 10 VUs pendant 30 sec
// Fait un "Ramp-down" à 0 en 5 sec
export const options = {
  vus: 10,
  duration: '30s',
  
  // (Optionnel)
  // rampUp: '5s',
  // rampDown: '5s',
};
Options Avancées (stages)

Pour un test par paliers (voir 4.2).

Options de Critères (thresholds)

Pour définir les SLA (voir 3.3).

export const options = {
  thresholds: {
    'http_req_duration': ['p(95)<500'], // P95 < 500ms
    'http_req_failed': ['rate<0.01'],    // Erreurs < 1%
  },
};
3.1 Sampler : k6/http

Le module k6/http est le "Sampler" principal pour les tests web.

http.get(url, [params])
import http from 'k6/http';

export default function () {
  const params = {
    headers: {
      'Authorization': 'Bearer 12345',
      'X-My-Header': 'MyValue',
    },
    tags: {
      name: 'GetUsers', // Tag la métrique
    },
  };
  const res = http.get('https://api.test.com/users', params);
}
http.post(url, [body], [params])
import http from 'k6/http';

export default function () {
  const url = 'https://api.test.com/login';
  const payload = JSON.stringify({
    email: 'user@example.com',
    password: '123',
  });

  const params = {
    headers: {
      'Content-Type': 'application/json',
    },
  };
  
  const res = http.post(url, payload, params);
}
3.2 check() (Assertion)

check() est l'équivalent des "Assertions" (JMeter). C'est ce qui vérifie si la réponse reçue est correcte.

Important : Un check() qui échoue n'arrête pas l'exécution du VU. Il incrémente simplement la métrique checks (en échec).

Exemple de check()
import http from 'k6/http';
import { check } from 'k6';

export default function () {
  const res = http.get('https://api.test.com/users/1');
  
  // 'check' prend la réponse (res) et un objet
  // de vérifications (Titre -> Fonction booléenne)
  check(res, {
    '1. Statut est 200': (r) => r.status === 200,
    
    '2. Le corps contient du texte': (r) => r.body.includes('Alice'),
    
    '3. Le JSON est correct': (r) => {
      // (Test coûteux, uniquement si nécessaire)
      const j = r.json(); 
      return j.id === 1;
    },
  });
}

Résultat (CLI) :

    ✓ 1. Statut est 200
    ✓ 2. Le corps contient du texte
    ✗ 3. Le JSON est correct
      ↳ (r.json().id === 1) returned false
3.3 thresholds (SLO/SLA)

Les Thresholds (Seuils) sont la fonctionnalité la plus importante de k6. C'est ce qui définit les critères de réussite (Pass/Fail) (SLA/SLO) de l'ensemble du test.

Si check() (3.2) vérifie la *correction fonctionnelle*, thresholds vérifie la *performance*.

Syntaxe (dans options)

[métrique]: [conditions (array de strings)]

Exemple Complet
export const options = {
  thresholds: {
    // 1. Métrique Standard (Durée)
    // "p(95)" = 95e percentile. (95% des requêtes
    // doivent être < 500ms).
    'http_req_duration': ['p(95)<500', 'p(99)<1500'],

    // 2. Métrique Standard (Erreurs)
    // "rate" = Taux. (Moins de 1% des requêtes
    // doivent être en échec (non-2xx/3xx)).
    'http_req_failed': ['rate<0.01'],

    // 3. Métrique Standard (Checks)
    // (Plus de 98% de nos "checks" (3.2)
    // doivent réussir).
    'checks': ['rate>0.98'],
    
    // 4. Métrique Custom (voir 5.2)
    // (P90 de notre "Trend" custom < 100ms)
    'my_custom_trend': ['p(90)<100'],
  },
};

Si un seul de ces seuils n'est pas atteint à la fin du test, k6 s'arrêtera avec un code d'erreur (non-zéro), ce qui fera échouer le pipeline CI/CD.

4.1 scenarios (Exécuteurs)

L'objet options simple (vus/duration) est un raccourci. Pour des tests complexes (plusieurs "Thread Groups"), on utilise l'objet scenarios.

Un "Scénario" lie un Exécuteur (Executor) (comment la charge est appliquée) à une fonction JS (ce qu'il faut exécuter).

Types d'Exécuteurs (Executors)
ExécuteurDescription
ramping-vus(Le plus courant) Montée/Palier/Descente basé sur le nombre de VUs. (Voir 4.2).
ramping-arrival-rateMontée/Palier/Descente basé sur le nombre de requêtes/sec (RPS).
constant-vusNombre fixe de VUs (ex: 10 VUs) pendant X temps.
per-vu-iterationsChaque VU exécute le scénario N fois (ex: 10 VUs, 5 itérations = 50 runs).
Exemple (2 Scénarios)
export function adminLogin() { /* ... */ }
export function userSearch() { /* ... */ }

export const options = {
  scenarios: {
    // Scénario 1 (Charge de base)
    search_users: {
      executor: 'ramping-vus',
      exec: 'userSearch', // Fonction à appeler
      stages: [
        { duration: '1m', target: 100 },
      ],
    },
    // Scénario 2 (Spike test)
    login_admin: {
      executor: 'per-vu-iterations',
      exec: 'adminLogin',
      vus: 5,
      iterations: 10,
    },
  },
};
4.2 Exécuteur : ramping-vus (Stages)

L'exécuteur ramping-vus est le plus utilisé pour simuler un test de charge réaliste (montée en charge, palier, descente).

Il est défini par un tableau (array) stages.

Exemple de Scénario (30 min)
export const options = {
  scenarios: {
    my_load_test: {
      executor: 'ramping-vus',
      startTime: '0s',
      
      stages: [
        // 1. Ramp-up (Montée en charge)
        // (De 0 à 100 VUs en 5 minutes)
        { duration: '5m', target: 100 },
        
        // 2. Hold (Palier)
        // (Maintient 100 VUs pendant 20 minutes)
        { duration: '20m', target: 100 },
        
        // 3. Ramp-down (Descente)
        // (De 100 à 0 VUs en 5 minutes)
        { duration: '5m', target: 0 },
      ],
    },
  },
};
4.3 Cycle de Vie (Setup/Teardown)

En plus du code init (1 fois, 1 thread) et VU (N fois, N threads), k6 offre deux fonctions spéciales pour la préparation et le nettoyage.

export function setup()

S'exécute 1 seule fois au tout début du test (après init, avant VU).

Usage : Préparer l'environnement de test (ex: insérer des données en BDD) ou récupérer un token d'authentification global.

La valeur retournée (return) par setup() est passée en argument à la fonction default (VU).

export function teardown(data)

S'exécute 1 seule fois à la toute fin du test (après que tous les VUs aient terminé).

Usage : Nettoyer l'environnement (ex: supprimer les données de test en BDD).

L'argument data (optionnel) est la valeur qui a été retournée par setup().

Exemple de Cycle de Vie
export function setup() {
  // 1. (Exécuté 1 fois)
  const res = http.post('https://api.test.com/auth', { ... });
  const token = res.json().token;
  
  // 2. Passe le token aux VUs
  return { authToken: token };
}

export default function (data) {
  // 3. (Exécuté N fois)
  // 'data' est { authToken: "..." }
  const params = { headers: { 'Authorization': `Bearer ${data.authToken}` } };
  http.get('https://api.test.com/profile', params);
}

export function teardown(data) {
  // 4. (Exécuté 1 fois)
  // 'data' est { authToken: "..." }
  http.post('https://api.test.com/cleanup', ...);
}
5.1 Métriques (Built-in)

k6 collecte automatiquement un ensemble de métriques système, utilisées pour les thresholds et le résumé final.

Nom de la MétriqueTypeDescription
vusGaugeNombre d'utilisateurs virtuels (VUs) actifs.
http_reqsCounterNombre total de requêtes HTTP effectuées.
http_req_durationTrend(La plus importante) Temps de réponse total (ms) (sans le DNS/Connect).
http_req_waitingTrend(TTFB - Time To First Byte). Temps d'attente de la réponse.
http_req_failedRateTaux de requêtes échouées (non-2xx/3xx).
checksRateTaux de check() (assertions) réussis.
data_sentCounterOctets envoyés.
data_receivedCounterOctets reçus.
5.2 Métriques (Custom)

Vous pouvez (et devriez) créer vos propres métriques pour mesurer des aspects spécifiques de votre application.

TypeDescriptionUsage
Counter (Compteur)Une valeur qui ne fait qu'augmenter (ex: total de logins).myCounter.add(1)
Gauge (Jauge)Une valeur qui peut monter ou descendre (ex: nb items dans le panier).myGauge.add(5)
Rate (Taux)Un pourcentage de "vrai" (ex: % de logins réussis).myRate.add(true), myRate.add(false)
Trend (Tendance)(Le plus utile) Stocke des valeurs de temps (ms). Calcule automatiquement Min, Max, Moy, P90, P95.myTrend.add(res.timings.waiting)
Exemple (Trend)
import { Trend } from 'k6/metrics';

// 1. (Contexte INIT) Définir la métrique (Trend)
const loginTrend = new Trend('login_response_time');

export default function () {
  // 2. (Contexte VU) Ajouter une valeur
  const res = http.post('/login', ...);
  loginTrend.add(res.timings.duration);
}

// 3. (Contexte INIT) Définir un seuil
export const options = {
  thresholds: {
    'login_response_time': ['p(95)<800'], // P95 des logins < 800ms
  },
};
5.3 group() (Transactions)

group() est l'équivalent du "Transaction Controller" de JMeter. Il permet de grouper logiquement plusieurs requêtes (Samplers) en une seule action métier.

Fonctionnement

Les métriques (ex: http_req_duration) collectées à l'intérieur du group() sont aussi taguées avec le nom du groupe.

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

export default function () {
  // Action 1: Login
  group('01_Login_Process', function () {
    http.get('https://test.k6.io/login');
    sleep(1);
    const res = http.post('https://test.k6.io/login', ...);
    // (Les métriques de ces 2 requêtes sont taguées
    //  "group:01_Login_Process")
  });

  sleep(5);

  // Action 2: Search
  group('02_Search_Users', function () {
    http.get('https://test.k6.io/search?q=test');
    // (Tagué "group:02_Search_Users")
  });
}

Résultat : Permet de filtrer les résultats (ex: "Quel est le P95 de http_req_duration uniquement pour le groupe '01_Login_Process' ?").

6.1 Exécution (CLI)

L'exécution se fait via la commande k6 run.

Commandes Courantes
# Exécuter un script (utilise les 'options' du .js)
$ k6 run script.js

# Surcharger les 'options' (Test rapide)
# (10 VUs pendant 30 secondes)
$ k6 run -u 10 -d 30s script.js

# Passer des variables d'environnement
# (-e ou --env)
$ k6 run -e MY_API_KEY="12345" script.js
// (Dans le script : __ENV.MY_API_KEY)
Exemple de Sortie (Résumé CLI)
     ✓ 1. Statut est 200

     checks..................: 100.00% ✓ 180   ✗ 0   
     data_received...........: 1.5 MB  50 kB/s
     data_sent...............: 22 kB   725 B/s
     http_req_duration.......: avg=300ms min=150ms med=280ms p(90)=450ms p(95)=490ms
     http_req_failed.........: 0.00%   ✓ 0     ✗ 180 
     http_reqs...............: 180     5.9/s
     vus.....................: 10      min=10  max=10
6.2 Sorties (Outputs) (Intégration Monitoring)

La force de k6 (pour l'observabilité) est sa capacité à "streamer" (envoyer) les résultats en temps réel vers d'autres plateformes (via l'option --out).

Fichiers Locaux
# 1. JSON (format structuré)
k6 run script.js --out json=results.json

# 2. CSV
k6 run script.js --out csv=results.csv
Plateformes de Monitoring (Temps Réel)

Permet de corréler le test de charge (k6) avec la performance de l'infra (Prometheus/Grafana).

# 1. InfluxDB (Push)
k6 run script.js --out influxdb=http://influx-server:8086/k6

# 2. Prometheus (Remote Write - Push)
k6 run script.js --out prometheusremotewrite=http://prom-server/api/v1/write

# 3. Datadog / New Relic (Agent)
# (Nécessite l'agent StatsD (Datadog) ou l'API (New Relic))
k6 run script.js --out datadog
k6 run script.js --out newrelic
6.3 k6 Cloud (SaaS)

k6 Cloud est la plateforme SaaS (payante) de Grafana Labs, conçue pour gérer et scaler les tests k6.

Fonctionnement

Au lieu de k6 run, vous utilisez k6 cloud :

# Authentification (1 fois)
k6 login cloud

# Exécuter le script sur le Cloud
k6 cloud script.js
Avantages
  • Scaling (Distribué) : (L'avantage n°1) Vous n'êtes plus limité par votre injecteur local. Vous pouvez demander à k6 Cloud de simuler 1 Million de VUs depuis 10 régions du monde (AWS, GCP...).
  • Dashboards : Fournit une interface web (Grafana) pour analyser les résultats (Pass/Fail, Graphes).
  • Historique : Stocke l'historique de tous vos tests (comparaison).
  • Intégration CI/CD : Facilite l'intégration dans GitLab/GitHub Actions (ex: "Fail si p95 > 500ms").
7.1 k6-browser (Test Front-End)

k6 (par défaut) est un testeur de protocole (Back-end). Il ne charge pas le HTML, n'exécute pas le JS, ne rend pas le CSS.

Le module k6-browser (inclus dans k6) ajoute le "Real Browser Testing" (Front-end). Il utilise Chromium (via Playwright) pour piloter un vrai navigateur.

Usage : Test Hybride

Permet de mesurer la performance perçue par l'utilisateur (Core Web Vitals) et de combiner des tests L7 (API) et des tests Browser (Front) dans le même scénario.

import { browser } from 'k6/browser';
import { check } from 'k6';

export const options = {
  scenarios: {
    browser_test: {
      executor: 'constant-vus',
      vus: 1,
      duration: '30s',
      options: {
        browser: {
          type: 'chromium',
        },
      },
    },
  },
};

export default async function () {
  const page = await browser.newPage();
  await page.goto('https://test.k6.io/');
  
  check(page, {
    'Page title': p => p.locator('h1').textContent() == 'Welcome!',
  });
  
  // Collecte automatiquement les métriques Core Web Vitals
  // (browser_web_vital_lcp, browser_web_vital_cls...)
  
  await page.close();
}
7.2 Extensions (xk6)

k6 (par défaut) ne supporte pas tout (ex: SQL, Kafka). Pour ajouter des protocoles, il faut recompiler le binaire k6 avec des extensions (écrites en Go).

xk6 est l'outil (le "builder") utilisé pour compiler k6 avec ces extensions.

Exemple (k6-sql)

Objectif : Tester une base de données PostgreSQL.

# 1. (Prérequis) Installer Go, Git

# 2. Installer xk6
$ go install go.k6.io/xk6/cmd/xk6@latest

# 3. Compiler un binaire k6 "personnalisé" (avec l'extension sql)
$ xk6 build --with github.com/loadimpact/k6-extension-sql

# (Résultat : un nouveau binaire "k6" est créé)
Script (sql.js)
import sql from 'k6/x/sql';

const db = sql.open('postgres', 'postgres://user:pass@host/db');

export default function () {
  sql.query(db, 'SELECT * FROM users WHERE id=1;');
}

// 4. Exécuter (avec le binaire personnalisé)
$ ./k6 run sql.js
7.3 Script Modèle (Bonnes Pratiques)

Un script de test de charge "propre" (ex: load-test.js) :

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

// --- 1. CONTEXTE INIT ---
// (Charger les données, définir les métriques custom)
const loginData = JSON.parse(open('./data/users.json'));
const myCustomTrend = new Trend('custom_api_time');

// --- 2. OPTIONS (La Charge) ---
export const options = {
  // Scénario par paliers
  scenarios: {
    my_scenario: {
      executor: 'ramping-vus',
      stages: [
        { duration: '2m', target: 50 }, // Ramp-up
        { duration: '5m', target: 50 }, // Hold
        { duration: '1m', target: 0 },  // Ramp-down
      ],
    },
  },
  
  // Critères de réussite (SLA/SLO)
  thresholds: {
    'http_req_duration': ['p(95)<1000'], // P95 < 1s
    'http_req_failed': ['rate<0.02'],    // Erreurs < 2%
    'checks': ['rate>0.95'],             // Checks > 95%
    'custom_api_time': ['p(90)<800'],
  },
};

// --- 3. SETUP / TEARDOWN (Optionnel) ---
export function setup() {
  // (Ex: Récupérer un token admin)
  return { token: 'abc123' };
}

// --- 4. VU CODE (Le Scénario) ---
export default function (data) {
  // Utiliser la variable d'environnement (passée par -e)
  const host = __ENV.TARGET_HOST || 'https://test.k6.io';
  
  const params = {
    headers: { 'Authorization': `Bearer ${data.token}` },
  };

  group('01_Homepage', function () {
    const res = http.get(`${host}/`, params);
    
    check(res, {
      'Homepage status 200': (r) => r.status === 200,
    });
    
    myCustomTrend.add(res.timings.duration);
    
    sleep(2); // Think time 2 sec
  });
  
  group('02_Profile_Page', function () {
    // (Autre requête...)
  });
}