eduardweb.
DevOps & VPSIntermediar#nodejs#devops#pm2#monitoring

De ce healthcheck-ul tău de PM2 minte (și cum am rezolvat-o pe producție)

De Ana Ionescu, 5 iul. 2026 · 18 vizualizări · 2 like-uri

Postat 5 iul. 2026
javascript
const pm2 = require('pm2');
const axios = require('axios');

const HEALTH_URL = 'http://localhost:3000/health/live';
const PROCESS_NAME = 'my-express-app';
const CHECK_INTERVAL = 15000; // 15 secunde
let failureCount = 0;

setInterval(async () => {
  try {
    await axios.get(HEALTH_URL, { timeout: 3000 });
    failureCount = 0;
  } catch (err) {
    failureCount++;
    console.warn(`[Watchdog] Healthcheck failed (${failureCount}/3): ${err.message}`);
    
    if (failureCount >= 3) {
      console.error(`[Watchdog] Process ${PROCESS_NAME} is unresponsive. Restarting...`);
      pm2.connect((connectErr) => {
        if (connectErr) return console.error(connectErr);
        
        pm2.restart(PROCESS_NAME, (restartErr) => {
          pm2.disconnect();
          failureCount = 0;
          if (restartErr) console.error('[Watchdog] Restart failed:', restartErr);
        });
      });
    }
  }
}, CHECK_INTERVAL);

PM2 e probabil cel mai folosit process manager pentru Node.js, dar are o mare problemă de logică: statusul "online" înseamnă doar că procesul de sistem rulează. Atât.

Am pățit-o urât acum vreo doi ani pe un proiect cu peste 15.000 de utilizatori activi concurenți. PM2 arăta totul verde în consolă, dar în realitate utilizatorii primeau erori 502 în neștire. Event loop-ul era blocat complet de o funcție sincronă scrisă prost, iar baza de date își dăduse duhul. Pentru PM2, procesul era viu și nevătămat.

Am învățat atunci, pe pielea mea și după câteva nopți nedormite, ce funcționează cu adevărat și unde se blochează monitorizarea clasică.

De ce minte statusul de PM2

Când rulezi pm2 status, el verifică practic dacă PID-ul (Process ID) alocat aplicației tale mai există în sistemul de operare. Dacă procesul consumă 100% CPU și nu mai răspunde la niciun request, sistemul de operare îl vede activ. PM2 la fel.

Aici intervine diferența dintre un proces viu din punct de vedere OS și un proces funcțional din punct de vedere business.

Dacă ai un memory leak masiv, PM2 îl poate prinde doar dacă configurezi limita de memorie (--max-memory-restart). Dar dacă ai o conexiune picată la baza de date sau un deadlock în PostgreSQL, PM2 nu are de unde să știe asta. El doar ține procesul pornit.

Soluția: Liveness vs Readiness în PM2

Dacă vii din lumea Kubernetes, știi deja conceptele astea. În Node.js simplu rulat pe VPS cu PM2, trebuie să le simulezi singur.

Am încercat inițial să las Nginx-ul să facă tot healthcheck-ul prin directive de tip proxy_next_upstream sau verificări active. Merge bine pentru a scoate instanța din load balancer, dar e nasol dacă vrei ca aplicația să-și dea restart singură, local, pentru a-și reveni dintr-un blocaj temporar de memorie sau de socket-uri.

Un healthcheck eficient are nevoie de două endpoint-uri:

  1. /health/live – Îți spune doar dacă aplicația răspunde la request-uri de bază (verifică dacă event loop-ul e blocat).
  2. /health/ready – Verifică dacă conexiunile la baza de date, Redis sau API-urile externe critice sunt funcționale.

Scriptul de restart automat (Watchdog local)

Pentru a nu complica arhitectura cu tool-uri grele de monitorizare pe un VPS simplu, am scris un script de Node.js care rulează ca un proces separat în PM2 (un fel de instanță de tip sidecar local). Acesta interoghează endpoint-ul de liveness local și, dacă dă fail de trei ori la rând, dă restart programatic procesului blocat.

Am redus astfel downtime-ul neplanificat cu aproape 90% în weekend-uri, pentru că aplicația își dădea singură restart înainte să apuce echipa de on-call să primească alerta pe Slack.

Trade-off-ul major aici: Dacă baza de date pică global, scriptul va intra într-o buclă infinită de restarturi (boot loop). De aceea, în scriptul de mai jos am pus verificarea doar pe endpoint-ul de liveness (dacă aplicația răspunde fizic), nu pe cel de readiness (dacă baza de date e sus). Dacă baza de date e picată global, nu vrei să omori procesul de Node la infinit, vrei doar să-l lași să aștepte reconectarea.

Voi cum gestionați treaba asta în producție când nu aveți Kubernetes? Vă bazați doar pe restarturile automate din PM2 sau aveți scripturi custom de watchdog?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

Doar membrii comunității pot lăsa comentarii.