Despliegue a producción para NodeJs: a mi manera con PM2

Una de las cosas que más me ha ayudado a apreciar y compartir mi trabajo cuando desarrollo una app o una API, sin duda, ha sido aprender a realizar mis despliegues al servidor de producción.

Tenemos nuestra aplicación ya hecha en NodeJs. Todo parece ir bien, y ya estamos con ganas de ver todo nuestro trabajo desplegado en una ruta real. Si es una webapp queremos verla en su dominio. Si es una API queremos que los endpoints ya funcionen más allá de nuestro local. Al final, lo más bonito de este trabajo es poder compartir las funcionalidades que desarrollamos con todo el mundo.

Pero ¿Qué pasa cuando nuestra app en Node tiene que ser desplegada a producción? ¿Seguimos el mismo procedimiento o hay algo más que hacer? ¿Es suficiente copiar la carpeta DIST en el servidor?
Pues hay un poco más que hacer, y se puede hacer de muchas formas. Hoy os voy a mostrar mi manera de hacerlo utilizando mi herramienta clave: PM2

1. Índice

  1. El entorno utilizado
  2. Qué es PM2
  3. Angular SSR: Desplegando desde el lado del servidor
  4. Vamos a hacerlo manualmente: desplegamos node con PM2
  5. Hacer un Proxy Inverso con Apache2 (y Plesk)

El entorno utilizado: Servidor cloud en Linux Ubuntu/Debian

No todos los entornos son iguales. La carácteristica más determinante del servidor que estamos utilizando es, entre otras, el sistema operativo. Este afecta aspectos importantes de nuestras operaciones como

  • Rutas y directorios, un ejemplo son las ubicaciones de las carpetas raíces de nuestros virtual hosts
  • Configuraciones necesarias para los proxys inversos, como el uso de apache2 o nginx, o la necesidad de eliminar carpetas de pre-configuración cuando utilizamos interfaces como Plesk (¡es mi caso!)

Las indicaciones que daré en este despliegue, por ende, podrían ser sólo parcialmente correctas en otros sistemas operativos.

Requisitos para realizar este despliegue

A raíz de lo dicho, vamos a ver qué he utilizado para el despliegue que haremos en esta ocasión:

  • 🔥 Un Servidor VPS (Virtual Private Server) con sistema operativo Linux, Ubuntu/Debian
  • 🔥 Apache2 como servidor en el proxy inverso, porque Plesk hace más fácil la utilización de Apache (aunque afecta la realización del proxy inverso)
  • 🔥 Una aplicación en Angular 17+, ya inicializada con el flag --ssr
    ng new mi-aplicacion --ssr

PM2: Un demonio para mantener en segundo plano nuestras apps

PM2, Process Manager 2, es un Módulo de Gestión de Procesos que nos ayuda a mantener online y monitorear nuestras aplicaciones. Aunque esté originariamente hecho para NodeJs, se puede utilizar para muchos tipos de procesos.

Sus ventajas principales son que Es gratuito, de código abierto y tiene un equilibrador de carga incorporado. Reinicia automáticamente el servidor Node.js cuando un proceso deja de funcionar. Es multiplataforma y se puede ejecutar en Windows, Linux y macOS. Te permite ejecutar la aplicación Node.js en modo de clúster sin realizar ningún cambio en el código.

¿Por qué necesitamos PM2 para desplegar? PM2 es una de aquellas herramientas que nos permiten ejecutar los procesos en segundo plano.

Si simplemente entrásemos en nuestro servidor, en la carpeta raíz de nuestro proyecto, y ejecutáramos de esta forma nuestra app:

node index.js

Seguramente, a la hora de salir de nuestra terminal, el proceso que habíamos activado se apagaría, porque node es como una bombilla que tiene que ser presionada para poder mantenerse encendida.

En cambio, PM2 mantiene nuestros procesos en segundo plano y los reinicia en caso de error.

Angular SSR: renderizando las rutas desde el servidor

Aplicaciones web hechas con frameworks como Angular necesitan un cuidado especial a la hora de querer mejorar el posicionamiento en los motores de búsqueda.

Si necesitamos que nuestra webapp se indicize bien, si hay trabajo de keywords y si queremos que las meta tags de cada página sean específicas, necesitamos que el navegador lea nuestra app a lado de servidor, no a lado de cliente.

Esto porque los bots de Google, por ejemplo, no son muy buenos en leer páginas renderizadas a lado del navegador directamente con Js. De hecho la mayoría de los bots, en realidad, no utilizan Javascript. Así que si queremos que nuestra app la rompa en Google, dale de SSR :)

