Du bør aldrig køre direkte mod Node.js i produktion. Måske.

Nogle gange spekulerer jeg på, om jeg overhovedet ved meget om noget.

For bare et par uger siden talte jeg med en ven, der nævnte off-hand, "du ville aldrig køre en applikation direkte mod Node i produktion".

Jeg nikkede kraftigt til signal om, at jeg også ville aldrig nogensinde køre mod Node i produktion, fordi ... hahaha ... .everyone ved det. Men det vidste jeg ikke! Skulle jeg have vidst det?! ?? FÅR jeg stadig tilladelse til at programmere?

Hvis jeg skulle tegne et Venn-diagram over hvad jeg ved vs hvad jeg har lyst til som alle andre ved, ville det se sådan ud ...

Forresten bliver den lille prik mindre, jo ældre jeg bliver.

Der er et bedre diagram oprettet af Alicia Liu, som ændrede mit liv. Hun siger, at det er mere sådan ...

Jeg elsker dette diagram så meget, fordi jeg vil have det til at være sandt. Jeg ønsker ikke at tilbringe resten af ​​mit liv som en lille, krympende blå prik af ubetydelighed.

SÅ DRAMATISK. Beklager Pandora. Jeg kontrollerer ikke, hvad der bliver spillet næste, mens jeg skriver denne artikel, og Dashboard Confessional er et helvedes stof.

Hvis jeg antager, at Alicias diagram er sandt, vil jeg gerne dele med dig, hvad jeg nu ved om at køre Node-apps i produktion. Måske overlapper vores relative Venn-diagrammer ikke dette emne.

Lad os først og fremmest tage stilling til ”kør aldrig apps direkte mod Node i produktion”.

Kør aldrig direkte mod Node i produktionen

Måske. Men måske ikke. Lad os tale om ræsonnementet bag denne erklæring. Lad os først se på, hvorfor ikke.

Sig, at vi har en simpel Express-server. Den enkleste Express-server, jeg kan tænke på ...

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}!`));

Vi kører dette med et start-script i package.jsonfilen.

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

Der er slags to problemer her. Det første er et udviklingsproblem, og det andet et produktionsproblem.

Udviklingsproblemet er, at når vi ændrer koden, skal vi stoppe og starte applikationen for at få vores ændringer afhentet.

For at løse det bruger vi normalt en slags Node-procesmanager som supervisoreller nodemon. Disse pakker vil se vores projekt og genstarte vores server, når vi foretager ændringer. Det gør jeg normalt sådan ...

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

Så løber jeg npm run dev. Bemærk, at jeg kører npx supervisorher, hvilket giver mig mulighed for at bruge supervisorpakken uden at skulle installere den. I ❤️ 2019. For det meste.

Vores andet problem er, at vi stadig kører direkte mod Node, og vi sagde allerede, at det var dårligt, og nu skal vi finde ud af hvorfor.

Jeg vil tilføje en anden rute her, der forsøger at læse en fil fra en disk, der ikke findes. Dette er en fejl, der let kan vises i enhver applikation i den virkelige verden.

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}!`));

Hvis vi kører dette direkte mod Node med npm startog navigerer til read slutpunktet, får vi en fejl, fordi filen ikke findes.

Hvilket - ingen big deal, ikke? Det er en fejl. Det sker.

INGEN. Big deal. Hvis du går tilbage til din terminal, vil du se, at applikationen er helt nede.

Hvilket betyder, at hvis du går tilbage til browseren og prøver at gå til webadressens rod-URL, får du den samme fejlside. Én fejl i en metode tog applikationen ned for alle .

Det er slemt. Som virkelig dårlig. Dette er en af ​​hovedårsagerne til, at folk siger "kør aldrig direkte mod Node i produktion" .

OKAY. Så hvis vi ikke kan køre mod Node i produktion, hvad er den rigtige måde at køre Node i produktion på?

Indstillinger for produktionsnode

Vi har et par muligheder.

En af dem ville være at bruge noget lignende supervisoreller nodemoni produktion på samme måde som vi bruger dem i dev. Det ville fungere, men disse værktøjer er lidt på den lette side. En bedre mulighed er noget, der hedder pm2.

pm2 redningen

pm2 er en Node-procesmanager, der har mange klokker og fløjter. Ligesom alt andet “JavaScript” installerer du det (globalt) fra npm- eller du kan bare bruge det npxigen. Jeg vil ikke fortælle dig, hvordan du skal leve dit liv.

Der er mange måder at køre din app med pm2. Den enkleste måde er bare at ringe pm2 starttil dit indgangspunkt.

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

Og du kan se noget lignende i terminalen ...

Det er vores proces, der kører i baggrunden overvåget af pm2. Hvis du besøger readslutpunktet og går ned i applikationen, genstarter pm2 det automatisk. Du kan ikke se noget af det i terminalen, fordi det kører i baggrunden. Hvis du vil se pm2 gøre sine ting, skal du løbe pm2 log 0. Det 0er id'et for den proces, vi vil se logfiler til.

Sådan der! Du kan se pm2 genstarte applikationen, når den går ned på grund af vores uhåndterede fejl.

Vi kan også trække vores dev-kommando ud og have pm2-overvågningsfiler til os og genstarte eventuelle ændringer.

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

Bemærk, at fordi pm2 kører ting i baggrunden, kan du ikke bare komme ctrl+cdig ud af en kørende pm2-proces. Du er nødt til at stoppe det ved at videregive ID eller navnet.

pm2 stop 0

pm2 stop index

Bemærk også, at pm2 bevarer en henvisning til processen, så du kan genstarte den.

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…

Denne mulighed er kun mulig, hvis du har adgang til Linux i din implementering, og du styrer den måde, hvorpå Node startes på et serviceniveau. Hvis du kører din Node.js-proces i en langvarig Linux-VM, som Azure VM'er, er du et godt sted at køre Node.js med systemd. Hvis du bare distribuerer dine filer til en tjeneste som Azure AppService eller Heroku eller kører inde i et containeriseret miljø som Azure Container Instances, bør du sandsynligvis undgå denne mulighed.

Kørsel af din Node.js-app med Systemd - del 1

Du har skrevet den næste fantastiske applikation i Node, og du er klar til at frigøre den over hele verden. Hvilket betyder, at du kan ... nodesource.com

Node.js arbejdstråde

Tierney vil også have dig til at vide, at arbejdertråde kommer i node. Dette giver dig mulighed for at starte din app på flere "arbejdere" (tråde) og dermed nægte behovet for noget som pm2. Måske. Jeg ved ikke. Jeg læste ikke rigtig artiklen.

Node.js v11.14.0 Dokumentation

Modulet worker_threads muliggør brug af tråde, der udfører JavaScript parallelt. For at få adgang til det: const worker = ... nodejs.org

Vær voksen

Tierneys sidste forslag var bare at håndtere fejlen og skrive nogle tests som en voksen. Men hvem har tid til det?

Den lille cirkel forbliver

Nu ved du det meste af, hvad der er i den lille blå cirkel. Resten er bare ubrugelige fakta om emo bands og øl.

For mere information om pm2, Node og Azure, tjek følgende ressourcer ...

  • //pm2.keymetrics.io/
  • Node.js-implementering på VS-kode
  • Implementere et enkelt Node-websted til Azure