Δεν πρέπει ποτέ να τρέχετε απευθείας εναντίον του Node.js στην παραγωγή. Μπορεί.

Μερικές φορές αναρωτιέμαι αν γνωρίζω τίποτα καθόλου.

Πριν από λίγες εβδομάδες, μιλούσα με έναν φίλο που ανέφερε ότι δεν θα εκτελούσατε μια εφαρμογή απευθείας κατά του Node στην παραγωγή.

Κούνησα έντονα για να σηματοδοτήσω ότι επίσης δεν θα έτρεχα ποτέ εναντίον του Node στην παραγωγή γιατί… χαχαχα… όλοι το γνωρίζουν αυτό. Αλλά δεν το ήξερα! Πρέπει να το ήξερα; ΜΠΟΡΩ ΝΑ ΕΠΙΤΡΕΠΟΜΕΝΟ ΠΡΟΓΡΑΜΜΑ;

Αν επρόκειτο να σχεδιάσω ένα διάγραμμα Venn για ό, τι ξέρω έναντι τι νιώθω όπως όλοι γνωρίζουν, θα μοιάζει με αυτό…

Παρεμπιπτόντως, αυτή η μικροσκοπική κουκίδα γίνεται μικρότερη όσο μεγαλώνω.

Υπάρχει ένα καλύτερο διάγραμμα που δημιουργήθηκε από την Alicia Liu που άλλαξε τη ζωή μου. Λέει ότι είναι περισσότερο έτσι ...

Μου αρέσει πολύ αυτό το διάγραμμα γιατί θέλω να είναι αλήθεια. Δεν θέλω να περάσω το υπόλοιπο της ζωής μου ως μια μικρή, συρρικνωμένη μπλε κουκίδα ασήμαντου.

ΤΟ ΔΡΑΜΑΤΙΚΟ. Κατηγορήστε την Πανδώρα. Δεν ελέγχω τι θα παίξει στη συνέχεια, ενώ γράφω αυτό το άρθρο και το Dashboard Confessional είναι ένα φάρμακο Helluva.

Λοιπόν, υποθέτοντας ότι το διάγραμμα της Alicia είναι αλήθεια, θα ήθελα να μοιραστώ μαζί σας όσα γνωρίζω τώρα σχετικά με την εκτέλεση εφαρμογών Node στην παραγωγή. Ίσως τα σχετικά διαγράμματά μας Venn να μην αλληλεπικαλύπτονται σε αυτό το θέμα.

Πρώτα απ 'όλα, ας ασχοληθούμε με τη δήλωση "μην εκτελείτε εφαρμογές απευθείας κατά του κόμβου στην παραγωγή".

Ποτέ μην τρέχετε απευθείας εναντίον του Node στην παραγωγή

Μπορεί. Αλλά ίσως όχι. Ας μιλήσουμε για το σκεπτικό πίσω από αυτήν τη δήλωση. Πρώτα, ας δούμε γιατί όχι.

Ας πούμε ότι έχουμε έναν απλό διακομιστή Express. Ο απλούστερος διακομιστής Express που μπορώ να σκεφτώ…

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

Θα το τρέξαμε με ένα σενάριο έναρξης στο package.jsonαρχείο.

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

Υπάρχουν δύο προβλήματα εδώ. Το πρώτο είναι ένα αναπτυξιακό πρόβλημα και το δεύτερο είναι ένα πρόβλημα παραγωγής.

Το πρόβλημα ανάπτυξης είναι ότι όταν αλλάζουμε τον κωδικό, πρέπει να σταματήσουμε και να ξεκινήσουμε την εφαρμογή για να πάρουμε τις αλλαγές μας.

Για να το λύσουμε αυτό, συνήθως χρησιμοποιούμε κάποιο είδος διαχειριστή διεργασιών Node όπως supervisorή nodemon. Αυτά τα πακέτα θα παρακολουθήσουν το έργο μας και θα επανεκκινήσουν τον διακομιστή μας όποτε κάνουμε αλλαγές. Συνήθως το κάνω έτσι…

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

Τότε τρέχω npm run dev. Σημειώστε ότι τρέχω npx supervisorεδώ που μου επιτρέπει να χρησιμοποιώ το supervisorπακέτο χωρίς να χρειάζεται να το εγκαταστήσω. ❤️ 2019. Κυρίως.

Το άλλο μας πρόβλημα είναι ότι εξακολουθούμε να τρέχουμε απευθείας εναντίον του Node και έχουμε ήδη πει ότι ήταν κακό και τώρα πρόκειται να μάθουμε γιατί.

Πρόκειται να προσθέσω μια άλλη διαδρομή εδώ που προσπαθεί να διαβάσει ένα αρχείο από δίσκο που δεν υπάρχει. Αυτό είναι ένα σφάλμα που θα μπορούσε εύκολα να εμφανιστεί σε οποιαδήποτε πραγματική εφαρμογή.

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

Εάν το εκτελέσουμε απευθείας στο Node με npm startκαι πλοηγηθούμε στο read τελικό σημείο, λαμβάνουμε ένα σφάλμα επειδή αυτό το αρχείο δεν υπάρχει.

Ποιο - όχι μεγάλη υπόθεση, σωστά; Είναι ένα λάθος. Συμβαίνει.

ΟΧΙ. Μεγάλο θέμα. Εάν επιστρέψετε στο τερματικό σας, θα δείτε ότι η εφαρμογή είναι εντελώς εκτός λειτουργίας.

