Nunca debe ejecutar directamente contra Node.js en producción. Tal vez.

A veces me pregunto si sé mucho de algo.

Hace apenas unas semanas, estaba hablando con un amigo que mencionó de improviso, "nunca ejecutarías una aplicación directamente contra Node en producción".

Asentí con la cabeza vigorosamente a la señal que yo también iba a funcionar nunca en contra de nodo en la producción porque ... jajaja ... .everyone sabe. ¡Pero yo no sabía eso! ¿Debería haberlo sabido? ¿SIGUE PERMITIDO PROGRAMAR?

Si tuviera que dibujar un diagrama de Venn de lo que sé frente a lo que siento que todos los demás saben, se vería así ...

Por cierto, ese pequeño punto se vuelve más pequeño a medida que envejezco.

Hay un diagrama mejor creado por Alicia Liu que cambió mi vida. Ella dice que es más así ...

Me encanta este diagrama porque quiero que sea cierto. No quiero pasar el resto de mi vida como un pequeño punto azul insignificante que se encoge.

TAN DRAMÁTICO. Culpa a Pandora. No controlo lo que se juega a continuación mientras escribo este artículo y Dashboard Confessional es una droga increíble.

Bueno, suponiendo que el diagrama de Alicia sea cierto, me gustaría compartir con ustedes lo que ahora sé sobre la ejecución de aplicaciones de Node en producción. Quizás nuestros diagramas de Venn relativos no se superponen en este tema.

En primer lugar, abordemos la afirmación "nunca ejecute aplicaciones directamente contra Node en producción".

Nunca ejecute directamente contra Node en producción

Tal vez. Pero tal vez no. Hablemos del razonamiento detrás de esta declaración. Primero, veamos por qué no.

Digamos que tenemos un servidor Express simple. El servidor Express más simple que se me ocurre ...

const express = require("express"); const app = express(); const port = process.env.PORT || 3000; // viewed at //localhost:3000 app.get("/", function(req, res) { res.send("Again I Go Unnoticed"); }); app.listen(port, () => console.log(`Example app listening on port ${port}!`));

Ejecutaríamos esto con un script de inicio en el package.jsonarchivo.

"scripts": { "dev": "npx supervisor index.js", "start": "node index.js" }

Aquí hay dos problemas. El primero es un problema de desarrollo y el segundo es un problema de producción.

El problema de desarrollo es que cuando cambiamos el código, tenemos que detener e iniciar la aplicación para que se recojan los cambios.

Para resolver eso, usualmente usamos algún tipo de administrador de procesos de Node como supervisoro nodemon. Estos paquetes vigilarán nuestro proyecto y reiniciarán nuestro servidor cada vez que hagamos cambios. Normalmente lo hago así ...

"scripts": { "dev": "npx supervisor index.js", "start": "node index.js"}

Luego corro npm run dev. Tenga en cuenta que estoy corriendo npx supervisoraquí, lo que me permite usar el supervisorpaquete sin tener que instalarlo. Yo ❤️ 2019. Mayormente.

Nuestro otro problema es que todavía estamos corriendo directamente contra Node y ya dijimos que eso era malo y ahora estamos a punto de averiguar por qué.

Voy a agregar otra ruta aquí que intenta leer un archivo del disco que no existe. Este es un error que podría aparecer fácilmente en cualquier aplicación del mundo real.

const express = require("express"); const app = express(); const fs = require("fs"); const port = process.env.PORT || 3000; // viewed at //localhost:3000 app.get("/", function(req, res) { res.send("Again I Go Unnoticed"); }); app.get("/read", function(req, res) { // this does not exist fs.createReadStream("my-self-esteem.txt"); }); app.listen(port, () => console.log(`Example app listening on port ${port}!`));

Si ejecutamos esto directamente contra Node con npm starty navegamos hasta el read punto final, obtenemos un error porque ese archivo no existe.

Lo cual, no es gran cosa, ¿verdad? Es un error. Sucede.

NO. Vaya cosa. Si regresa a su terminal verá que la aplicación está completamente inactiva.

Lo que significa que si vuelve al navegador e intenta ir a la URL raíz del sitio, obtendrá la misma página de error. Un error en un método eliminó la aplicación para todos .

