module.exports = {
apps: [{
name: "api-prod",
script: "./dist/index.js",
instances: "max",
exec_mode: "cluster",
wait_ready: true,
listen_timeout: 10000,
kill_timeout: 4000,
max_memory_restart: "800M",
env: {
NODE_ENV: "production",
PORT: 3000
}
}]
};Dacă încă pornești aplicațiile de Node în producție cu clasicul pm2 start server.js, ești la doar un memory leak distanță de un weekend stricat. Am trecut prin asta acum vreo doi ani la un proiect cu peste 15.000 de utilizatori activi, când VPS-ul crăpa pur și simplu din cauza logurilor gigantice și a memoriei nestrunite.
De atunci, nu mai plec la drum în proiecte Node.js fără un fișier ecosystem.config.js bine pus la punct. PM2 este extrem de puternic, dar setările lui implicite sunt destinate mai degrabă mediului de dezvoltare, nu producției.
Cluster mode și mitul „merge din cutie”
Când treci de la fork la cluster, PM2 rulează instanțe multiple ale aplicației tale, împărțind traficul între ele. Este cel mai simplu mod de a scala pe orizontală și de a folosi toate nucleele procesorului.
Dar vine cu un trade-off sincer: dacă ai sesiuni stocate local în memorie (in-memory sessions) sau folosești Socket.IO fără un adapter de Redis, o să ai surprize mari. Utilizatorii vor fi deconectați random sau vor pierde sesiunea pentru că cererile lor succesive ajung la instanțe diferite ale aplicației. Dacă folosești cluster mode, aplicația ta trebuie să fie stateless.
Un alt avantaj uriaș în cluster mode este graceful reload. În loc de pm2 restart, care oprește toate instanțele simultan și creează downtime, folosim pm2 reload. Acesta repornește instanțele pe rând. Ca să funcționeze corect, trebuie să îi dai timp aplicației să își închidă conexiunile la baza de date înainte ca PM2 să o omoare de tot. Pentru asta folosim kill_timeout în config și ascultăm semnalul SIGINT în codul Node.
Lupta cu memoria (OOM) și logurile care umplu discul
Node.js are un comportament ciudat când vine vorba de managementul memoriei în containere sau pe mașini virtuale mici. Pe un VPS de 4GB RAM, dacă lași setările default, două instanțe de Node pot încerca să mănânce câte 1.4GB fiecare (limita default de heap pe sisteme pe 64 de biți). Când se apropie de limită, sistemul de operare va intra în panică și va omorî procesele la întâmplare prin OOM (Out Of Memory) killer.
Prin setarea max_memory_restart: '800M', îi spunem lui PM2 să dea restart automat acelei instanțe dacă depășește pragul de 800MB. Restartul se face controlat, iar celelalte instanțe din cluster preiau traficul în acest timp.
O altă problemă reală: logurile. Am pățit ca un script de sincronizare care a o luat-o razna să scrie peste 30GB de loguri în doar 4 zile, blocând complet discul serverului și, implicit, baza de date. PM2 nu știe singur să își curețe logurile vechi. Pentru asta, trebuie să rulezi în terminal:
pm2 install pm2-logrotate
Acest modul se va ocupa de arhivarea și ștergerea logurilor vechi conform regulilor stabilite global.
Voi cum gestionați crash-urile de memorie în producție? Preferați să lăsați PM2 să își facă treaba sau ați trecut complet pe Docker și Kubernetes pentru autoscaling?