Come eseguire Node.js in produzione

 

Se hai sviluppato un’applicazione o dei servizi usando Node.js e ora devi passare alla fase successiva: vediamo come eseguire Node.js in produzione.

 

Introduzione

 

Da qualche anno a questa parte, Node.js (e altre tecnologie che sfruttano Javascript e Typescript) sono diventati sempre più popolari: vedi React, Vue, Angular, Svelte, e via dicendo.

 

Per quasi tutte le applicazioni che sfruttano questa tecnologia, ci si trova davanti ad un bivio: eseguirla in locale è relativamente semplice (con un comando come npm start, l’applicazione è pronta per essere eseguita), ma manutenere e gestire eventuali malfunzionamenti non lo è.

 

Inoltre, poiché Node.js è un framework molto utilizzato per chi lavora con servizi di backend, lavorare con processi e thread può rappresentare un ostacolo: è qui che entra in gioco PM2. PM2 è infatti un process manager, ossia uno strumento che permette in pochi e semplici -giuro!- comandi di gestire un ambiente come quello di collaudo o produzione.

 

Cos’è PM2

 

PM2 è un gestore di processi per applicazioni in Node.js che fornisce un bilanciatore di carico integrato; consente di mantenere in vita le applicazioni costantemente, di riavviarle senza tempi di inattività e di facilitare le attività di amministrazione di sistema. Con PM2 è difficile trovarsi nella situazione in cui il processo della tua applicazione o servizio vada in errore, bloccandone l’utilizzo fino al riavvio manuale; è infatti possibile definire una strategia di riavvio automatico in caso di arresto anomalo. Fornisce anche pratici strumenti di monitoraggio e la capacità di bilanciare il carico su più cluster.

Esistono moltissime alternative, come Forever, il Process Manager di StrongLoop e il buon vecchio SystemD, ma PM2, con oltre 60 milioni di download e 34.000 stelle GitHub (in costante aumento) è facile da usare e semplifica la gestione di un ambiente di produzione.

 

Installare PM2

 

$ npm install pm2

 

Come funziona

 

Supponiamo di aver creato una REST API in Express.js (come mostrato in questo articolo): tramite il comando pm2 start possiamo avviare il processo che rende disponibile la nostra app:

 

$ pm2 start index.js

$ pm2 start index.js --watch // riavvio automatico se vengono riscontrati dei cambiamenti nei file (!!)

$ pm2 start index.js --name my-api // assegno un nome al processo


Per verificare lo stato del processo, sarà sufficiente usare l’istruzione list, che ci mostrerà lo stato legato alla nostra app, con informazioni riguardo l’utilizzo delle risorse, il tempo di esecuzione, e via dicendo:

 

$ pm2 list

 

pm2 ls

 

Non solo: è possibile arrestare, riavviare e rimuovere il processo con i seguenti comandi, passando un ID oppure “all” come argomento:

 

$ pm2 stop all // arresta tutti i processi avviati tramite PM2

$ pm2 restart 123 // riavvia il processo con ID 123


$ pm2 delete all // uccide tutti i processi avviati tramite PM2

 

E con i logs? Semplice: usiamo logs per stampare i log recenti, flush per cancellare i file dei logs e reloadLogs per aggiornarli:

 

$ pm2 logs



[TAILING] Tailing last 15 lines for [all] processes (change the value with --lines option)
C:\Users\user\.pm2\pm2.log last 15 lines:
PM2 | 2021-05-03T14:33:15: PM2 log: PM2 version : 4.5.6
PM2 | 2021-05-03T14:33:15: PM2 log: Node.js version : 14.15.3
PM2 | 2021-05-03T14:33:15: PM2 log: Current arch : x64
PM2 | 2021-05-03T14:33:15: PM2 log: PM2 home : C:\Users\user\.pm2
PM2 | 2021-05-03T14:33:15: PM2 log: PM2 PID file : C:\Users\user\.pm2\pm2.pid
PM2 | 2021-05-03T14:33:15: PM2 log: RPC socket file : \\.\pipe\rpc.sock
PM2 | 2021-05-03T14:33:15: PM2 log: BUS socket file : \\.\pipe\pub.sock
PM2 | 2021-05-03T14:33:15: PM2 log: Application log path : C:\Users\user\.pm2\logs
PM2 | 2021-05-03T14:33:15: PM2 log: Worker Interval : 30000
PM2 | 2021-05-03T14:33:15: PM2 log: Process dump file : C:\Users\user\.pm2\dump.pm2
PM2 | 2021-05-03T14:33:15: PM2 log: Concurrent actions : 2
PM2 | 2021-05-03T14:33:15: PM2 log: SIGTERM timeout : 1600
PM2 | 2021-05-03T14:33:15: PM2 log: ===============================================================================
PM2 | 2021-05-03T14:33:15: PM2 log: App [index:0] starting in -fork mode-
PM2 | 2021-05-03T14:33:15: PM2 log: App [index:0] online

C:\Users\user\.pm2\logs\index-out.log last 15 lines:
0|index | Db connected successfully
0|index | Running webapp on port 8081


