Aller au contenu

PM2 : mode cluster et reload

Le mode cluster lance plusieurs processus Node.js qui partagent le même port, derrière un répartiteur intégré. Il exploite tous les cœurs du processeur et permet de recharger sans coupure.

Fenêtre de terminal
pm2 start app.js --name api -i max # un processus par cœur
pm2 start app.js --name api -i 4 # quatre processus
pm2 start app.js --name api -i -1 # cœurs moins un

Dans le fichier ecosystem :

module.exports = {
apps: [
{
name: 'api',
script: './src/serveur.js',
instances: 'max',
exec_mode: 'cluster',
},
],
};
Fenêtre de terminal
pm2 list # mode « cluster », plusieurs lignes « api »
pm2 scale api 2 # passe à 2 instances, à chaud
pm2 scale api +1 # ajoute une instance
  • Applications sans état : chaque processus a sa propre mémoire. Les sessions, caches et compteurs partagés doivent être dans un service externe (base de données, Redis).
  • Pas de verrou sur un fichier local écrit par plusieurs instances en même temps.
  • Les tâches planifiées internes (setInterval, cron) s’exécutent dans chaque instance : les confier à une seule application (instance unique).
Fenêtre de terminal
pm2 reload api
pm2 reload all
pm2 reload ecosystem.config.cjs --update-env
Commande Comportement
pm2 restart Arrête tous les processus puis les redémarre : coupure brève
pm2 reload Redémarre les instances une par une en gardant les autres disponibles : pas de coupure (mode cluster)

En mode fork, reload se comporte comme restart.

Quand PM2 arrête une instance, il envoie le signal SIGINT, puis SIGKILL après kill_timeout (1,6 seconde par défaut). L’application doit terminer ses requêtes en cours :

const serveur = app.listen(3000);
process.on('SIGINT', () => {
serveur.close(() => {
// fermer les connexions aux bases de données ici
process.exit(0);
});
});

Augmenter le délai si les requêtes sont longues :

{ name: 'api', script: './src/serveur.js', kill_timeout: 10000 }

Pour qu’un rechargement ne coupe l’ancienne instance qu’une fois la nouvelle prête :

{
name: 'api',
script: './src/serveur.js',
instances: 2,
exec_mode: 'cluster',
wait_ready: true,
listen_timeout: 10000,
}

et dans l’application, après le démarrage (connexion à la base, port ouvert) :

const serveur = app.listen(3000, () => {
if (process.send) process.send('ready');
});

listen_timeout fixe le délai maximal d’attente du signal ready.

Fenêtre de terminal
cd /srv/monapp
git pull
npm ci --omit=dev
pm2 reload ecosystem.config.cjs --update-env
pm2 list

Si le déploiement modifie le schéma d’une base de données, appliquer des migrations compatibles avec l’ancienne et la nouvelle version du code pendant le rechargement.

Le répartiteur de PM2 sert à plusieurs processus sur une machine. Pour plusieurs machines, ou pour une meilleure isolation, utiliser un reverse proxy (Caddy : exemples réutilisables, Nginx : reverse proxy) devant l’ensemble.

Symptôme Piste
Sessions perdues d’une requête à l’autre État stocké en mémoire : sessions dans Redis ou la base de données
reload coupe le service Mode fork (pas de cluster), ou arrêt non propre : gérer SIGINT
reload attend longtemps puis force wait_ready sans process.send('ready'), ou listen_timeout trop court
Une instance consomme tout le processeur Boucle bloquante dans le code : profiler (node --prof)
EADDRINUSE en cluster Application qui ouvre un second port ou lance son propre cluster
Tâche planifiée exécutée plusieurs fois Une exécution par instance : isoler la tâche dans une application à instance unique

Ubuntu 24.04 et 26.04 : différences à connaître

Section intitulée « Ubuntu 24.04 et 26.04 : différences à connaître »

Le mode cluster dépend de PM2 et de Node.js, pas de la distribution. Le nombre d’instances max correspond au nombre de cœurs de la machine.