Usar PM2 para gestionar aplicaciones

Problemas con las aplicaciones de Node.js

Si llevas tiempo trabajando con Node.js sabrás que una de sus grandes limitaciones es que se ejecuta en un solo core, o núcleo del procesador. De otros temas "complicados" como las dependencias ya hablaremos en futuras ocasiones...

Que Node.js use solo un core no es un gran problema en sí mismo, recuerda que otros motores de ejecución muy populares también son monocore y no van llorando por ahí.

No obstante deberás tener en mente que si ejecutamos nuestro código en un servidor que cuenta con varios núcleos, ya sea dedicado o VPS, estaríamos desaprovechando potencia de computación ya que solo se estaría utilizando un core, quedando el resto sin usar... eso es desperdiciar dinero y no mola.

samurai harakiri
El Harakiri puede solucionar muchas cosas, pero no esta...

¡Tranquilidad! No recurras al seppuku ni regreses a PHP (todavía), aún hay esperanza, viene en forma de paquete de NPM y se llama PM2.

Qué es PM2

PM2 es un gestor de procesos OpenSource para Node.js que fue creado por Alexandre Strzelewicz allá por 2013. PM2 está disponible para Windows, Mac y Linux, y puedes encontrar su repositorio oficial en GitHub.

Quizá la definición de gestor de procesos no te dice nada, pero traducido al cristiano vendría a ser "un programa que gestiona tu código para que esté siempre funcionando, incluso cuando peta".

PM2 se encarga de que tus programas estén funcionado de la forma correcta, incluso gestionando reinicios, logs, despliegues e incluso monitoreo de tu aplicación. La magia de este proyecto viene con su posibilidad de controlar la ejecución de cualquier script, no solo funciona con Node.js. Así que puedes controlar tus scripts de Python, Ruby, Perl y, Dios no quiera que te veas en ello, de PHP.

PM2 permite simplificar el proceso de gestionar aplicaciones, sobre todo en entornos de producción.

-- Proverbio Japonés

El escenario perfecto donde PM2 puede ayudarnos a gestionar nuestros proyectos es con una arquitectura basada en micro servicios, ya que podemos ver y gestionar cada proceso por separado y realizar tareas sobre uno sin afectar al resto. Todo desde la misma interfaz de control.

Para finalizar con la radiografía general de qué es PM2 y por qué mola tanto, contarte que PM2 permite ejecutar nuestro código en modo cluster, con lo que se lanza una instancia de nuestro proyecto por cada uno de los cores de la CPU y PM2 gestiona las peticiones a cada una de estas instancias mediante un balanceador de carga 100% transparente para tu código y los clientes que acceden a la aplicación.

Si a estas alturas no estás flipando en colores ya no sé que más contarte... vamos con la instalación.

Cómo instalar PM2

Para instalar PM2 debemos de tener instalados previamente Node.js y NPM. Te dejo un artículo donde te cuento cómo instalar Node.js en Windows y Linux.

Una vez tenemos instalados Node.js y NPM podemos instalar PM2 abriendo una consola y escribiendo npm install -g pm2. Este proceso es idéntico para Linux, Windows y Mac.

En este caso el parámetro -g hace referencia a que es un paquete que se instalará en el ámbito global de Node.js.

Es posible que la instalación de paquetes de npm a nivel global te de algún error de permisos en Linux. Hay varias formas de solucionar esto, pero lo mas sencillo es que ejecutes el comando con sudo: sudo npm install -g pm2.

Cómo funciona PM2

PM2 nos permite gestionar nuestro proyectos, sí, pero hay dos formas de indicar a PM2 qué queremos ejecutar y la configuración que necesitamos aplicar. La primera opción es pasar los datos directamente a través de la línea de comandos, y la segunda es usando un fichero de configuración.

Comandos de PM2

Cómo ya he dicho anteriormente, PM2 es muy amplio pero vamos a ver una lista de los comandos más habituales y ya tendremos tiempo de complicarnos la existencia más adelante.

