module.exports = {
apps: [{
name: "api-prod",
script: "./dist/main.js",
instances: "max",
exec_mode: "cluster",
watch: false,
max_memory_restart: "800M",
kill_timeout: 3000,
env_production: {
NODE_ENV: "production",
PORT: 3000
},
error_file: "./logs/err.log",
out_file: "./logs/out.log",
log_date_format: "YYYY-MM-DD HH:mm:ss"
}]
};Dacă încă pornești aplicațiile de Node.js cu pm2 start app.js --name instance, pierzi vremea și te chinui singur la fiecare deploy. Am trecut și eu prin asta la un proiect cu vreo 12 microservicii unde, după fiecare restart manual, uitam să pun vreun flag de memorie și mă trezeam cu serverul în genunchi la 3 dimineața. Soluția e un fișier de config bine pus la punct, care să stea în repo-ul tău lângă package.json.
La un proiect cu 15k request-uri pe minut, cluster mode ne-a salvat efectiv viața. Am setat instances: 'max' și am văzut imediat cum load-ul s-a distribuit pe toate cele 16 nuclee ale serverului. Totuși, aici e un trade-off mare de care trebuie să fii conștient: dacă ai sesiuni stocate în memoria locală (fără Redis) sau folosești socket.io fără adaptor, o să ai probleme masive pentru că userul tău o să sară de pe un proces pe altul și va fi deconectat. Dacă ai aplicația stateless, cluster mode e aur curat.
Logurile care îți mănâncă discul
Am pățit o fază nasoală acum doi ani: un junior a lăsat un console.log în interiorul unei bucle care procesa niște fișiere mari. În 48 de ore, log-ul a crescut la 60GB și a blocat tot VPS-ul. De atunci, regula mea e clară: PM2 fără pm2-logrotate nu există. În fișierul de config, poți să separi clar out_file de error_file. E mult mai ușor să faci debug când nu trebuie să cauți erorile printr-un munte de log-uri de info. Recomand să instalezi modulul de logrotate separat și să-l configurezi să păstreze maxim 10 fișiere a câte 10MB fiecare. E mai mult decât suficient pentru orice debug sănătos.
Graceful Reload și OOM
Node.js e destul de gurmand cu memoria. Am observat că pe unele instanțe de AWS, garbage collector-ul mai dă rateuri și memoria crește constant până când procesul devine lent. Am pus max_memory_restart: '800M' pe niște containere care aveau limită de 1GB. E mai bine să ai un restart controlat care durează 2 secunde decât să intre kernel-ul cu OOM Killer-ul și să-ți omoare procesul când ți-e lumea mai dragă.
Și apropo de restart, folosiți pm2 reload în loc de restart. Diferența e că reload pornește procesele noi pe rând, menținându-le pe cele vechi până când cele noi sunt gata. Ca să funcționeze perfect, ai nevoie de un kill_timeout generos. Eu îl pun de obicei la 3000ms. În codul aplicației, trebuie să asculți semnalul SIGINT și să închizi conexiunile la baza de date înainte să lași procesul să moară. Altfel, o să ai tranzacții corupte sau conexiuni 'atârnate' care o să-ți umple pool-ul de DB la fiecare deploy.
E mult mai simplu să dai un pm2 start ecosystem.config.js --env production și să știi că totul e configurat identic, indiferent pe ce mașină rulezi. Voi cum gestionați logurile în producție, mergeți pe varianta locală sau le împingeți direct într-un ELK sau Datadog?