Eso es malo. Realmente mal. Esta es una de las principales razones por las que la gente dice "nunca corra directamente contra Node en producción" .

OKAY. Entonces, si no podemos ejecutar Node en producción, ¿cuál es la forma correcta de ejecutar Node en producción?

Opciones para el nodo de producción

Tenemos algunas opciones.

Uno de ellos sería simplemente usar algo como supervisoro nodemonen producción de la misma manera que los estamos usando en dev. Eso funcionaría, pero estas herramientas son un poco ligeras. Una mejor opción es algo llamado pm2.

pm2 el rescate

pm2 es un administrador de procesos de Node que tiene muchas campanas y silbatos. Al igual que todo lo demás "JavaScript", lo instalas (globalmente) desde npm- o puedes usarlo npxnuevamente. No quiero decirte cómo vivir tu vida.

Hay muchas formas de ejecutar su aplicación con pm2. La forma más sencilla es simplemente llamar pm2 starta su punto de entrada.

"scripts": { "start": "pm2 start index.js", "dev": "npx supervisor index.js" },

Y verás algo como esto en la terminal ...

Ese es nuestro proceso ejecutándose en segundo plano monitoreado por pm2. Si visita el readpunto final y bloquea la aplicación, pm2 la reiniciará automáticamente. No verá nada de eso en la terminal porque se está ejecutando en segundo plano. Si quieres ver a pm2 hacer lo suyo, tienes que correr pm2 log 0. El 0es el ID del proceso que queremos ver los registros de.

¡Aquí vamos! Puede ver que pm2 reinicia la aplicación cuando se cae debido a nuestro error no controlado.

También podemos sacar nuestro comando dev y hacer que pm2 vigile los archivos para nosotros y reiniciar con cualquier cambio.

"scripts": { "start": "pm2 start index.js --watch", "dev": "npx supervisor index.js" },

Tenga en cuenta que debido a que pm2 ejecuta cosas en segundo plano, no puede ctrl+csalir de un proceso pm2 en ejecución. Tienes que detenerlo pasando la identificación o el nombre.

pm2 stop 0

pm2 stop index

Además, tenga en cuenta que pm2 conserva una referencia al proceso para que pueda reiniciarlo.

If you want to delete that process reference, you need to run pm2 delete. You can stop and delete a process in one command with delete.

pm2 delete index

We can also use pm2 to run multiple processes of our application. pm2 will automatically balance the load across those instances.

Multiple processes with pm2 fork mode

pm2 has a ton of configuration options and those are contained in an “ecosystem” file. To create one, run pm2 init. You’ll get something like this…

module.exports = { apps: [ { name: "Express App", script: "index.js", instances: 4, autorestart: true, watch: true, max_memory_restart: "1G", env: { NODE_ENV: "development" }, env_production: { NODE_ENV: "production" } } ] };

I’m going to ignore the “deploy” section in this article because I have no idea what it does.

The “apps” section is where you define the apps you want pm2 to run and monitor. You can run more than one. A lot of these configuration settings are probably self-explanatory. The one that I want to focus on here is the instances setting.

pm2 can run multiple instances of your application. You can pass in a number of instances that you want to run and pm2 will spin up that many. So if we wanted to run 4 instances, we could have the following configuration file.

module.exports = { apps: [ { name: "Express App", script: "index.js", instances: 4, autorestart: true, watch: true, max_memory_restart: "1G", env: { NODE_ENV: "development" }, env_production: { NODE_ENV: "production" } } ] };

Then we just run it with pm2 start.

pm2 is now running in “cluster” mode. Each of these processes is running on a different CPU on my machine, depending on how many cores I have. If we wanted to run a process for each core without knowing how many cores we have, we can just pass the max parameter to the instances value.

{ ... instances: "max", ... }

Let’s find out how many cores I’ve got in this machine.

8 CORES! Holy crap. I’m gonna install Subnautica on my Microsoft issued machine. Don’t tell them I said that.

The good thing about running processes on separate CPU’s is that if you have a process that runs amok and takes up 100% of the CPU, the others will still function. If you pass in more instances than you have cores, pm2 will double up processes on CPU’s as necessary.