pm2 list Te Muestra el panel de información sobre los procesos que PM2 gestiona. También puedes usar ls, l, o status como alias de este comando. Yo suelo usar pm2 status por costumbre 😉

pm2 status
Detalle de las aplicaciones gestionadas por PM2

Como puedes observar en la imagen, cada aplicación tiene asociado un identificador y nombre, si se lo hemos indicado. Al lanzar un comando para gestionar nuestra aplicación podremos identificarla por el id o por su name. En caso de querer lanzar un comando para todas las apliciones no es necesario ir por cada una indicando su id o name. En ese caso usaremos la palabra reservada all.

Este comportamiento es que aplica a mayoria de comandos disponibles.

pm2 restart <id|name|all> Reinicia manualmente la aplicación indicada o todas ellas.

pm2 stop <id|name|all> Detiene manualmente la aplicación indicada o todas ellas.

pm2 delete <id|name|all> Elimina manualmente la aplicación indicada o todas ellas.

pm2 logs <id|name> Muestra un listado con los logs de todas las aplicaciones por defecto. Deberás indicar el id o name si quieres ver el de una en concreto.

Gestionar una aplicación con PM2 desde línea de comandos

La forma más simple de iniciar una aplicación de Node.js con PM2 es esta:

pm2 start app.js

Este comando arranca la aplicación y la pone a funcionar con todas las opciones por defecto y en modo fork.

PM2 arranque básico en node.js
Arranque básico de PM2

¿Recuerdas que te he dicho que PM2 puede gestionar aplicaciones de otros tipos?

Tres ejemplos ejecutando dos script, uno de bash y otro de python, y un fichero binario.

pm2 start my-script.sh
pm2 start app.py
pm2 start binary-file

PM2 dispone de una cantidad de parámetros bastante grande, pero puedes comenzar con algunos de estos.

--name <app_name> Indica el nombre de la aplicación, así podremos usar este nombre para referirnos a ella en lugar de usar el id que le asigna PM2.

Ejemplo: pm2 start app.js --name reservas_online

--max-memory-restart <memory><K|M|G> Especifica la cantidad máxima de memoria RAM que el proceso podrá usar antes de que PM2 lo reinicie por glotón. Puedes usar K, M o G como unidades de medida para la memoria.

Ejemplo: pm2 start app.js --name reservas_online --max-memory-restart 200M

-- arg1 arg2 ... argN Te permite pasar parámetros directamente a tu script. Personalmente recomiendo usar dotEnv para este tema, pero para un apaño te puede servir. Recuerda que lo que pasas a la app es el valor del argumento y en el orden que lo escribes. Te recomiendo que pases este parámetro a PM2 en último lugar para evitar confusiones con otros argumentos de la línea de comandos.

Ejemplo: pm2 start app.js --name reservas_online --max-memory-restart 200M -- development 8080

--restart-delay <delay in ms> Especifica el tiempo en milisegundos que PM2 va a esperar entre cada reinicio automático de la aplicación.

Ejemplo: pm2 start app.js --name reservas_online --max-memory-restart 200M --restart-delay 5000 -- development 8080

Hay más parámetros, pero como toma de contacto creo que son suficientes. ¡Seguimos para bingo!

Gestionar una aplicación con PM2 y el fichero ecosystem.config

Hasta ahora hemos estado usando la línea de comandos para configurar cómo se deben gestionar cada una de nuestras aplicaciones, pero hay una forma más adecuada de hacer esto, y es a través del fichero de configuración ecosystem.config.

Este fichero se puede generar a mano o bien ejecutar el comando pm2 init simple para que PM2 lo genere por nosotros, y así de paso ahorramos tiempo y reducimos las posibilidades de error.

PM2 generar fichero ecosystem.config
Generando el fichero ecosystem.config

Si abrimos el fichero con un editor de código podremos ver algo similar a esto:

