Node.js是一套开源JavaScript运行时环境,能够轻松构建服务器端与网络应用。这套平台可运行在Linux、OS X、FreeBSD以及Windows系统之上。Node.js应用可通过命令行运行,但今天我们着重关注以服务方式运行,因此其需要在重启或崩溃情况下自动恢复,并可安全应用于生产环境。
在本教程中,我们将探讨如何在单一Ubuntu 16.04服务器上设置能够用于实际生产的Node.js环境。此服务器将运行由PM2负责管理的Node.js应用,且为用户提供经由Nginx反向代理实现的安全访问。Nginx服务器将提供HTTPS,并由Let’s Encrypt提供免费证书。
先决条件
本教程要求大家拥有一套Ubuntu 16.04服务器,且配置一个拥有sudo权限的非root用户。
另外,大家还需要拥有自己的域名,且指向服务器的公共IP地址。
下面在服务器上安装Node.js运行时。
安装Node.js
我们将使用NodeSource软件包归档文件安装最新Node.js版本。
首先,大家需要安装NodeSource PPA以访问其内容。现在前往主目录,使用curl以提取Node.js 6.x归档文件中的安装脚本:
- cd ~
- curl -sL https://deb.nodesource.com/setup_6.x -o nodesource_setup.sh
大家可以使用nano(或者其它文本编辑器)修改此脚本内容:
- nano nodesource_setup.sh
下面使用sudo权限运行此脚本:
- sudo bash nodesource_setup.sh
此PPA将被添加至配置当中,而本地软件包缓存也将自动更新。在从nodesource运行了设置脚本后,大家即可以同样的方式安装Node.js软件包:
- sudo apt-get install nodejs
除了nodejs二进制代码外,nodejs软件包还包含npm,意味着大家无需单独安装npm。不过为了确保某些npm软件包能够正常运行(例如需要对源代码进行编译的情况),大家需要安装build-essential软件包:
- sudo apt-get install build-essential
Node.js运行时已经安装完成,下面开始编写我们的Node.js应用。
请注意:当从NodeSource PPA进行安装时,Node.js可执行文件名为nodejs,而非node。
创建Node.js应用
我们这里编写一个简单的Hello World应用,即向任意HTTP请求返回“Hello World”。
Hello World代码
首先,创建并打开Node.js应用以进行编辑。在本示例中,我们将使用nano对hello.js示例应用进行编辑:
- cd ~
- nano hello.js
在文件中插入以下代码。如果大家愿意,也可以替换其中的8080端口(请确保使用非admin端口,例如1024或更高):
hello.js
#!/usr/bin/env nodejs
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
}).listen(8080, 'localhost');
console.log('Server running at http://localhost:8080/');
保存并退出。
此Node.js应用能够监听特定地址(localhost)与端口(8080),并利用200 HTTP success代码返回“Hello Wor”字样。由于我们监听的是localhost,因此远程客户端无法接入我们的应用。
测试应用
为了进行应用测试,我们将hello.js设为可执行:
- chmod +x ./hello.js
运行方法如下:
- ./hello.js
Output
Server running at http://localhost:8080/
请注意:以这种方式运行Node.js应用会阻断其它命令,直到以Ctrl-C关闭该应用。
为了测试此应用,我们需要在服务器上打开另一终端会话并利用curl接入localhost:
- curl http://localhost:8080
如果大家看到以下输出结果,则该应用已经正常运行并开始监听正确的地址与端口:
Output
Hello World
如果大家看到的输出结果有误,请确保Node.js应用正在运行并监听正确的地址与端口。
测试完成后,按下Ctrl-C关闭应用。
安装PM2
现在安装PM2,这是一款面向Node.js应用的进程管理器。PM2能够轻松管理应用并实现后台化(以服务形式将其运行于后台)。
我们将使用npm安装各Node模块。下面安装PM2:
- sudo npm install -g pm2
其中的-g要求npm将模块安装在本地,从而实现系统内使用。
利用PM2管理应用
易于上手及使用,下面来了解PM2的几种基本用法。
启动应用
首先利用pm2命令在后台运行hello.js应用:
- pm2 start hello.js
这条命令还能够将应用添加至PM2进程列表中,其会在每次应用启动时输出结果:
Output
[PM2] Spawning PM2 daemon
[PM2] PM2 Successfully daemonized
[PM2] Starting hello.js in fork_mode (1 instance)
[PM2] Done.
Use `pm2 show <id|name>` to get more details about an app
如大家所见,PM2能够自动分配一个App名称(基于文件名称,但不包含.js后缀)以及一个PM2 ID。PM2还能够包含其它信息,例如进程的PID、当前状态以及内存使用量。
运行在PM2下的应用可在崩溃或被关闭后自动恢复,但我们还需要另行调整以确保其在系统重启后也能得到恢复。幸运的是,PM2能够非常简单的实现方式,即startup子命令。
这里的startup子命令能够生成并配置一套startup脚本,用于启动PM2并在服务器启动时管理进程。大家还需要指定其运行平台,即ubuntu:
- pm2 startup systemd
输出结果的最后一行中包含一条必须以superuser权限运行的命令:
Output
[PM2] You have to run this command as root. Execute the following command:
sudo su -c "env PATH=$PATH:/usr/bin pm2 startup systemd -u sammy --hp /home/sammy"
运行该命令用于(类似于以上内容,但sammy应被替换为实际用户名)设置PM2以确保其在引导过程中启动:
- sudo su -c "env PATH=$PATH:/usr/bin pm2 startup systemd -u sammy --hp /home/sammy"
这条命令将创建一个systemd单元以确保在引导时运行pm2。该pm2实例运行hello.js。大家可以利用systemctl命令检查该systemd单元的状态:
- systemctl status pm2
其它PM2使用方式(可选)
PM2提供多种子命令,允许大家管理应用并查看相关信息。请注意,直接运行pm2而不加任何参数则会显示帮助页面,其中包括示例使用情况等。
使用以下命令关闭应用(指定PM2应用名称或ID):
- pm2 stop app_name_or_id
以下命令可重启应用:
- pm2 restart app_name_or_id
以下list子命令可列出当前由PM2管理的各应用:
- pm2 list
大家可以利用info子命令查看更多特定应用的相关信息:
- pm2 info example
PM2进程监控器则需要使用monit子命令,其能够显示应用状态、CPU与内存占用量:
- pm2 monit
现在Node.js应用已经开始运行并由PM2管理,接下来探讨反向代理。
设置Nginx作为反向代理服务器
现在我们的应用开始运行,监听localhost,接下来需要设置用户访问机制。我们将利用Nginx Web服务器作为反向代理。在本教程中,我们从零开始进行Nginx设置。如果大家已经拥有自己的Nginx服务器,则可将locationblock复制到选定的serverblock中(确保位置信息不会与现有内容相冲突)。
首先使用apt-get安装Nginx:
- sudo apt-get install nginx
现在打开默认server block配置文件进行编辑:
- sudo nano /etc/nginx/sites-available/default
删除文件内的全部内容并插入以下配置。注意将server_name替换为实际域名。另外,如果大家的应用需要监听其它端口,也记得进行对应调整:
/etc/nginx/sites-available/default
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
假定大家的服务器配备example.com域名,则通过浏览器访问http://example.com/即会向hello.js发送请求,并监听localhost上的端口8080。
大家也可以添加其它location block以在同一服务器上访问其它应用。例如,如果我们在端口8081上运行有另一Node.js应用,则可添加以下location block以通过http://example.com/app2实现访问:
Nginx Configuration — Additional Locations
location /app2 {
proxy_pass http://localhost:8081;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
完成添加之后,保存并退出。
重启Nginx:
- sudo systemctl restart nginx
假定大家的Node.js应用正在运行,应用与Nginx配置也一切正常,则可通过Nginx反向代理实现应用访问。
下面利用Let’s Encrypt保护应用连接。
安装Let’s Encrypt及其关联性
Let’s Encrypt是一款新型证书发布程序,能够轻松帮助我们获取免费的TLS/SSL证书。
大家必须拥有或者控制需要使用证书的注册域名。如果大家还没有注册域名,则可通过各域名注册商进行申请。
另外,请记得为服务器的公共IP地址创建一条A记录,因为Let’s Encrypt会据此进行验证。
在安装Let’s Encrypt前,确保Git与bc软件包已经安装完成:
- sudo apt-get -y install git bc
接下来将letsencrypt库由GitHub克隆至/opt/letsencrypt。其中/opt/目录属于发行版官方软件包库不包含的各软件的标准保存位置:
- sudo git clone https://github.com/letsencrypt/letsencrypt /opt/letsencrypt
将其克隆至letsencrypt目录:
- cd /opt/letsencrypt
获取初始证书
由于nginx目前已经运行在端口80上,而Let’s Encrypt客户端也需要使用该端口以验证域名,所以暂时停止nginx:
- sudo systemctl stop nginx
使用Standalone插件运行letsencrypt:
- ./letsencrypt-auto certonly --standalone
根据提示输入信息,包括邮箱地址、同意服务条款以及证书适用的域名。完成后,大家将获得以下信息:
IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at
/etc/letsencrypt/live/your_domain_name/fullchain.pem. Your cert will expire
on 2016-08-10. To obtain a new version of the certificate in the
future, simply run Let's Encrypt again.
- If you like Let's Encrypt, please consider supporting our work by:
Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
Donating to EFF: https://eff.org/donate-le
请注意证书的路径与过期日期。大家的证书文件现在应该已经存在于/etc/letsencrypt/your_domain_name/位置了。
面向HTTPS配置Nginx
大家需要在Nginx配置中添加更多细节。在nano中打开/etc/nginx/sites-enabled/default:
- sudo nano /etc/nginx/sites-enabled/default
利用以下内容替换其原有内容:
/etc/nginx/sites-enabled/default
# HTTP - redirect all requests to HTTPS:
server {
listen 80;
listen [::]:80 default_server ipv6only=on;
return 301 https://$host$request_uri;
}
# HTTPS - proxy requests on to local Node.js app:
server {
listen 443;
server_name your_domain_name;
ssl on;
# Use certificate and key provided by Let's Encrypt:
ssl_certificate /etc/letsencrypt/live/your_domain_name/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your_domain_name/privkey.pem;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
# Pass requests for / to localhost:8080:
location / {
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:8080/;
proxy_ssl_session_reuse off;
proxy_set_header Host $http_host;
proxy_cache_bypass $http_upgrade;
proxy_redirect off;
}
}
保存并退出。再次启动Nginx:
- sudo systemctl start nginx
现在大家可以在浏览器中访问http://your_domain_name/以测试证书及Nginx配置了。在这里,大家应当被重新定向至http://your_domain_name/,且在不触发任何安全错误的前提下查看到“Hello World”。
设置Let’s Encrypt自动续租
注意:即使不进行证书续租,大家也可以顺利完成本教程。不过在长期运行的生产环境下,自动续租显然非常必要。
Let’s Encrypt证书的有效期为90天,这是为了尽可能缩短问题证书的存在时间。
Let’s Encrypt客户端能够自动进行证书续租,但大家仍然需要重复证书获取流程,或者使用脚本来完成这项任务。
总结
恭喜大家!现在各位已经拥有了运行在Ubuntu 16.04服务器上且由Nginx反向代理支持的Node.js应用。这套反向代理设置非常灵活,足以将用户引导至其它应用或者静态页面处。