E-NO
Planification de capacité Node.js 8 min de lecture

Planification de capacité Node.js : un guide pratique pour dimensionner et faire évoluer

calendar_today Publié : 2026-09-02
update Dernière mise à jour : 2026-09-02
analytics Efficacité SEO : 100%
Illustration du guide technique pour « Planification de capacité Node.js : un guide pratique pour dimensionner et faire évoluer ».

Introduction

La planification de capacité pour Node.js consiste à estimer et provisionner les ressources CPU, mémoire et la concurrence dont votre application a besoin pour atteindre ses objectifs de performance sous une charge attendue. Contrairement aux serveurs multithreads traditionnels, Node.js exécute JavaScript sur une boucle d'événements monothread, ce qui crée des goulots d'étranglement et des modèles de mise à l'échelle uniques. Bien dimensionner les capacités évite les réponses lentes, les plantages et une infrastructure surprovisionnée.

Ce guide présente un flux de travail pratique, sensible aux versions, pour la planification de capacité Node.js. Vous apprendrez à inventorier votre environnement, dimensionner les ressources en toute sécurité, vérifier les performances, gérer les modes de défaillance et utiliser une liste de contrôle opérationnelle. Chaque section comprend des commandes concrètes, les sorties attendues et les étapes de récupération, avec des espaces réservés que vous pouvez adapter à votre pile.

Nous supposons Node.js 18 ou ultérieur sur Linux (Ubuntu 22.04 LTS dans les exemples) pour un comportement déterministe. Les commandes qui diffèrent selon les versions sont signalées.

Inventaire des versions et de l'environnement

Avant tout réglage de capacité, capturez votre version de Node.js, le système d'exploitation et la topologie de déploiement. Cette base de référence est essentielle pour reproduire les problèmes et savoir quels indicateurs et API sont disponibles.

Identifier la version installée et la plateforme

Exécutez les commandes en lecture seule suivantes :

node --version
# v18.19.0 (exemple)

npm --version
# 10.2.3

uname -a
# Linux app-server-01 5.15.0-91-generic #101-Ubuntu SMP ... x86_64 x86_64 x86_64 GNU/Linux

Vérifiez si votre binaire Node.js est une version de production ou de débogage, car les versions de débogage peuvent être beaucoup plus lentes :

node -p "process.release"
# { name: 'node', lts: 'Hydrogen', sourceUrl: '...', headersUrl: '...', libUrl: '...' }

Si vous utilisez un gestionnaire de processus comme systemd, PM2 ou Docker, enregistrez-le également. Exemple pour systemd :

systemctl show node-app -p Environment,ExecStart,MemoryMax,CPUQuota
# Environment= NODE_ENV=production PORT=3000
# ExecStart={ path=/usr/bin/node ; argv[]=/usr/bin/node /opt/app/server.js ; ... }
# MemoryMax=infinity
# CPUQuota=infinity

Prérequis

  • Accès au serveur avec sudo ou des autorisations appropriées pour l'inspection au niveau du système.
  • Node.js 18+ installé à partir des paquets officiels ou de nvm.
  • Votre code d'application disponible localement ou sur le serveur.
  • Un outil de test de charge plus tard (nous utiliserons autocannon pour les benchmarks HTTP).
  • Des outils de surveillance tels que Prometheus et Grafana sont recommandés mais pas obligatoires.

Rayon d'impact et récupération pour l'inventaire

Les commandes d'inventaire sont en lecture seule et ne modifient pas l'état du système. Il n'y a pas de rayon d'impact. Si une commande est introuvable, installez le paquet manquant ou utilisez une alternative. Par exemple, uname fait partie de coreutils et devrait toujours exister. Aucune récupération n'est nécessaire, mais enregistrez la sortie dans votre runbook.

Chemin de configuration sûr

Dimensionner Node.js commence par comprendre la boucle d'événements, puis appliquer des limites correspondant à votre charge de travail. Un chemin sûr consiste à mesurer d'abord, définir des limites prudentes, puis ajuster un paramètre à la fois.

Bases de la boucle d'événements et du pool de threads

