module.exports = {
apps: [{
name: 'api-service',
script: './dist/server.js',
instances: 'max',
exec_mode: 'cluster',
max_memory_restart: '400M',
kill_timeout: 5000,
wait_ready: true,
listen_timeout: 8000,
env_production: {
NODE_ENV: 'production',
PORT: 3000
},
error_file: './logs/err.log',
out_file: './logs/out.log',
merge_logs: true,
log_date_format: 'YYYY-MM-DD HH:mm:ss'
}]
};Dacă încă lansezi aplicații Node.js în producție cu pm2 start app.js --name api, e timpul să trecem la lucruri serioase. Un fișier ecosystem.config.js bine structurat te scapă de nopți pierdute când crapă procesul din cauza vreunui memory leak nedescoperit la QA. Vă arăt mai jos configurația pe care o trag după mine pe toate proiectele medii și mari.
Cluster mode și capcanele stării în memorie
Pe un server cu 8 vCPU, e păcat să rulezi o singură instanță de Node.js. Prin exec_mode: 'cluster' și instances: 'max', PM2 folosește modulul nativ de cluster din Node și multiplică procesul pe toate nucleele disponibile. Am avut la un proiect e-commerce vreo 12k utilizatori simultani, iar simpla trecere pe cluster mode ne-a scăzut latența medie per request de la 450ms la 80ms.
Există însă un trade-off sincer aici: memorie mai multă consumată și pierderea stării locale. Dacă stochezi sesiunile userilor sau jetoanele direct într-un obiect global din memorie, un request va ajunge la worker-ul 1, iar următorul la worker-ul 2, deconectând utilizatorul. Dacă treci pe cluster, ești obligat să muti starea în Redis și să folosești un adapter de Pub/Sub dacă ai Socket.io.
Graceful reload fără nicio secundă de 502
Comanda clasică pm2 restart oprește brut toate procesele și le repornește. În cele 2-3 secunde de boot, Nginx îți va servi erori 502 Bad Gateway pe bandă rulantă. Soluția curată este pm2 reload ecosystem.config.js combinat cu kill_timeout.
Ce se întâmplă în fundal? PM2 trimite un semnal SIGINT procesului vechi, pornește instanța nouă și abia după ce noua instanță confirmă că ascultă pe port, o taie pe cea veche. În codul aplicației trebuie doar să prinzi semnalul și să închizi conexiunile de DB înainte de exit:
process.on('SIGINT', () => { db.close(); process.exit(0); });
Dacă aplicația nu se închide singură în limita setată la kill_timeout (de exemplu 5000ms), PM2 îi dă SIGKILL forțat.
Limita de memorie (OOM) și managementul logurilor
Node.js are prostul obicei să umfle heap-ul când procesează fișiere mari sau când rămâne vreun closure uitat prin middleware-uri. Am pățit pe un VPS de 4GB RAM ca un singur proces scăpat de sub control să mănânce 3GB și să blocheze tot sistemul, inclusiv SSH-ul.
Cu opțiunea max_memory_restart: '400M', PM2 monitorizează procesul și îi dă restart automat imediat ce trece de prag. Fiind în cluster mode, celelalte procese preiau traficul, iar utilizatorul nici nu simte că un worker s-a reciclat.
Cât despre loguri, nu le lăsa să crească la infinit. Instalează modulul de rotație cu pm2 install pm2-logrotate și asigură-te că ai setat merge_logs: true în config ca să nu te trezești cu 8 fișiere separate de loguri pentru fiecare worker în parte.
Voi ce trucuri sau directoare de logare folosiți când aveți zeci de microservicii gestionate prin PM2?