Tuesday, October 1, 2024

Master PM2: Ultimate Guide to Node.js Process Management & Optimization

Process Management is a very important aspect in any application configuration. There are several process management libraries. PM2 is one of them that is widely used. Here, I am showing how to use it in Windows system along with its different commands.

IMPORTANT NOTE: You can get full detail in my below Youtube video. Here is the link: https://youtu.be/mwGV0thGBfI?si=mC7hdFg--qmaA-XT


Benefits of Process Management libraries:

  • Automatic Restart: If your application crashes, PM2 automatically restarts it, ensuring minimal downtime.
  • Load balancing: PM2 can run your application in cluster mode, leveraging multi-core processors for better performance and scalability.
  • Process monitoring: It tracks resource consumption (CPU, memory) and provides insights through monitoring tools.
  • Log Management: PM2 aggregates logs from multiple instances of your application, providing centralized logging and an easy way to debug issues. It also allows you to view real-time logs and can be configured to send logs to external log services.
  • Performance Optimization: PM2 allows you to run multiple instances of your application (in cluster mode), distributing the load across multiple CPU cores, improving application throughput.
  • Environment Management: PM2 supports different environments (development, production) and allows you to define different configurations for each environment.
  • Memory Management: PM2 provides options to set memory limits for your applications. When memory consumption exceeds the limit, it will automatically restart the app, preventing memory leaks from causing issues.
  • Real-time Monitoring Dashboard: PM2 offers a real-time web dashboard to monitor application status, CPU, memory, and other metrics. This helps in identifying performance bottlenecks or issues in real time.
PM2 Installation: We can use npm command to install PM2 at global mode by using below command:
                    npm install pm2@latest -g

Start application using PM2: We can use below command to start our node js application named as Test1
 pm2 start C:\Personal\Jitendra\workspace\youtube\youtubeCode\app.js --name Test1


Similarly we can another node js application using PM2 on different port like:
pm2 start C:\Personal\Jitendra\workspace\youtube\youtubeCode2\app.js --name Test2

List all applications: We can list down all applications along with their different instances running under PM2.

pm2 list


Monitor application: We can use "pm2 monit" command to open a monitor window to get the real time metrics of the applications running in PM2



Watch logs: We can use below command to get the real time logs of our applications running under pm2


Stop applications: We can use below commands to stop applications running under PM2
                                pm2 stop Test1 - stops only application named as Test1
                                pm2 stop all - stops all applications running under PM2


Passing Environment variables: We can pass some secure data to our application at startup time in PM2 through environment variables using below command:

set PORT=3005 && pm2 start C:\Personal\Jitendra\workspace\youtube\youtubeCode\app.js --name Test1

Here, I am passing PORT data as an environment variable at the startup time. 

Ecosystem: we can add multiple sub-commands here for different purposes like instance handling, log handling, load balancing and thus the start command will become very complex. So, to solve this problem we can use ecosystem config file. We can automatically generate the file named as "ecosystem.config.js" using command like "pm2 ecosystem" and then we can modify the newly created file as per the application requirement:

module.exports = {
  apps : [{
    name: 'Test2',
    script: './app.js',
    env: {
      NODE_ENV: 'development',
      PORT: 3006
    },
    watch: '.'
  }],

  deploy : {
    production : {
      user : 'SSH_USERNAME',
      host : 'SSH_HOSTMACHINE',
      ref  : 'origin/master',
      repo : 'GIT_REPOSITORY',
      path : 'DESTINATION_PATH',
      'pre-deploy-local': '',
      'post-deploy' : 'npm install && pm2 reload ecosystem.config.js --env production',
      'pre-setup': ''
    }
  }
};


In this file we are passing the port and Node Env as environment variables. Similarly we can pass other variables and config parameters.
To start the application using ecosystem.config.js, we need to use command like "pm2 start ecosystem.config.js"


Similarly, if we want to run another application by using ecosystem.config.js file, then we have to create a separate ecosystem.config.js file and run the command "pm2 start ecosystem.config.js" with respect to this file.

But, what if we have 100 microservices, then we have to create such 100 files for ecosystem.config.js and thus it becomes unmanageable.

To solve this issue, we can create a single ecosystem.config.js file and we can define all the required metrics for each of the microservices in this single file and when we start it through PM2 then all microservices will start. The example of such ecosystem.config.js file is as follows:

module.exports = {
    apps: [
        {
            name: 'Test1',
            script: '../youtube/youtubeCode/app.js',
            instances: 2,
            max_memory_restart: "300M",
            // Logging
            out_file: "./test1.out.log",
            error_file: "./test1.error.log",
            merge_logs: true,
            log_date_format: "DD-MM HH:mm:ss Z",
            log_type: "json",
            watch: true,
            ignore_watch: [
                "../youtube/youtubeCode/node_modules",
                "../youtube/youtubeCode/package.json"
            ],
            // Env Specific Config          
            env: {
                NODE_ENV: 'development',
                PORT: 3005
            }
        },
        {
            name: 'Test2',
            script: '../youtube/youtubeCode2/app.js',
            instances: "1",
            exec_mode: "cluster",
            out_file: "./test2.out.log",
            error_file: "./test2.error.log",
            watch: true,
            ignore_watch: [
                "../youtube/youtubeCode2/node_modules",
                "../youtube/youtubeCode2/package.json"
            ],
            env: {
                NODE_ENV: 'development',
                PORT: 3006
            }
        }
    ],

    deploy: {
        production: {
            user: 'SSH_USERNAME',
            host: 'SSH_HOSTMACHINE',
            ref: 'origin/master',
            repo: 'GIT_REPOSITORY',
            path: 'DESTINATION_PATH',
            'pre-deploy-local': '',
            'post-deploy': 'npm install && pm2 reload ecosystem.config.js --env production',
            'pre-setup': ''
        }
    }
};


Please ping in the comment section if you have any question or suggestion.