Ubuntu部署Parse Server (关键: 安装Nginx)

前提条件:
Ubuntu Server 16.04.1 LTS 64 服务器
一个域名

  1. 登陆Ubuntu 服务器,除了root或者安装的用户之外,创建一个管理员用户:
//创建用户
sudo adduser xxxxxx

//授权管理员权限
sudo usermod -aG sudo xxxxxx

参考连接:Initial Server Setup with Ubuntu 16.04

  1. 安装Node和npm
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -

sudo apt-get install -y nodejs

//安装完毕后可以用下面的命令查看node和npm的版本
node --version
npm --version

参考连接:Installing Node.js via package manager

  1. 安装mongo db
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 2930ADAE8CAF5059EE73BB4B58712A2291FA4AD5
echo "deb [ arch=amd64,arm64 ] 

https://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.6 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.6.list

sudo apt-get update

sudo apt-get install -y mongodb-org

参考连接:Install MongoDB Community Edition on Ubuntu

系统重启时,自动启动mongodb

sudo systemctl enable mongod
  1. 安装Nginx
sudo apt-get update
sudo apt-get install nginx

//安装完毕之后,Nginx会自动启动
//可以在浏览器查看ip,得到的应该如下:
//不知道ip的话,可以用下面的命令得到
ip addr show eth0 | grep inet | awk '{ print $2; }' | sed 's/\/.*$//'
image.png

参考连接:How To Install Nginx on Ubuntu 14.04 LTS

  1. 设置Let’s Encrypt支持https
    5.1 添加云解析:
    在购买的云服务器的控制台,找到云解析,找到购买的域名,添加两条解析:
    — 添加一条A record,将域名”example.com”指向Ubuntu服务器的公网IP
    — 添加一条A record,将域名”www.example.com“指向Ubuntu服务器的公网IP
    5.2 安装Certbot
sudo add-apt-repository ppa:certbot/certbot

sudo apt-get update

sudo apt-get install python-certbot-nginx

配置Nginx

sudo nano /etc/nginx/sites-available/default

将server_name _;这一行的内容修改为对应的域名,例如:

server_name example.com www.example.com;

//检查修改内容是否正确
sudo nginx -t

//重启nginx
sudo service nginx reload

获取SSL证书

//记得修改对应的域名
sudo certbot --nginx -d example.com -d www.example.com

自动续约SSL证书

sudo certbot renew --dry-run

参考连接:How To Secure Nginx with Let’s Encrypt on Ubuntu 14.04

配置Mongo DB支持SSL连接

sudo cat /etc/letsencrypt/archive/domain_name/{fullchain1.pem,privkey1.pem} | sudo tee /etc/ssl/mongo.pem

//确保mongo.pem 只可以被 mongodb 访问
sudo chown mongodb:mongodb /etc/ssl/mongo.pem
sudo chmod 600 /etc/ssl/mongo.pem

修改mongo配置文件

sudo nano /etc/mongod.conf

//修改内容如下:
# network interfaces
net:
  port: 27017
  bindIp: 0.0.0.0
  ssl:
    mode: requireSSL
    PEMKeyFile: /etc/ssl/mongo.pem

# security
security:
  authorization: enabled

setParameter:
  failIndexKeyTooLong: false

保存配置文件

添加一个新的DB管理员用户

//连接mongo db
mongo --port 27017

//创建一个管理员用户(请修改为自己的用户名和密码)
use admin
db.createUser({
  user: "sammy",
  pwd: "password",
  roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
})
exit

重启mongo db

sudo service mongod restart

以后可以这样连接mongo db

//然后需要输入密码
mongo --port 27017 --ssl --sslAllowInvalidCertificates --authenticationDatabase admin --username sammy --password

创建一个database和对应的db用户

use database_name
db.createUser({ user: "database_user", pwd: "password", roles: [ "readWrite", "dbAdmin" ] })

//注意,上面的”sammy”是DB登陆用户,而这个”database_user”才可以对这个database可以操作。我们后面连接数据库时,需要用到这个”database_user”和对应的密码以及数据库。

例如:

mongodb://database_user:password@your_domain_name:27017/database_name?ssl=true

安装Parse Server App Sample

git clone https://github.com/ParsePlatform/parse-server-example.git