Node.js utilise une boucle d'événements monothread pour l'exécution de JavaScript. Les tâches intensives en CPU bloquent la boucle et retardent toutes les autres requêtes. Certaines opérations (fs, crypto, dns) utilisent un pool de threads libuv avec une taille par défaut de 4. Vous pouvez inspecter la taille du pool :

node -p "process.env.UV_THREADPOOL_SIZE || 'default (4)'"

Si elle est définie, la variable d'environnement apparaît. Pour la modifier, définissez-la avant de démarrer le processus :

UV_THREADPOOL_SIZE=8 node server.js

Rayon d'impact : affecte uniquement les opérations de système de fichiers, DNS et crypto. Augmentez uniquement si le profilage montre une saturation du pool de threads. Récupération : désactivez la variable ou remettez-la à 4.

Appliquer des limites de mémoire

Node.js a une taille maximale d'ancien espace par défaut d'environ 2 Go sur les systèmes 64 bits. Vous pouvez définir une limite avec --max-old-space-size (en Mo). Pour un serveur avec 4 Go de RAM exécutant uniquement Node, vous pouvez allouer 3 Go au tas :

node --max-old-space-size=3072 server.js

Vérifiez la limite dans l'application :

console.log(v8.getHeapStatistics().heap_size_limit);
// 3221225472 (3 Go)

Rayon d'impact : uniquement ce processus. Si la limite est trop basse, l'application plante avec JavaScript heap out of memory. Récupération : redémarrez avec une limite plus élevée ou corrigez les fuites de mémoire.

Définir une configuration spécifique à l'environnement

Utilisez des variables d'environnement pour les paramètres réglables comme le port, la taille du pool de base de données et les délais d'attente. Exemple de fichier .env (committé dans un gestionnaire de secrets, pas dans le contrôle de version) :

PORT=3000
DB_POOL_MAX=20
HTTP_TIMEOUT_MS=5000

Chargez et validez avec dotenv (ou le --env-file intégré de Node 20) :

node --env-file=.env server.js

Dans l'application :

const port = parseInt(process.env.PORT, 10);
if (!port || port < 1 || port > 65535) {
  throw new Error(`Invalid PORT: ${process.env.PORT}`);
}

Rayon d'impact : affecte toutes les connexions entrantes. Récupération : annulez le changement de variable d'environnement et redémarrez.

Choisir un gestionnaire de processus

Pour la production, utilisez systemd, PM2 ou Docker pour gérer les redémarrages et les limites de ressources. Exemple d'unité systemd avec des limites :

[Service]
ExecStart=/usr/bin/node /opt/app/server.js
Restart=on-failure
MemoryMax=3G
CPUQuota=200%
Environment=NODE_ENV=production PORT=3000

Vérifiez après le rechargement :

sudo systemctl daemon-reload
sudo systemctl restart node-app
systemctl show node-app -p MemoryMax,CPUQuota
# MemoryMax=3221225472
# CPUQuota=200%

Rayon d'impact : service entier. Récupération : revenez au fichier d'unité précédent et redémarrez.

Vérification et diagnostics

La planification de capacité n'est pas complète sans test de charge pour vérifier que vos limites et votre topologie choisies gèrent le trafic attendu. Utilisez des charges de travail réalistes et mesurez la latence, le débit et l'utilisation des ressources.

Mesure de base

Tout d'abord, mesurez les performances de votre application sans limites artificielles. Utilisez autocannon pour tester en charge un point de terminaison HTTP simple. Installez-le globalement ou utilisez npx :

npm install -g autocannon

Démarrez votre serveur (par exemple, sur le port 3000). Ensuite, exécutez un test de base avec 100 connexions pendant 30 secondes :

autocannon -c 100 -d 30 http://localhost:3000/api/health

Exemple de sortie (tronquée) :

Running 30s test @ http://localhost:3000/api/health
100 connections

Stat         Avg     Stdev   Max
Latency (ms) 12.34   5.67    45.67
Req/Sec      8234.56 123.45  8567.89

Bytes/Sec    2.1 MB

Requests per second: 8234.56

Enregistrez ceci comme votre référence. Si le point de terminaison est léger en CPU, vous pouvez voir des milliers de requêtes par seconde.