$ pm2 flush
[PM2] Flushing C:\Users\user\.pm2\pm2.log
[PM2] Flushing:
[PM2] C:\Users\user\.pm2\logs\index-out.log
[PM2] C:\Users\user\.pm2\logs\index-error.log
[PM2] Logs flushed


$ pm2 reloadLogs
Reloading all logs...
All logs reloaded

 

Non finisce qui: è possibile utilizzare PM2 in modalità cluster: in altra parole, PM2 eseguirà diversi processi e sfrutterà il bilanciatore di carico per distribuire il carico di lavoro tra i diversi processi. Per attivarlo, è possibile eseguire il seguente comando, dove andiamo a specificare il numero massimo di processi da eseguire:

 

$ pm2 start app.js -i max_processes

 

Il numero massimo di processi è determinato dal numero di core della macchina su cui si sta eseguendo PM2. Ad esempio, se il sistema ha 4 core, il numero massimo potrà essere di 8 processi in esecuzione contemporaneamente, ossia due processi per core.

 

Passiamo al monitoraggio. Eseguiamo quanto segue per visualizzare il monitoraggio di PM2 e vedremo una schermata simile a quella riportata in figura, dove ci vengono mostrate le metriche dell’applicazione e i metadata:

 

$ pm2 monit

 

pm2 monit

 

Questo ci offre un modo semplice per visualizzare le informazioni della nostra applicazione NodeJS in produzione con alcune altre informazioni utili. Ancora più utile può essere monitorare l’applicazione nel caso in cui i processi siano molteplici; supponiamo allora di aver eseguito il comando precedente che ci permetteva di creare un certo numero massimo di processi, e andiamo a eseguire nuovamente il comando per vedere il monitoraggio:

 

pm2 ls

 

pm2 monit

 

Tips&Tricks

 

Ecosystem

 

PM2 potenzia il flusso di lavoro della gestione dei processi grazie alla possibilità di mettere a punto il comportamento, le opzioni, le variabili di ambiente, i file di log di ciascuna applicazione tramite un file di processo che descrive l’ambiente dove stiamo lavorando sfruttando un file Javascript, YAML o JSON, definendo l’ecosistema del nostro processo.

 

Per generare un nuovo ecosistema, eseguiamo il comando seguente:

 

$ pm2 ecosystem

 

Grazie a questo comando, verrà generato un file ecosystem.config.js simile al seguente, dove vengono riportate le informazioni sulla definizione dell’applicazione e degli ambienti:

<span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span>
  <span class="na">apps</span> <span class="p">:</span> <span class="p">[{</span>
    <span class="na">name</span><span class="p">:</span> <span class="dl">"</span><span class="s2">app</span><span class="dl">"</span><span class="p">,</span>
    <span class="na">script</span><span class="p">:</span> <span class="dl">"</span><span class="s2">./app.js</span><span class="dl">"</span><span class="p">,</span>
    <span class="na">env</span><span class="p">:</span> <span class="p">{</span>
      <span class="na">NODE_ENV</span><span class="p">:</span> <span class="dl">"</span><span class="s2">development</span><span class="dl">"</span><span class="p">,</span>
    <span class="p">},</span>
    <span class="na">env_production</span><span class="p">:</span> <span class="p">{</span>
      <span class="na">NODE_ENV</span><span class="p">:</span> <span class="dl">"</span><span class="s2">production</span><span class="dl">"</span><span class="p">,</span>
    <span class="p">}</span>
  <span class="p">}]</span>
<span class="p">}</span>

 

 

Una volta modificato opportunamente questo file, è possibile sfruttarlo per avviare i servizi con il comando start visto in precedenza:

 

$ pm2 start ecosystem.config.js

 

All’interno di questo file possiamo configurare tutte le proprietà viste in precedenza, come watch, l’uso della modalità cluster, e via dicendo:

 


<span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span>
  <span class="na">apps</span> <span class="p">:</span> <span class="p">[{</span>
    <span class="na">name</span><span class="p">:</span> <span class="dl">"</span><span class="s2">app</span><span class="dl">"</span><span class="p">,</span>
    <span class="na">script</span><span class="p">:</span> <span class="dl">"</span><span class="s2">./app.js</span><span class="dl">"</span><span class="p">,</span>
    <span class="na">env</span><span class="p">:</span> <span class="p">{</span>
      <span class="na">NODE_ENV</span><span class="p">:</span> <span class="dl">"</span><span class="s2">development</span><span class="dl">"</span><span class="p">,</span>
    <span class="p">},</span>
    <span class="na">env_production</span><span class="p">:</span> <span class="p">{</span>
      <span class="na">NODE_ENV</span><span class="p">:</span> <span class="dl">"</span><span class="s2">production</span><span class="dl">"</span><span class="p">,</span>
    <span class="p">},
    "instances": 6,
    "exec_mode": "cluster",
    "watch": false</span>
  <span class="p">}]</span>
<span class="p">}</span>

 

 

Condividi la tua opinione