cd ~/parse-server-example

npm install

测试Sample Application

npm start

结果如下:
> parse-server-example@1.0.0 start /home/sammy/parse-server-example
> node index.js

DATABASE_URI not specified, falling back to localhost.
parse-server-example running on port 1337.

另外开启一个session连接服务器,用如下测试

curl -X POST \
  -H "X-Parse-Application-Id: myAppId" \
  -H "Content-Type: application/json" \
  -d '{"score":1337,"playerName":"Sammy","cheatMode":false}' \
  http://localhost:1337/parse/classes/GameScore

Output
{"objectId":"fu7t4oWLuW","createdAt":"2016-02-02T18:43:00.659Z"}

curl -H "X-Parse-Application-Id: myAppId" http://localhost:1337/parse/classes/GameScore

Output
{"results":[{"objectId":"GWuEydYCcd","score":1337,"playerName":"Sammy","cheatMode":false,"updatedAt":"2016-02-02T04:04:29.497Z","createdAt":"2016-02-02T04:04:29.497Z"}]}

curl -X POST \
  -H "X-Parse-Application-Id: myAppId" \
  -H "Content-Type: application/json" \
  -d '{}' \
  http://localhost:1337/parse/functions/hello

Output
{"result":"Hi"}

回到原来的Session,Ctrl-C终止Parse Application的运行。

安装dashboard

cd ~

npm install -g parse-dashboard

//将dashboard加入package中
cd ~/parse-server-example

nano package.json
"dependencies"中添加一行
parse-dashboard": "1.1.2" 

npm install

配置新的Application,包括dashboard

nano my_app.js

内容如下:

// Packtor Server
var express = require('express');
var ParseServer = require('parse-server').ParseServer;
var ParseDashboard = require('parse-dashboard');
var path = require('path');

var databaseUri = process.env.DATABASE_URI || 'mongodb://database_user:password@your_domain_name:27017/database_name?ssl=tru';

if (!databaseUri) {
  console.log('DATABASE_URI not specified, falling back to localhost.');
}

// Set up parse server
var api = new ParseServer({
  databaseURI: databaseUri || 'mongodb://database_user:password@your_domain_name:27017/database_name?ssl=tru',
  cloud: process.env.CLOUD_CODE_MAIN || __dirname + '/cloud/main.js',
  appId: process.env.APP_ID || 'appId',
  masterKey: process.env.MASTER_KEY || 'masterKey',
  serverURL: process.env.SERVER_URL || 'http://localhost:1337/parse',
  publicServerURL: 'https://your_domain_name/parse'
});

var app = express();

// Serve static assets from the /public folder
app.use('/public', express.static(path.join(__dirname, '/public')));

// Serve the Parse API on the /parse URL prefix
var mountPath = process.env.PARSE_MOUNT || '/parse';
app.use(mountPath, api);

// Parse Server plays nicely with the rest of your web routes
app.get('/', function(req, res) {
  res.status(200).send('Parse Server App');
});

var port = process.env.PORT || 1337;
var httpServer = require('http').createServer(app);
httpServer.listen(port, function() {
    console.log('Parse-server running on port ' + port + '.');
});

// Set up parse dashboard
var dashboard = new ParseDashboard({
  "apps": [{
      "serverURL": 'https://your_domain_name/parse', // Not localhost
      "appId": 'appId',
      "masterKey": 'masterKey',
      "appName": "appName",
      "production": false,
      "iconName": "app-icon.png",
  }],
  "users": [
    {
      "user":"user",
      "pass":"password"
    }
  ],
  "iconsFolder": "icons"
});

var dashApp = express();

// make the Parse Dashboard available at /dashboard
dashApp.use('/dashboard', dashboard);

// Parse Server plays nicely with the rest of your web routes
dashApp.get('/', function(req, res) {
  res.status(200).send('Parse Dashboard App');
});

var httpServerDash = require('http').createServer(dashApp);
httpServerDash.listen(4040, function() {
    console.log('dashboard-server running on port 4040.');
});

运行这个Application,可以用如下命令:

node my_app.js

结果应该如下:
Iconsfolder at path: icons not found!
Parse-server running on port 1337.
dashboard-server running on port 4040.

配置Nignx

Ctrl+C终止Application运行

sudo nano /etc/nginx/sites-available/default

