Tests de performance - 03 - Scénarios k6 : ramping, spike et soak

Les scénarios k6 avances : montee progressive, pic de charge, test d'endurance. Avec des exemples concrets sur une vraie API.

03 - Scénarios k6 : ramping, spike et soak

Ce que tu vas apprendre

  • Configurer une montee en charge progressive (ramping)
  • Simuler un pic de trafic soudain (spike)
  • Lancer un test d'endurance longue duree (soak)
  • Définir des seuils de reussite automatiques (thresholds)

Prerequisites

Avoir k6 installe et compris les bases de l'article précédent. Voir 02 - k6 : ton premier test de charge.


Au-dela des VUs constants

L'article précédent utilisait vus: 10, duration: "30s". C'est bien pour un premier test. Mais en prod, le trafic ne ressemble jamais a ca. Il monte progressivement le matin, se stabilise, puis un tweet viral envoie 500 personnes en 10 secondes. Ou bien c'est le Black Friday et ta charge triple pendant 8 heures d'affilee.

k6 gere tout ca avec les stages et les scenarios. Voyons les patterns les plus utiles.

Ramping : montee progressive

Le ramping simule une montee en charge naturelle. Tu commences a 0 VUs, tu montes a 20, tu stabilises, tu montes a 50, tu stabilises, et tu redescends. Ca te permet de voir exactement a quel palier la latence commence a degrader.

javascriptimport http from "k6/http";
import { check, sleep } from "k6";

export const options = {
  stages: [
    { duration: "2m", target: 20 }, // montee a 20 VUs en 2 min
    { duration: "5m", target: 20 }, // plateau a 20 VUs pendant 5 min
    { duration: "2m", target: 50 }, // montee a 50 VUs en 2 min
    { duration: "5m", target: 50 }, // plateau a 50 VUs pendant 5 min
    { duration: "2m", target: 0 }, // descente a 0
  ],
};

export default function () {
  const res = http.get("https://blog.paltemps.fr/");
  check(res, {
    "status 200": (r) => r.status === 200,
    "response < 500ms": (r) => r.timings.duration < 500,
  });
  sleep(1);
}

Ce test dure 16 minutes au total. C'est long, mais c'est voulu. Un test de charge serieux ne dure pas 30 secondes. Il faut le temps que les caches se remplissent, que le pool de connexions se stabilise, que le GC fasse son travail.

Quand je teste paltemps.fr, je lance ce genre de ramping pour trouver le seuil ou la latence p95 dépassé 200ms. Sur mon VPS (2 vCPU, 4 GB RAM), ce seuil est autour de 80 VUs concurrents pour l'API Elysia.

Spike : pic de charge soudain

Le spike test simule un afflux brutal. Imaginons qu'un article soit partage sur Hacker News : tu passes de 5 a 100 utilisateurs en quelques secondes.

javascriptexport const options = {
  stages: [
    { duration: "1m", target: 5 }, // trafic normal
    { duration: "10s", target: 100 }, // spike brutal
    { duration: "3m", target: 100 }, // maintien du spike
    { duration: "10s", target: 5 }, // retour a la normale
    { duration: "1m", target: 5 }, // stabilisation
    { duration: "30s", target: 0 }, // fin
  ],
};

Ce qu'on veut observer :

  • Est-ce que le serveur absorbe le spike sans erreur ?
  • Combien de temps met la latence a revenir a la normale apres le spike ?
  • Est-ce que des requêtes sont perdues pendant la montee ?

Un bon système absorbe le spike avec une augmentation de latence acceptable (2x a 5x) et revient a la normale en quelques secondes apres la descente. Un mauvais système cascade : la latence monte, les timeouts arrivent, les retries amplifient la charge, et tout s'effondre.

Soak : endurance longue duree

Le soak test maintient une charge moderee pendant des heures. L'objectif n'est pas de surcharger le serveur, c'est de détecter les problèmes qui n'apparaissent qu'avec le temps : fuites mémoire, connexions qui ne sont pas fermees, logs qui remplissent le disque.

javascriptexport const options = {
  stages: [
    { duration: "5m", target: 30 }, // montee
    { duration: "4h", target: 30 }, // plateau de 4 heures
    { duration: "5m", target: 0 }, // descente
  ],
};

4 heures. Oui, c'est long. Lance-le le soir avant de partir, regarde les résultats le lendemain. On verra comment analyser les résultats d'un soak test dans l'article 07.

Thresholds : pass/fail automatique

Les thresholds transforment ton test de charge en test automatisable. Tu définis des critères, et k6 sort avec un code de retour non-zero si un critère est viole. Parfait pour le CI/CD.

javascriptexport const options = {
  stages: [
    { duration: "2m", target: 30 },
    { duration: "5m", target: 30 },
    { duration: "2m", target: 0 },
  ],
  thresholds: {
    http_req_duration: ["p(95)<200", "p(99)<500"],
    http_req_failed: ["rate<0.01"],
    checks: ["rate>0.95"],
  },
};

Ici on exige que :

  • 95% des requêtes repondent en moins de 200ms
  • 99% en moins de 500ms
  • Le taux d'erreur HTTP reste sous 1%
  • Au moins 95% des checks passent

Si un de ces seuils est dépassé, k6 affiche un gros FAIL rouge a cote du threshold et retourne le code de sortie 99. Tu peux intégrer ca dans un pipeline GitLab CI (on en parle dans la serie déploiement GitLab) :

bashk6 run --quiet load-test.js || echo "Performance regression detected!"

Combiner les scénarios

k6 permet de définir plusieurs scénarios qui tournent en parallèle. Par exemple : un flux constant de lecteurs + des pics occasionnels d'ecritures.

javascriptexport const options = {
  scenarios: {
    readers: {
      executor: "ramping-vus",
      startVUs: 0,
      stages: [
        { duration: "2m", target: 50 },
        { duration: "5m", target: 50 },
        { duration: "2m", target: 0 },
      ],
    },
    writers: {
      executor: "per-vu-iterations",
      vus: 5,
      iterations: 100,
      startTime: "2m",
    },
  },
};

export default function () {
  // scenario par defaut (readers)
  const res = http.get("https://blog.paltemps.fr/");
  check(res, { "status 200": (r) => r.status === 200 });
  sleep(1);
}

Le scénario readers monte progressivement a 50 VUs. Le scénario writers démarré apres 2 minutes avec 5 VUs qui font 100 itérations chacun. Ca simule un usage realiste ou la majorite du trafic est en lecture.

Choisir le bon scénario

Mon raisonnement est simple :

Pour un deploy en prod, je lance un ramping de 10 minutes avec les thresholds du projet. Si ca passe, je deploie.

Apres un changement d'infrastructure (migration de VPS, mise à jour de Bun, changement de config Caddy), je lance un spike pour vérifier que le système encaisse.

Une fois par mois, je lance un soak de 2-4 heures sur un environnement de staging pour détecter les fuites lentes. Ca m'a deja sauve la mise quand j'ai découvert une fuite de connexions Redis qui ne se manifestait qu'apres 45 minutes de charge.


Article précédent : 02 - k6 : ton premier test de charge HTTP Article suivant : 04 - Stress test : trouver le point de rupture

Sources

Réservez un audit gratuit de 30 minutes. Je vous montre concrètement ce qu'on peut automatiser.