Écrit par Mouloud H.
Définitions des types de tests
Avant de plonger dans l’utilisation de K6, il est important de comprendre les différents types de tests qu’il permet de réaliser :
- Smoke testing : Un test rapide visant à valider que le système ou le script fonctionne correctement sous une charge minimale. Souvent appelé “test de santé”, il s’agit d’une méthode préliminaire pour vérifier que les fonctionnalités essentielles de l’application fonctionnent correctement avant de procéder à des tests plus approfondis. Cela permet de s’assurer qu’il n’y a pas de problèmes critiques, comme des erreurs empêchant l’application de démarrer ou de répondre correctement à des requêtes simples.
- Load testing : Ce type de test mesure les performances du système sous une charge normale ou attendue. L’objectif est de vérifier comment le système se comporte dans des conditions standard d’utilisation.
- Stress testing : Ici, le test pousse le système au-delà de ses limites normales pour évaluer sa stabilité et sa résilience sous une charge extrême.
- Soak testing : Aussi appelé test d’endurance, il évalue les performances et la fiabilité du système sur une période prolongée, permettant d’identifier les éventuelles fuites de mémoire ou autres problèmes de stabilité à long terme.
Qu’est-ce que K6 ?
K6 est un outil de test de charge open source, conçu pour les développeurs et les équipes DevOps. Il permet de simuler des charges réalistes sur des applications web et des API, afin de tester leurs performances et leur évolutivité. Développé en Go et racheté par Grafana Labs en 2021, K6 offre une solution complète pour détecter les régressions de performance et construire des systèmes robustes.
Pourquoi utiliser K6 ?
- Simplicité d’utilisation : Facile à installer et à utiliser, avec une prise en main rapide grâce à JavaScript pour la rédaction des scripts.
- Haute performance : Consomme moins de ressources que les outils basés sur la JVM comme Gatling ou JMeter.
- Polyvalence : Convient pour différents types de tests, comme le smoke testing, le load testing, le stress testing ou encore le soak testing.
- Visualisation des données : Compatible avec des outils comme Grafana et Datadog pour des tableaux de bord interactifs et un suivi en temps réel.
- Open source et gratuit : Accessible à tous sans frais, avec une documentation complète.
Fonctionnalités principales
K6 se distingue par plusieurs fonctionnalités majeures, qui en font un outil très polyvalent et puissant pour les tests de performance :
- Tests de charge : Cette fonctionnalité permet de simuler des milliers d’utilisateurs simultanés pour évaluer la résilience de vos applications. Par exemple, vous pouvez identifier comment votre serveur répond lorsqu’un grand nombre d’utilisateurs accèdent simultanément à une API ou à un site web. C’est essentiel pour anticiper les comportements lors de pics d’activité ou lors d’événements promotionnels importants.
- Tests de performance : Avec K6, vous pouvez analyser les goulets d’étranglement dans vos systèmes. Cela inclut la détection de lenteurs dans les bases de données, les appels réseau ou encore les API tierces. Grâce à des métriques détaillées, comme le temps moyen de réponse ou les percentiles, vous obtenez une vue complète des performances de votre application sous diverses charges.
- Tests fonctionnels : Validez les comportements attendus de vos API même sous des charges importantes. Par exemple, vous pouvez vous assurer que les réponses aux requêtes respectent bien les spécifications (codes HTTP, contenu attendu, etc.), garantissant ainsi une qualité de service optimale.
- Métriques personnalisées : K6 permet de définir vos propres métriques en fonction de vos besoins spécifiques. Par exemple, vous pouvez mesurer le taux d’erreur, le temps de traitement d’une requête ou encore le pourcentage de requêtes réussies. Ces métriques offrent une flexibilité et une précision accrues pour vos analyses.
- Rapports détaillés : Les rapports générés par K6 incluent des données complètes sur les performances de vos tests. Vous pouvez visualiser les tendances, les erreurs et les temps de réponse sous différentes formes, facilitant ainsi la prise de décision pour optimiser votre application.
- Gestion des scénarios avancés : K6 offre une configuration flexible des scénarios de test, permettant de simuler des comportements proches de ceux des utilisateurs réels. Cela inclut la possibilité de moduler progressivement la charge, d’ajouter des délais entre les actions des utilisateurs ou de tester différentes étapes d’un processus utilisateur complexe.
Prise en main avec K6
Installation
Pour installer K6, utilisez le gestionnaire de paquets approprié à votre système :
Sous macOS
brew install k6
Sous Linux
sudo apt update && sudo apt install -y k6
Sous Windows
Téléchargez l’exécutable et ajoutez-le à votre PATH.
Création d’un script de test
Un script K6 est écrit en JavaScript ES6. Voici un exemple simple pour tester une API :
import http from 'k6/http';
import { check } from 'k6';
export default function () {
const res = http.get('http://localhost:3000/slow');
check(res, {
'status est 200': (r) => r.status === 200,
});
}
Présentation du serveur Express
Pour les tests réalisés avec K6, nous avons utilisé un petit serveur Express local qui simule deux types de requêtes : une requête rapide et une requête lente. Ce serveur est conçu pour servir de base à nos tests de charge et de performance.
Voici le code de ce serveur :
const express = require('express');
const app = express();
const port = 3000;
app.get('/fast', (req, res) => {
setTimeout(() => res.send('Fast response!'), 50);
});
app.get('/slow', (req, res) => {
setTimeout(() => res.send('Slow response!'), 2000);
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
Exécution d’un test
Pour exécuter le script ci-dessus, utilisez la commande suivante :
k6 run script.js
Le résultat obtenu est le suivant :
checks.........................: 100.00% 1 out of 1
data_received..................: 241 B 120 B/s
data_sent......................: 84 B 42 B/s
http_req_blocked...............: avg=1.26ms min=1.26ms med=1.26ms max=1.26ms p(90)=1.26ms p(95)=1.26ms
http_req_connecting............: avg=210µs min=210µs med=210µs max=210µs p(90)=210µs p(95)=210µs
http_req_duration..............: avg=2s min=2s med=2s max=2s p(90)=2s p(95)=2s
{ expected_response:true }...: avg=2s min=2s med=2s max=2s p(90)=2s p(95)=2s
http_req_failed................: 0.00% 0 out of 1
http_req_receiving.............: avg=224µs min=224µs med=224µs max=224µs p(90)=224µs p(95)=224µs
http_req_sending...............: avg=121µs min=121µs med=121µs max=121µs p(90)=121µs p(95)=121µs
http_req_tls_handshaking.......: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s
http_req_waiting...............: avg=2s min=2s med=2s max=2s p(90)=2s p(95)=2s
http_reqs......................: 1 0.497928/s
iteration_duration.............: avg=2s min=2s med=2s max=2s p(90)=2s p(95)=2s
iterations.....................: 1 0.497928/s
vus............................: 1 min=1 max=1
vus_max........................: 1 min=1 max=1
Définition des résultats générés
- checks : Indique le pourcentage et le nombre de vérifications réussies.
- data_received : Volume total de données reçues par le script.
- data_sent : Volume total de données envoyées par le script.
- http_req_blocked : Temps passé à établir une connexion (y compris l’attente si toutes les connexions disponibles sont utilisées).
- http_req_connecting : Temps passé à établir la connexion TCP.
- http_req_duration : Durée totale de la requête, incluant le temps d’attente pour une réponse et l’échange de données.
- http_req_failed : Pourcentage de requêtes HTTP ayant échoué.
- http_req_receiving : Temps pris pour recevoir les données après qu’une réponse a commencé.
- http_req_sending : Temps pris pour envoyer les données au serveur.
- http_req_tls_handshaking : Temps pris pour établir une connexion TLS (si applicable).
- http_req_waiting : Temps d’attente avant de recevoir la première réponse du serveur.
- http_reqs : Nombre total de requêtes HTTP effectuées.
- iteration_duration : Temps total pris pour exécuter une itération complète du script.
- iterations : Nombre total d’itérations exécutées pendant le test.
- vus : Nombre d’utilisateurs virtuels actifs pendant le test.
- vus_max : Nombre maximal d’utilisateurs virtuels pendant le test.
Exemples de tests
Smoke Test
import http from 'k6/http';
import { sleep } from 'k6';
export default function () {
http.get('http://localhost:3000/fast');
sleep(1);
}
Résultat attendu
Voici un exemple de résultat obtenu lors de l’exécution d’un Smoke Test :
scenarios: (100.00%) 1 scenario, 1 max VUs, 10m30s max duration (incl. graceful stop):
* default: 1 iterations for each of 1 VUs (maxDuration: 10m0s, gracefulStop: 30s)
data_received..................: 241 B 221 B/s
data_sent......................: 84 B 77 B/s
http_req_blocked...............: avg=7.09ms min=7.09ms med=7.09ms max=7.09ms p(90)=7.09ms p(95)=7.09ms
http_req_connecting............: avg=189µs min=189µs med=189µs max=189µs p(90)=189µs p(95)=189µs
http_req_duration..............: avg=74.24ms min=74.24ms med=74.24ms max=74.24ms p(90)=74.24ms p(95)=74.24ms
{ expected_response:true }...: avg=74.24ms min=74.24ms med=74.24ms max=74.24ms p(90)=74.24ms p(95)=74.24ms
http_req_failed................: 0.00% 0 out of 1
http_req_receiving.............: avg=55µs min=55µs med=55µs max=55µs p(90)=55µs p(95)=55µs
http_req_sending...............: avg=422µs min=422µs med=422µs max=422µs p(90)=422µs p(95)=422µs
http_req_tls_handshaking.......: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s
http_req_waiting...............: avg=73.76ms min=73.76ms med=73.76ms max=73.76ms p(90)=73.76ms p(95)=73.76ms
http_reqs......................: 1 0.918662/s
iteration_duration.............: avg=1.08s min=1.08s med=1.08s max=1.08s p(90)=1.08s p(95)=1.08s
iterations.....................: 1 0.918662/s
vus............................: 1 min=1 max=1
vus_max........................: 1 min=1 max=1
Le rapport montre que l’API répond correctement avec des temps de réponse très courts, ce qui est attendu dans un Smoke Test.
Stress Test
Le Stress Test pousse le système au-delà de ses limites pour évaluer sa stabilité et identifier les points de défaillance. Il permet de déterminer à quel moment le système commence à échouer lorsqu’il est soumis à une charge importante.
Exemple de script
import http from 'k6/http';
import { sleep } from 'k6';
export const options = {
stages: [
{ duration: '30s', target: 200 },
{ duration: '1m', target: 15000 },
{ duration: '20s', target: 0 },
],
};
export default function () {
http.get('http://localhost:3000/slow');
sleep(1);
}
Résultat attendu
Voici un exemple de résultat obtenu lors d’un Stress Test :
scenarios: (100.00%) 1 scenario, 15000 max VUs, 2m0s max duration (incl. graceful stop):
* default: Up to 15000 looping VUs for 2m0s over 3 stages (gracefulRampDown: 20s, gracefulStop: 20s)
...
WARN[0088] Request Failed error="Get \"http://localhost:3000/slow\": dial tcp 127.0.0.1:3000: connect: connection reset by peer"
WARN[0088] Request Failed error="Get \"http://localhost:3000/slow\": read tcp 127.0.0.1:59822->127.0.0.1:3000: read: connection reset by peer"
...
data_received..................: 50 MB 445 kB/s
data_sent......................: 18 MB 155 kB/s
http_req_blocked...............: avg=1.03ms min=0s med=3µs max=1.92s p(90)=23µs p(95)=221µs
http_req_connecting............: avg=1.01ms min=0s med=0s max=1.92s p(90)=0s p(95)=181µs
http_req_duration..............: avg=2.02s min=0s med=2s max=3.59s p(90)=2.07s p(95)=2.17s
{ expected_response:true }...: avg=2.02s min=1.99s med=2s max=3.59s p(90)=2.07s p(95)=2.17s
http_req_failed................: 0.01% 26 out of 208653
http_req_receiving.............: avg=58.19µs min=0s med=18µs max=156.64ms p(90)=64µs p(95)=129µs
http_req_sending...............: avg=165.75µs min=0s med=6µs max=295.11ms p(90)=35µs p(95)=98µs
http_req_tls_handshaking.......: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s
http_req_waiting...............: avg=2.02s min=0s med=2s max=3.59s p(90)=2.07s p(95)=2.17s
http_reqs......................: 208653 1847.535897/s
iteration_duration.............: avg=3.03s min=1s med=3s max=4.93s p(90)=3.08s p(95)=3.18s
iterations.....................: 208653 1847.535897/s
vus............................: 37 min=3 max=15000
vus_max........................: 15000 min=15000 max=15000
running (1m52.9s), 00000/15000 VUs, 208653 complete and 0 interrupted iterations
Ce résultat montre que le système est soumis à une charge très élevée, avec des utilisateurs simultanés atteignant 15 000 au pic. Les temps de réponse restent raisonnables bien que légèrement augmentés sous la charge. Quelques erreurs peuvent apparaître (par exemple, 0.01% d’échecs), mais cela est attendu dans des tests de stress extrêmes. Une analyse plus approfondie peut être nécessaire pour optimiser les performances sous ces conditions.
Personnalisation des métriques
K6 permet de créer des métriques personnalisées pour enrichir les rapports. Voici un exemple mesurant le taux d’erreurs :
import http from 'k6/http';
import { Rate } from 'k6/metrics';
const errorRate = new Rate('error_rate');
export default function () {
const res = http.get('http://localhost:3000/fast');
errorRate.add(res.status >= 122);
}
data_received..................: 241 B 4.2 kB/s
data_sent......................: 84 B 1.5 kB/s
error_rate.....................: 100.00% 1 out of 1
http_req_blocked...............: avg=1.39ms min=1.39ms med=1.39ms max=1.39ms p(90)=1.39ms p(95)=1.39ms
running (00m00.1s), 0/1 VUs, 1 complete and 0 interrupted iterations
Intégration avec Grafana et Prometheus
Voici une configuration complète pour visualiser vos résultats dans Grafana et Prometheus.
Pré-requis
- Docker et Docker Compose installés sur votre machine.
Configuration Docker Compose
Créez un fichier docker-compose.yml :
version: '3.7'
services:
prometheus:
image: prom/prometheus:latest
container_name: prometheus
ports:
- "9090:9090"
command:
- "--config.file=/etc/prometheus/prometheus.yml"
- "--web.enable-remote-write-receiver"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
networks:
- monitoring
grafana:
image: grafana/grafana:latest
container_name: grafana
ports:
- "3001:3000"
volumes:
- grafana-data:/var/lib/grafana
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
networks:
- monitoring
networks:
monitoring:
volumes:
grafana-data:
Configuration Prometheus
Créez un fichier prometheus.yml :
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'k6'
static_configs:
- targets: ['host.docker.internal:6565']
Exécution
Démarrez les conteneurs :
docker-compose up -d
Lancer un test K6 avec Prometheus Remote Write
Ajoutez cette configuration dans votre script K6 :
import http from 'k6/http';
import { sleep, check } from 'k6';
export const options = {
stages: [
{ duration: '30s', target: 50 }, // Monter jusqu'à 50 utilisateurs virtuels
{ duration: '1m', target: 10000 }, // Monter jusqu'à 10000 utilisateurs virtuels
{ duration: '2m', target: 200 }, // Monter jusqu'à 200 utilisateurs virtuels
{ duration: '30s', target: 0 }, // Descendre progressivement à 0
],
thresholds: {
http_req_duration: ['p(95)<2000'], // 95% des requêtes doivent répondre en moins de 2s
http_req_failed: ['rate<0.01'], // Moins de 1% des requêtes échouées
},
ext: {
experimental: {
prometheus_rww: {
address: 'http://host.docker.internal:9090/api/v1/write',
pushInterval: '5s',
},
},
},
};
export default function () {
const fastRes = http.get('http://localhost:3000/fast');
check(fastRes, {
'status is 200': (r) => r.status === 200,
'response time < 100ms': (r) => r.timings.duration < 100,
});
const slowRes = http.get('http://localhost:3000/slow');
check(slowRes, {
'status is 200': (r) => r.status === 200,
'response time < 300ms': (r) => r.timings.duration < 300,
});
sleep(1);
}
Configurer Grafana
- Accédez à Grafana via http://localhost:3001.
- Connectez-vous (utilisateur : admin, mot de passe : admin).
- Ajoutez Prometheus comme source de données :
- URL :
http://prometheus:9090
- URL :
- Importez un tableau de bord K6 disponible sur Grafana Dashboards (ID : 19665). (k6 Prometheus | Grafana Labs )
Exécution du test :
k6 run --out experimental-prometheus-rw script.js
Résultats des tests et visualisation dans Grafana
Après avoir exécuté le test de charge avec K6, nous avons intégré les données dans Prometheus et visualisé les résultats dans Grafana. Cette configuration permet une analyse approfondie des performances de votre API en temps réel. Voici deux exemples de tableaux de bord obtenus lors des tests et disponible sur localhost:3001:
Exemple de tableau de bord 1
Ce tableau affiche les métriques clés suivantes :
- Taux de transfert (data sent/received) : Analyse de la quantité de données envoyées et reçues au cours du test.
- Durée des requêtes HTTP : Répartition des durées de réponse pour les requêtes réussies, ainsi que les statistiques détaillées par URL.
- Taux de requêtes par seconde : Indication de la charge générée par les utilisateurs virtuels (Virtual Users).
Exemple de tableau de bord 2
Dans ce tableau, nous pouvons observer :
- Vue d’ensemble des performances : Indication des utilisateurs virtuels actifs (VUs), des requêtes HTTP envoyées, et des taux de succès/échec.
- Durée des requêtes HTTP : Visualisation des temps de réponse moyens et des percentiles élevés (p95, p99) pour détecter les points d’anomalie.
- Rythme des requêtes : Statistiques sur les requêtes par seconde au pic de la charge.
Ces graphiques mettent en évidence les performances globales de votre système et permettent de diagnostiquer les éventuels goulots d’étranglement.
Meilleures pratiques pour K6
- Modularité : Divisez vos scripts pour une meilleure lisibilité et maintenance.
- Validation : Utilisez des
checkpour valider les réponses de vos API. - Rapports avancés : Intégrez avec Grafana pour visualiser les métriques en temps réel.
- Automatisation : Intégrez K6 dans vos pipelines CI/CD pour surveiller continuellement les performances.
Conclusion
K6 est un outil indispensable pour évaluer la performance et la résilience des applications modernes. Sa simplicité, ses hautes performances et ses nombreuses options en font un choix idéal pour les développeurs et les équipes DevOps. Essayez-le pour garantir une expérience utilisateur optimale, même sous des charges importantes.
Sources
À lire aussi
Créer une API en Python avec FastAPI
Optimiser vos tests avec Cucumber
Comment préparer votre Jira Cloud dès maintenant ?