在location / {
                # First attempt to serve request as file, then
                # as directory, then fall back to displaying a 404.
                try_files $uri $uri/ =404;
        }
之后, 添加如下内容
        location /parse/ {
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-NginX-Proxy true;
                proxy_pass http://localhost:1337/parse/;
                proxy_ssl_session_reuse off;
                proxy_set_header Host $http_host;
                proxy_redirect off;
        }

        # Pass requests for /dashboard/ to Parse Server instance at localhost:4040
        location /dashboard/ {
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-NginX-Proxy true;
            proxy_pass http://localhost:4040/dashboard/;
            proxy_ssl_session_reuse off;
            proxy_set_header Host $http_host;
            proxy_redirect off;
        }

保存,退出,并重启Nginx

sudo nginx -t

sudo service nginx reload

再次运行Application

node my_app.js

此时,访问https://www.iosreader.com/dashboard/将会跳转到dashboard的login界面,可以用定义在配置文件里的用户名(user)和密码(password)进行登陆

安装PM2

Ctrl+c退出Application

//返回用户目录
cd ~
sudo npm install -g pm2

/*
配置ecosystem.json

nano ecosystem.json
{
  "apps" : [{
    "name"        : "parse-wrapper",
    "script"      : "/home/xxxxxx/parse-server-example/my_app.js",
    "watch"       : true,
    "merge_logs"  : true,
    "cwd"         : "/home/xxxxxx/parse-server-example",
    "env": {
      "PARSE_SERVER_CLOUD_CODE_MAIN": "/home/xxxxxx/parse-server-example/cloud/main.js",
      "PARSE_SERVER_DATABASE_URI": "mongodb://database_user:password@your_domain_name:27017/database_name?ssl=tru';
e",
      "PARSE_SERVER_APPLICATION_ID": "appId",
      "PARSE_SERVER_MASTER_KEY": "masterKey",
    }
  }]
}

*/

运行PM2

//pm2 start ecosystem.json
修改为pm2 start my_app.js 
不知道为何,配置ecosystem.json并且通过该文件启动my_app.js,在服务器重启之后,parse server的连接会出错,暂时不清楚问题的原因,以及如何修改。但是可以通过直接启动my_app.js,此方法在服务器重启之后,一切服务正常。

Sample Output
...
[PM2] Spawning PM2 daemon
[PM2] PM2 Successfully daemonized
[PM2] Process launched
┌───────────────┬────┬──────┬──────┬────────┬─────────┬────────┬─────────────┬──────────┐
│ App name      │ id │ mode │ pid  │ status │ restart │ uptime │ memory      │ watching │
├───────────────┼────┼──────┼──────┼────────┼─────────┼────────┼─────────────┼──────────┤
│ parse-wrapper │ 0  │ fork │ 3499 │ online │ 00s     │ 13.680 MB   │  enabled │
└───────────────┴────┴──────┴──────┴────────┴─────────┴────────┴─────────────┴──────────┘
 Use `pm2 show <id|name>` to get more details about an app

保存

pm2 save

Sample Output
[PM2] Dumping processes
sudo pm2 startup ubuntu -u xxxxxx --hp /home/xxxxxx/

republished from: https://www.jianshu.com/p/fde2039344ab

Run a node.js app as a background service

Make a myapp.service file (replacing ‘myapp’ with your app’s name, obviously):

[Unit]
Description=My app

[Service]
ExecStart=/var/www/myapp/app.js
Restart=always
User=nobody
# Note RHEL/Fedora uses 'nobody', Debian/Ubuntu uses 'nogroup'
Group=nobody  
Environment=PATH=/usr/bin:/usr/local/bin
Environment=NODE_ENV=production
WorkingDirectory=/var/www/myapp

[Install]
WantedBy=multi-user.target

Note if you’re new to Unix: /var/www/myapp/app.js should have #!/usr/bin/env node on the very first line.

Copy your service file into the /etc/systemd/system.

Start it with systemctl start myapp.

Enable it to run on boot with systemctl enable myapp.

See logs with journalctl -u myapp

This is taken from How we deploy node apps on Linux, 2018 edition, which also includes commands to generate an AWS/DigitalOcean/Azure CloudConfig to build Linux/node servers (including the .service file).