使用 PM2 部署 Nodejs 项目

2016年12月4日

当代 Nodejs 项目开发完毕,一般前端和后台都是分离的,所以部署项目的时候就会涉及同时启动和管理多个进程(比如前台启动一个进程,运行在8001端口,后台代码另外启动一个进程,运行在8002端口)的工作。解决这个问题,以前我用 tmux 多一些,但是本期推荐的是另外一个工具,叫做 PM2

PM2 ( Process Manager ,进程管理器)可以帮我们很方便的启动多个进程,进程崩溃自动重启,也可以帮我们创建系统启动脚本,这样,机器重启了,我们的项目也可以自动重启。

注意:本期的内容适合部署《 React 手牵手》 中的案例项目。

服务器运行环境以及需要的软件包

  • Ubuntu v14.04

  • Node.js v5.4.1 版本以上

  • Nginx v1.4.6

  • PM2 v2.1.5

安装 Nodejs 可以参考 《 Nodejs 乐高》 。 Ubuntu 命令行使用可以参考 《驾驭命令行怪兽》 。上面的包版本没有硬性要求,只要相近的版本应该操作都类似。

安装 PM2 包

登录到服务器,安装 PM2 包:

npm install -g pm2

前端准备工作

前端 index.html 代码如下:

var express = require('express');
var app = express();
var axios = require('axios');

app.get('/', function(req, res) {
  axios.get('http://localhost:8002/content').then(function(response){
    res.send(response.data.content);
  });
})

app.listen(8001, function() {
  console.log('express server is running on port 8001');
});

代码上传到服务器,一般都是采用 github 的形式,但是作为 demo ,我这里直接使用 scp 命令了

scp -r pm2-demo haoqicat.com

准备工作完成后,这时到服务器上运行 node index.js 命令就能让前端代码跑起来了。但是当我们退出登录服务器的时候,node server.js 命令会终止运行。要解决这个问题,就要用到 PM2 。

后端准备工作

后端 index.js 内容如下:

var express = require('express');
var app = express();
var cors = require('cors');

app.use(cors());

app.get('/content', function(req, res) {
  res.json({ content: 'Content From Server API' });
})

app.listen(8002, function() {
  console.log('express server is running on port 8002');
});

进入后端代码目录,安装 npm 包

cd server
npm install

这时运行 node index.js 命令,后端 API 服务就生效了。同样的问题,node index.js 不能长久运行,因此用 PM2 启动后端代码。

配置 JSON 文件,PM2 启动多个应用

现在,我们有前端和后端两个 Node.js 应用需要启动,所以在 Home 目录下,新建一个 app.json 文件,内容如下:

{
  "apps": [{
    "name": "api",
    "script": "server/index.js"
  },{
    "name": "front-end",
    "script": "client/index.js"
  }]
}

这个 app.json 文件将作为参数传递给 pm2 start 命令,如下:

pm2 start app.json

这样,前端和后端两个应用就都运行起来了。

Nginx

配置一个域名指向当前服务器,然后到 /etc/nginx/sites-enabled 目录下, 删除一个默认文件:

sudo rm default

然后,新建一个 client.conf 配置文件对应前端展示页面的网址:

cd /etc/nginx/sites-enabled
sudo touch client.conf

这个 client.conf 文件的内容如下:

server {
    listen     80;
    server_name clientpm2.haoqicat.com;

    location / {
        proxy_pass http://localhost:8001;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $http_x_forwarded_host;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_read_timeout 3m;
        proxy_send_timeout 3m;
    }
}

在同一目录下,再添加对应后端 API 地址的配置文件 server.conf,文件内容如下:

server {
    listen     80;
    server_name serverpm2.haoqicat.com;

    location / {
        proxy_pass http://localhost:8002;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $http_x_forwarded_host;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_read_timeout 3m;
        proxy_send_timeout 3m;
    }
}

修改 Nginx 的配置文件之后,需要执行下面命令,配置信息才能生效:

sudo service nginx reload

添加 pm2 启动脚本

上面的步骤做完,pm2 已经成功为我们管理了2个不同的前后端进程,网站已经可以正确访问了。 但是现在的问题是,如果 ubuntu 系统重启了,那么我们的应用就会死掉。要解决这个问题,可以用添加 pm2 启动脚本的形式。

让 PM2 根据系统信息,自动生成启动脚本,先执行命令:

pm2 startup

当上面命令运行之后,会自动打印出类似如下一行命令信息,xxx 会被您自己的登录用户名代替,直接复制这行命令,在命令行中粘贴运行就可以。

sudo su -c "env PATH=$PATH:/home/xxx/.nvm/v6.3.1/bin pm2 startup linux -u ubuntu --hp /home/xxx"

当上面命令执行之后,在系统 /etc/init.d/ 目录下,出现一个 pm2-init.sh 文件,即为 PM2 的启动脚本,当服务器重启的时候,会运行 pm2-init.sh 文件,从而启动 PM2。

详细文档参考 启动脚本

测试重启功能

kill -9 来杀死一个进程,会发现根本杀不死。因为 pm2 的一个优点就是:一旦发现我们的应用进程死了,会自动帮我们重启。

另外,重启我们的 ubuntu 机器,因为前面 PM2 已经为我们创建了启动脚本,所以项目也可以自动重启。