NodeJS applications are, by design, single threaded. Well, not in the really, and for good reason, mainly the fact that multi-threaded applications are hard to maintain and debug. Its easier to build an application that has multiple instances of a single thread for scaling, than to do a multithreaded application. Ask anyone who has written one. Obviously, it can be done, and it has its usecases, but not everyone needs to solve those problems.
You can read up this really good article on Medium by Salil Arora (medium.com/better-programming/is-node-js-re..)
Multi-CPU deployment architectures
Assuming, for reasons of architecture of your application, you want to use more worker thread, the current strategies are:
use NodeJS cluster APIs ( sitepoint.com/how-to-create-a-node-js-clust.. )
use PM2 to do the same thing as above, just outside of the application code: pm2.keymetrics.io/docs/usage/cluster-mode
One of the challenges with this approach, is it is limited by the number of CPUs available to the container, and its ability to be resized on demand.
In the world of Containers, the number of vCPUs available to a container can be controlled, as part of the resource configuration. So, more often than not, the host (on which the container) will have more CPUs than the container instance is configured for.
Given that each container instance is just a "process" running on the host OS, this is very convenient. So, each NodeJS container instance can run as a single worker.
So, now the choice is driven by the deployment architecture. The options are:
- Scale up single VMs/Containers and run NodeJS in cluster mode
- Use low cost, small instances, and run multiple of them, with a fronting load balancer or reverse proxy
As you can see in this diagram, both the strategies work more or less the same for single instance, monoliths. For applications that are designed to be stateless and can scale horizontally, the multiple container strategy can quickly turn into an advantage when there is need for scale on demand.
So, PM2 or Docker ?
The advantages of working with PM2: It handles a lot of the process management, with scale out cluster mode, process restarts, log consolidation, etc. The feature list is on their website (pm2.keymetrics.io), so I won't list those out here.
When working with Docker, you get most of those, but are achieved in different ways than PM2 does. The things that pm2 is not able to match are the ability to move up the chain into Kubernetes, if things really go north, and the quantum of third party tools that are available to manage and script the deployment environment. The integrations with Log Collectors (ELK), Container services on all major cloud vendors (eg. AWS Container Service, Azure Container Instances, and the world of Managed Kubernetes ), and you name it, brings in the ability to add so much, without having to build it yourself.
Secrets management, production hardening, CI/CD can be significantly easier when GitHub/GitLab is used with K8s and Docker.
No right answers here. So how do you decide ?
If you are already on pm2, just run with that, but bring it inside the container, to start with. Once you have tools to monitor your containers in place (eg. CloudWatch), then moving towards a single instance per container will add significant value. Plus, if you can go the k8s route, if your application needs frequent upgrades / updates.
If you are starting today, I recommend you give a serious look at Docker based setup, right from day one. The development workflows can be easily integrated too. And the ecosystem is primed with information about the tools and debugging tips, in case you hit a wall.
You may want to read my blog about using Visual Studio Code in Dev Containers to get a sense of how easy it is to keep the development environment and workflows matched up with the production environment.
Do reach out, or leave a comment.
About the Author
Navneet Karnani is a Full Stack Ployglot veteran, and explores technology to build great products, always striving to extract more productivity from the tools he uses.