Tester avec un point de terminaison gourmand en CPU

Créez un point de terminaison temporaire qui effectue un calcul intensif en CPU (par exemple, Fibonacci) pour voir le blocage de la boucle d'événements :

app.get('/cpu', (req, res) => {
  const n = 40;
  const fib = (n) => (n < 2 ? n : fib(n - 1) + fib(n - 2));
  res.json({ fib: fib(n) });
});

Exécutez le même test de charge. Vous verrez la latence p99 grimper à plusieurs secondes et le débit tomber à un chiffre. Cela démontre pourquoi le travail CPU doit être déchargé (worker threads, service séparé ou algorithme optimisé).

Mesurer l'utilisation des ressources

Pendant le test de charge, surveillez le CPU, la mémoire et le retard de la boucle d'événements. Utilisez pidstat (de sysstat) et clinic doctor ou node --prof.

Exemple pidstat :

pidstat -p $(pgrep -f 'node server.js') 1
# Linux ...
# 09:00:01   UID  PID  %usr %system  %guest  %CPU  CPU  minflt/s  majflt/s  VSZ  RSS  %MEM
# 09:00:02  1000  1234  45.0  5.0  0.0  50.0  0  0.0  0.0  700000  120000  3.0

Pour le retard de la boucle d'événements, utilisez clinic doctor :

npx clinic doctor --autocannon [ -c 100 -d 30 http://localhost:3000 ] -- node server.js

Cela produit un rapport HTML montrant l'utilisation de la boucle d'événements et les lacunes.

Vérifier la mise à l'échelle avec cluster

Si vous utilisez le module cluster, vérifiez que les workers sont équilibrés. Exemple de server.js avec cluster :

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').availableParallelism();

if (cluster.isPrimary) {
  console.log(`Primary ${process.pid} is running`);
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }
  cluster.on('exit', (worker, code, signal) => {
    console.log(`worker ${worker.process.pid} died`);
  });
} else {
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end('hello world\n');
  }).listen(3000);
  console.log(`Worker ${process.pid} started`);
}

Exécutez le serveur et vérifiez les processus workers :

pgrep -af 'node server.js'
# 4567 node server.js  (primary)
# 4568 /usr/bin/node server.js (worker 1)
# 4569 /usr/bin/node server.js (worker 2)
# ...

Testez à nouveau en charge ; vous devriez voir un débit total plus élevé et une latence plus faible sous forte charge par rapport à un processus unique.

Modes de défaillance et récupération

Comprendre comment Node.js échoue sous pression de ressources vous aide à concevoir des procédures de récupération.

Mémoire insuffisante (OOM)

Symptôme : Le processus se termine avec le code 134 ou 137, le journal affiche FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory.

Diagnostic : Consultez les journaux système (journalctl -u node-app -n 50) et utilisez des instantanés de tas avant le plantage si possible.

Récupération :

  • Augmentez --max-old-space-size si la limite est artificielle et que le serveur a de la RAM libre.
  • Corrigez la fuite de mémoire (par exemple, utilisez --inspect et le profileur de tas Chrome DevTools).
  • Ajoutez plus de swap comme soulagement temporaire, mais ne vous y fiez pas.
  • Redémarrez le service : sudo systemctl restart node-app.

Blocage de la boucle d'événements

Symptôme : Latence élevée, les contrôles de santé expirent, mais l'utilisation du CPU peut être faible si bloqué sur les E/S.

Diagnostic : Utilisez clinic doctor ou surveillez le retard de la boucle d'événements via un échantillonnage process.hrtime() :

setInterval(() => {
  const start = process.hrtime.bigint();
  setImmediate(() => {
    const lag = Number(process.hrtime.bigint() - start) / 1e6;
    console.log(`Event loop lag: ${lag.toFixed(2)} ms`);
  });
}, 1000);

Récupération :

  • Déchargez les tâches CPU vers des worker threads ou une file d'attente de travaux.
  • Optimisez le code bloquant (par exemple, remplacez les appels fs synchrones par des asynchrones).
  • Mettez à l'échelle horizontalement avec cluster ou un équilibreur de charge.