module.exports = {
apps : [{
name : "app1",
script : "./app.js"
}]
}

Dentro de la variable apps podremos añadir tantas aplicaciones como queramos que PM2 nos gestione, además de la configuración para cada una de ellas.

Modifiquemos un poco el fichero para añadir nuestras apps. Aquí hemos configurado las mismas opciones que anteriormente por línea de comandos, es decir name, script, max_memory_restart, restart_delay y args.

module.exports = {
apps: [
{
name: "my node app",
script: "./app.js"
},
{
name: "my python app",
script: "./test.py",
interpreter: "/usr/bin/python3.8",
max_memory_restart: "200M",
restart_delay: "5000",
args: "development 8080"
}
]
}

Si observas un poco te darás cuenta que cada app que definamos puede tener su propio conjunto de configuraciones, y además que en las aplicaciones de Node.js no necesitamos indicarle el intérprete, cosa que sí tendremos que hacer con el resto a través del parámetro interpreter, indicando la ruta absoluta al binario del intérprete que necesites.

Otro punto a destacar es que el parámetro script espera recibir la ruta relativa al script de tu aplicación desde la ubicación del fichero ecosystem.config. Por eso es recomendable que el fichero de configuración de PM2 esté ubicado en la carpeta raiz de tus proyectos, así te evitarás confusiones y que PM2 te tire errores a la cara uno tras otro.

Como usar el fichero ecosystem.config de PM2

Ya tenemos las aplicaciones definidas en nuestro fichero de configuración, vamos a ver como se gestiona todo esto.

pm2 start ecosystem.config.js Inicia todas las aplicaciones definidas en el fichero.

pm2 stop ecosystem.config.js Detiene todas las aplicaciones definidas en el fichero.

pm2 restart ecosystem.config.js Reinicia todas las aplicaciones definidas en el fichero.

pm2 delete ecosystem.config.js Borra todas las aplicaciones definidas en el fichero.

Como podrás observar ya no hacemos referencia a nuestras aplicaciones por su nombre o su ID, sino que PM2 las trata a todas ellas como un conjunto, y lo que ordenamos a una lo ordenamos a todas.

Arrancamos nuestras apps con pm2 start ecosystem.config.js, y podremos ver la ventana de estatus.

PM2 detalle de estatus
Detalle de estatus de PM2 tras el arranque

Para ver que nuestros scripts están corriendo vamos a revisar el log de PM2. Para ello ejecuta pm2 logs.

PM2 detalle del log
Detalle del log

Ahí podemos ver nuestras aplicaciones escupiendo por consola sus mensajes cada ciertos segundos. Observa como PM2 identifica cada línea del log con el name e id de cada aplicación.

Ya hemos visto nuestras aplicaciones haciendo su trabajo, ahora podemos detenerlas con un pm2 stop ecosystem.config.

PM2 detención de las aplicaciones
Detalle de las aplicaciones

Observa como la columna de status indica stopped... en colorao y negrita para que te de susto 😰

Si nos hemos cansado de nuestras aplicaciones y queremos que PM2 se olvide de ellas, basta con ejecutar pm2 delete ecosystem.config y listo. Ya podemos crear otras o volver a lanzarlas con el comando pm2 start ecosystem.config... ¡tu decides!

PM2 borrado de las aplicaciones
PM2 ha borrado las aplicaciones

Conclusiones

En esté artículo hemos visto una aproximación inicial a PM2 y su potencial, pero cubriendo la mayoría de las funciones que se suelen usar al trabajar con este software.

Hemos dejado fuera temas muy chulos como el despliegue, la gestión de logs, monitoreo o la configuración avanzada del fichero ecosystem.config entre otros. Iremos cubriendo cada una de estos apartados más adelante.

De momento te dejo en GitHub los ficheros usados en el artículo por si te apetece replicarlo. Además tienes el hilo del post en Twitter por si quieres comentar alguna cosa... ¡nos leemos!