Αυτό σημαίνει ότι αν επιστρέψετε στο πρόγραμμα περιήγησης και προσπαθήσετε να μεταβείτε στη ριζική διεύθυνση URL του ιστότοπου, λαμβάνετε την ίδια σελίδα σφάλματος. Ένα σφάλμα σε μία μέθοδο έβγαλε την εφαρμογή για όλους .

Αυτό είναι κακό. Όπως πολύ κακό. Αυτός είναι ένας από τους κύριους λόγους για τους οποίους οι άνθρωποι λένε ότι «δεν τρέχετε ποτέ ενάντια στον κόμβο στην παραγωγή» .

ΕΝΤΑΞΕΙ. Αν λοιπόν δεν μπορούμε να τρέξουμε τον Node στην παραγωγή, ποιος είναι ο σωστός τρόπος εκτέλεσης του Node στην παραγωγή;

Επιλογές για παραγωγή Κόμβος

Έχουμε μερικές επιλογές.

Ένα από αυτά θα ήταν απλώς να χρησιμοποιήσουμε κάτι σαν supervisorή nodemonστην παραγωγή με τον ίδιο τρόπο που τα χρησιμοποιούμε στο dev. Αυτό θα λειτουργούσε, αλλά αυτά τα εργαλεία είναι λίγο από την ελαφριά πλευρά. Μια καλύτερη επιλογή είναι κάτι που ονομάζεται pm2.

pm2 η διάσωση

Το pm2 είναι ένας διαχειριστής διεργασιών κόμβου που έχει πολλές καμπάνες και σφυρίχτρες. Όπως όλα τα άλλα "JavaScript", το εγκαθιστάτε (σε παγκόσμιο επίπεδο) από npm- ή μπορείτε απλώς να το χρησιμοποιήσετε npxξανά. Δεν θέλω να σου πω πώς να ζήσεις τη ζωή σου.

Υπάρχουν πολλοί τρόποι για να εκτελέσετε την εφαρμογή σας με το pm2. Ο απλούστερος τρόπος είναι να καλέσετε μόνο pm2 startτο σημείο εισόδου σας.

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

Και θα δείτε κάτι τέτοιο στο τερματικό…

That’s our process running in the background monitored by pm2. If you visit the read endpoint and crash the application, pm2 will automatically restart it. You won’t see any of that in the terminal because it’s running in the background. If you want to watch pm2 do its thing, you gotta run pm2 log 0. The 0 is the ID of the process we want to see logs for.

There we go! You can see pm2 restart the application when it goes down because of our unhandled error.

We can also pull out our dev command and have pm2 watch files for us and restart on any changes.

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

Note that because pm2 runs things in the background, you can’t just ctrl+c your way out of a running pm2 process. You have to stop it by passing the ID or the name.

pm2 stop 0

pm2 stop index

Also, note that pm2 retains a reference to the process so you can restart it.

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…

Αυτή η επιλογή είναι δυνατή μόνο εάν έχετε πρόσβαση στο Linux κατά την ανάπτυξή σας και ελέγχετε τον τρόπο εκκίνησης του Node σε επίπεδο υπηρεσίας. Εάν εκτελείτε τη διαδικασία Node.js σε μακροχρόνιο Linux VM, όπως το Azure VMs, είστε σε καλό μέρος για να εκτελέσετε το Node.js με το systemd. Εάν απλώς αναπτύσσετε τα αρχεία σας σε μια υπηρεσία όπως το Azure AppService ή το Heroku ή εκτελείτε μέσα σε περιβάλλον με κοντέινερ όπως το Azure Container Instances, πιθανότατα πρέπει να αποφύγετε αυτήν την επιλογή.

Εκτέλεση της εφαρμογής Node.js με το Systemd - Μέρος 1

Έχετε γράψει την επόμενη υπέροχη εφαρμογή, στο Node, και είστε έτοιμοι να την απελευθερώσετε στον κόσμο. Αυτό σημαίνει ότι μπορείτε… nodesource.com

Νήματα εργαζομένων Node.js

Η Tierney θέλει επίσης να γνωρίζετε ότι τα Worker Threads έρχονται στο Node. Αυτό θα σας επιτρέψει να ξεκινήσετε την εφαρμογή σας σε πολλούς «εργαζόμενους» (νήματα), έτσι ώστε να αναιρείται η ανάγκη για κάτι σαν pm2. Μπορεί. Δεν γνωρίζω. Δεν διάβασα πραγματικά το άρθρο.

Node.js v11.14.0 Τεκμηρίωση

Η ενότητα worker_threads επιτρέπει τη χρήση νημάτων που εκτελούν παράλληλα τη JavaScript. Για πρόσβαση σε αυτό: const worker =… nodejs.org

Γίνετε ενήλικας

Η τελευταία πρόταση του Tierney ήταν απλώς να χειριστεί το σφάλμα και να γράψει κάποιες εξετάσεις όπως ένας ενήλικας. Αλλά ποιος έχει χρόνο για αυτό;

Ο μικροσκοπικός κύκλος ακολουθεί

Τώρα ξέρετε το μεγαλύτερο μέρος του μικροσκοπικού μπλε κύκλου. Τα υπόλοιπα είναι απλώς άχρηστα γεγονότα για μπάντες emo και μπύρα.

Για περισσότερες πληροφορίες σχετικά με τα pm2, Node και Azure, ρίξτε μια ματιά στους παρακάτω πόρους…

  • //pm2.keymetrics.io/
  • Ανάπτυξη Node.js στον κώδικα VS
  • Αναπτύξτε έναν απλό ιστότοπο Node στο Azure