Épuisement des ports

Symptôme : Les requêtes sortantes échouent avec EADDRNOTAVAIL ou EADDRINUSE lors de l'utilisation de nombreux services externes.

Diagnostic : Vérifiez le nombre de sockets en TIME_WAIT : ss -s.

Récupération :

  • Réutilisez les connexions avec des agents keep-alive.
  • Augmentez la plage de ports éphémères et réduisez TIME_WAIT (Linux : net.ipv4.ip_local_port_range et net.ipv4.tcp_tw_reuse).
  • Réduisez le renouvellement des connexions.

Disque plein à cause des journaux

Symptôme : L'application plante ou les écritures échouent. Vérifiez df -h.

Récupération :

  • Faites pivoter les journaux avec logrotate ou une bibliothèque de journalisation.
  • Déchargez les journaux vers un système central.
  • Supprimez les anciens journaux : sudo find /var/log/myapp -name '*.log' -mtime +7 -delete.

Liste de contrôle opérationnelle

Utilisez cette liste de contrôle pour les examens de capacité de routine et avant les changements majeurs. Remplacez les exemples entre crochets par vos valeurs réelles.

  • [ ] Inventaire : Exécutez node --version, uname -a et enregistrez les paramètres du gestionnaire de processus. Exemple de sortie stocké dans le runbook.
  • [ ] Test de charge de base : Exécutez autocannon -c 100 -d 30 http://localhost:3000/health et enregistrez la latence p95 et le RPS. Exemple : p95=25 ms, RPS=8000.
  • [ ] Vérifier la limite de mémoire : Confirmez que v8.getHeapStatistics().heap_size_limit correspond à la valeur prévue. Exemple : 3221225472 octets.
  • [ ] Examiner le retard de la boucle d'événements : Assurez-vous que le retard reste inférieur à 50 ms en fonctionnement normal. Exemple : retard moyen 5 ms.
  • [ ] Vérifier les workers du cluster : Pour une configuration multicœur, assurez-vous d'avoir un worker par CPU. Exemple : 8 workers sur une machine à 8 cœurs.
  • [ ] Tester la récupération après défaillance : Simulez un OOM en démarrant avec une limite de mémoire basse et vérifiez que systemd redémarre. Exemple : node --max-old-space-size=64 server.js devrait planter et redémarrer automatiquement.
  • [ ] Vérifier les alertes de surveillance : Assurez-vous que des alertes sont définies pour CPU > 80 %, mémoire > 80 %, retard de boucle d'événements > 100 ms et taux d'erreur > 1 %.
  • [ ] Examiner les dépendances : Vérifiez que les tailles de pool de connexions de base de données et les délais d'attente correspondent à la capacité. Exemple : pool PostgreSQL max 20, délai d'attente 5 s.

Lorsqu'un contrôle échoue, documentez la défaillance et prenez des mesures correctives avant de procéder aux changements de capacité.

Conclusion

La planification de capacité Node.js est un processus itératif de mesure, de définition de limites et de vérification des performances sous charge. En suivant l'inventaire spécifique à la version, la configuration sûre, la vérification et les étapes de récupération de ce guide, vous pouvez éviter les pièges courants comme le blocage de la boucle d'événements et les plantages par manque de mémoire.

Commencez par une vérification à faible risque : exécutez un test de charge de base sur un environnement de préproduction, enregistrez les métriques et comparez-les à vos objectifs de production. Ensuite, appliquez progressivement des limites de ressources et des stratégies de mise à l'échelle, un changement à la fois, toujours avec un plan de retour en arrière. Intégrez la liste de contrôle opérationnelle dans votre processus de publication pour faire de la planification de capacité une discipline régulière plutôt qu'une réaction aux incidents.

Avec les bonnes commandes et les bons diagnostics, vous pouvez dimensionner en toute confiance votre application Node.js pour la demande actuelle et future.

Score de qualité de l’article

Utilité pour le lecteur 100%
  • check_circle Guide prêt à lire
  • check_circle Exemples pratiques inclus
  • check_circle URL d’article optimisée pour le SEO