You can do a WHOLE lot more with pm2, including monitoring and otherwise wrangling those pesky environment variables.

One other item of note: if for some reason you want pm2 to run your npm start script, you can do that by running npm as the process and passing the -- start. The space before the “start” is super important here.

pm2 start npm -- start

In Azure AppService, we include pm2 by default in the background. If you want to use pm2 in Azure, you don’t need to include it in your package.json file. You can just add an ecosystem file and you’re good to go.

OK! Now that we’ve learned all about pm2, let’s talk about why you may not want to use it and it might indeed be ok to run directly against Node.

Running directly against Node in production

I had some questions on this so I reached out to Tierney Cyren who is part of the enormous orange circle of knowledge, especially when it comes to Node.

Tierney pointed out a few drawbacks to using Node based process managers like pm2.

The main reason is that you shouldn’t use Node to monitor Node. You don’t want to use the thing that you are monitoring to monitor that thing. It’s kind of like you asking my teenage son to supervise himself on a Friday night: Will that end badly? It might, and it might not. But you’re about to find out the hard way.

Tierney recommends that you not have a Node process manager running your application at all. Instead, have something at a higher level which watches multiple separate instances of your application. For example, an ideal setup would be if you had a Kubernetes cluster with your app running on separate containers. Kubernetes can then monitor those containers and if any of them go down, it can bring them back and report on their health.

In this case, you can run directly against Node because you are monitoring at a higher level.

As it turns out, Azure is already doing this. If we don’t push a pm2 ecosystem file to Azure, it will start the application with our package.json file start script and we can run directly against Node.

"scripts": { "start": "node index.js" }

In this case, we are running directly against Node and it’s OK. If the application were to crash, you’ll notice that it comes back. That’s because in Azure, your app runs in a container. Azure is orchestrating the container in which your app is running and knows when it faceplants.

But you still only have one instance here. It takes the container a second to come back online after it crashes meaning that there could be a few seconds of downtime for your users.

Ideally, you would want more than one container running. The solution to this would be to deploy multiple instances of your application to multiple Azure AppService sites and then use Azure Front Door to load balance the apps behind a single IP address. Front Door will know when a container is down and will route traffic to other healthy instances of your application.

Azure Front Door Service | Microsoft Azure

Deliver, protect and track the performance of your globally distributed microservice applications with Azure Front Door…azure.microsoft.com

systemd

Another suggestion that Tierney had is to run Node with systemd. I don’t understand too much (or anything at all) about systemd and I’ve already messed this phrasing up once already, so I’ll let Tierney say it in his own words…

Esta opción solo es posible si tiene acceso a Linux en su implementación y controla la forma en que Node se inicia en un nivel de servicio. Si está ejecutando su proceso Node.js en una máquina virtual Linux de larga ejecución, como las máquinas virtuales de Azure, está en un buen lugar para ejecutar Node.js con systemd. Si solo está implementando sus archivos en un servicio como Azure AppService o Heroku o ejecutándose dentro de un entorno en contenedores como Azure Container Instances, probablemente debería evitar esta opción.

Ejecutando su aplicación Node.js con Systemd - Parte 1

Ha escrito la próxima gran aplicación, en Node, y está listo para lanzarla al mundo. Lo que significa que puedes… nodesource.com

Hilos de trabajo de Node.js

Tierney also wants you to know that Worker Threads are coming in Node. This will allow you to start your app on multiple “workers” (threads) thusly negating the need for something like pm2. Maybe. I don’t know. I didn’t really read the article.

Node.js v11.14.0 Documentation

The worker_threads module enables the use of threads that execute JavaScript in parallel. To access it: const worker =…nodejs.org

Be an Adult

Tierney’s last suggestion was to just handle the error and write some tests like an adult. But who has time for that?

The tiny circle abides

Now you know most of what is in the tiny blue circle. The rest is just useless facts about emo bands and beer.

For more information on pm2, Node and Azure, check out the following resources…

  • //pm2.keymetrics.io/
  • Node.js deployment on VS Code
  • Deploy a simple Node site to Azure