Por otro lado, generalmente, el CSR (Client Side Rendering) es útil cuando tenemos un aplicativo altamente interactivo. A partir de angular 18 se está implementando el Hybrid Rendering para cuando queramos que en nuestra página se renderice parte del contenido por servidor, y parte por navegador.

Vamos a hacerlo manualmente: despliegue de tu app en SSR en el servidor

Vamos ahora a ver cómo ejecutar una aplicación en PM2 manualmente en un servidor

Vale. Vamos a empezar por la creación de un nuevo proyecto. Como comentábamos antes, uno de los requisitos para poder hacer todo esto es crear nuestro proyecto con el flag --ssr utilizando una versión superior a 17 de Angular.

ng new mi-aplicacion --ssr

En caso en que no lo hayamos hecho, tendremos que instalar Angular Universal en nuestro proyecto a posteriori.

Y luego generar la carpeta /dist de nuestro proyecto:

ng build

En Angular 17+, la carpeta /dist tendrá esta estructura:


dist/
├── mi-app/
│   ├── browser/
│   ├── server/
│   ├── prerendered-routes.json
│   └── 3rdpartylicenses.txt

☑️ Configura el puerto en que verás desplegada inicialmente tu aplicación en el archivo server.ts:


//server.ts de tu app en angular CLI:
function run(): void {
  const port = process.env['PORT'] || 4200;

  // Start up the Node server
  const server = app();
  server.listen(port, () => {
    console.log('Node Express server listening on http://localhost:${port}');
  });
}

☑️ Una vez que ya tengas todo esto, conéctate a tu servidor cloud via SSH (yo lo hago con MobaXTerm).

☑️ Copia la carpeta /dist y todo su contenido en la carpeta raíz de tu servidor (si no tienes configuradas unas llaves SSH Puedes mirar esto o hacer esta operación directamente en FileZilla)

Por ejemplo en tu servidor Ubuntu con un virtual host de Plesk, una ruta típica de un virtual host puede ser esta:

/var/www/vhosts/miproyecto.misitioweb.es/httpdocs

☑️ Colócate desde MobaXTerm en la esta carpeta raíz:

cd /var/www/vhosts/miproyecto.misitioweb.es/httpdocs

☑️ Instala node y PM2 en tu servidor, si ya no lo habías hecho:

# curl -sL https://deb.nodesource.com/setup_14.x | bash - 
# apt-get install nodejs -y
# node --version
# npm i -g pm2 

☑️ Posicionate en la carpeta /dist/nombre-proyecto/server/:

cd dist/mi-app/server

Si tu app está hecha en Typescript, lo más probable es que los archivos Js en la dist serán unos .mjs. En caso en que esté en Javascript, las extensiones serán los .js de siempre. Supongamos que la app está en Typescript:

pm2 start index.mjs --name mi-app

☑️ Verifica que tu aplicación esté online:

pm2 status

Esta es la tabla que deberías ver en tu terminal al ejecutar este comando:

En este caso tiene muchos procesos activo. En tu caso, si es tu primer despliegue, solo verás uno.

Cada tabla te devuelve muchos datos sobre tu proceso:

  • 💻 Su id único (PID)
  • 💻 Su nombre
  • 💻 Su namespace: una etiqueta que puedes ir dándole si quieres
  • 💻 El mode, que puede ser fork (como en este caso) o cluster (como lo haremos en la automatización)
  • 💻 El uptime: el número de minutos que lleva online
  • 💻 Número de reinicios desde el útimo start
  • 💻 Status: el estado del proceso, que si no es online puede ser por ejemplo stopped o errored
  • 💻 El uso de CPU, de memoria y el usuario que lo desplegó.

Estos comandos son los más básicos de pm2, y puedes ir probándolos en tu servidor:


        pm2 start mi-app.js --name mi-app --namespace angular #hace un start del archivo desde cero
        pm2 restart mi-app #hace un reinicio de un proceso ya iniciado anteriormente
        pm2 startup #guarda la configuración después de un start, en caso de reinicios
        pm2 save #guarda la configuración después de un restart
        pm2 stop mi-app #pausa el proceso
        pm2 delete mi-app #elimina el proceso
        pm2 describe mi-app #te devuelve muchos datos sobre tu proceso específico
        pm2 logs #te devuelve unos logs unificados de todos tus procesos. Muy útil cuando nos falla algo y cuando algún proceso da errored
    

¡Ahora deberíamos de tener nuestra app activa! Vamos al puerto que configuramos en server.ts (en este caso, el 4200), y desde el navegador vamos a http://nuestrodominio.es:4200 (recuerda haber temporalmente habilitado un firewall para abrir este puerto, y recuerda cerrarlo cuando ya tengas tu proxy inverso hecho 😆)

Lo que veremos en el puerto 4200 sólo es una versión provisional de nuestra app, porque ahora nos toca hacer un proxy inverso con Apache2

Hacer un Proxy Inverso con Apache2 (Y Plesk)

En mi servidor estoy utilizando el control panel para hosting Plesk, porque me facilita muchísimo la creación de virtual hosts, databases, asignación de certificados SSL con Let's Encrypt y muchas más cositas interesantes. Vamos a ver todos los pasos para implementar un proxy inverso para poder ver reflejada nuestra app en el puerto 80, que es el puerto por defecto al que se accede públicamente, y via https, a un sitio web.

🚀 Posiciónate en la ruta /etc/apache2/sites-available/ en tu servidor, desde la terminal:

cd /etc/apache2/sites-available/

🚀 Ahora, en tu ordenador, genera un archivo .config. Puede tener por ejemplo el nombre de tu dominio con la extensión .config: misitio.config

Aquí tienes un típico archivo de configuración de proxy. Adáptalo con las características de tu sitio web:

Es importante recordar que en esta configuración se está utilizando un certificado SSL sencillo y gratuito de Let's Encrypt, proporcionado normalmente por la interfaz de Plesk. Por ende, tendrás que enlazar tu configuración de apache a la siguiente carpeta:

/opt/psa/var/modules/letsencrypt/etc/live/
<VirtualHost aqui.tu.ip:80>
    ServerName tudominio.com
    Redirect permanent / https://tudominio.com/
</VirtualHost>

<VirtualHost aqui.tu.ip:443>
    ServerName tudominio.com
    ProxyRequests Off
    ProxyPreserveHost On
    ProxyPass / http://localhost:4200/
    ProxyPassReverse / http://localhost:4200/

    SSLEngine on
    SSLCertificateFile /opt/psa/var/modules/letsencrypt/etc/live/tudominio.com/cert.pem
    SSLCertificateKeyFile /opt/psa/var/modules/letsencrypt/etc/live/tudominio.com/privkey.pem
    SSLCertificateChainFile /opt/psa/var/modules/letsencrypt/etc/live/tudominio.com/chain.pem

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

Puedes ir sustituyendo el campo aqui.tu.ip con la ip de tu servidor. Sobre todo si usas Plesk, esto va a ser muy necesario para no pisarte con las configs de Plesk. En otros casos podría no ser necesario incluir la ip pública en este archivo.

🚀 Cuando ya tengas esto hecho, copia desde tu ordenador el contenido de este archivo en la ruta anteriormente indicada de tu servidor:

sudo nano /etc/apache2/sites-available/tusitio.config

Guarda haciendo CTRL+O

🚀 Y ahora, para terminar, y sólo si usas Plesk, vas a tener que desactivar el archivo por defecto que genera plesk para todos sus virtual hosts en la siguiente ruta del servidor:

/etc/apache2/plesk.conf.d/vhosts/

Accede a esta ruta con cd, y renombra el archivo correspondiente a tu dominio para deshabilitarlo. Ejemplo, le añadimos al final un .disabled:

midominio.com.conf.disabled

🚀 Para que todo funcione, actualiza Apache2 en tu servidor con estos comandos:


    a2ensite tusitio.config
    sudo systemctl reload apache2
    sudo systemctl restart apache2
  

Si todo ha ido a buen fin, si vas a tu https://tudominio.com, podrás ver ya tu aplicación desplegada y reflejada en el puerto correcto: el puerto 80.

🚀 Cierra el acceso al puerto 4200 desde tu firewall y disfruta de tu trabajo online!

🚀 Ya tienes la base para empezar a cuidar más aspectos de tu SEO on page: Aquí te explico como setear las meta tags con ngx-seo-helper, y aquí cómo generar un sitemap dinámico. Ambos puntos son bastante importantes en trabajos de posicionamiento web.

Acabamos de ver juntos cómo desplegar una app en node en un servidor Cloud en Ubuntu/Debian. Esto dará mucho potencial a tus proyectos y te permitirá aumentar tu expertise en front en algo que se cruza mucho con el marketing digital. Hay pocos especialistas SEO en este mundo. Tú podrías hacer la diferencia.
Si tienes dudas, recuerda que Puedes escribirme a contacto@giuliacapozzi.com