介绍
Node.js是用于构建服务器端和网络应用程序的开源 JavaScript 运行时环境。该平台在 Linux、macOS、FreeBSD 和 Windows 上运行。尽管您可以在命令行中运行 Node.js 应用程序,但本教程将重点关注将它们作为服务运行。这意味着它们将在重新启动或故障时重新启动,并且在生产环境中使用是安全的。
在本教程中,您将在单个 Ubuntu 20.04 服务器上设置生产就绪的 Node.js 环境。该服务器将运行由PM2管理的 Node.js 应用程序,并通过 Nginx 反向代理为用户提供对应用程序的安全访问。Nginx 服务器将使用Let's Encrypt提供的免费证书提供 HTTPS 。
先决条件
本指南假定您具备以下条件:
- Ubuntu 20.04 服务器设置,如 Ubuntu 20.04 的初始服务器设置指南中所述。您应该有一个具有 sudo 权限和活动防火墙的非 root 用户。
- 指向您服务器的公共 IP的域名。本教程将始终使用域名example.com 。
- 已安装 Nginx,如如何在 Ubuntu 20.04 上安装 Nginx 中所述。
- Nginx 使用 Let's Encrypt 证书配置了 SSL。如何在 Ubuntu 20.04 上使用 Let's Encrypt 保护 Nginx将引导您完成整个过程。
完成先决条件后,您将拥有一台服务器,用于为您的域的默认占位符页面提供服务。https://example.com/
第 1 步 — 安装 Node.js
让我们首先使用NodeSource包档案安装最新的 LTS 版本的 Node.js。
首先,安装 NodeSource PPA 以访问其内容。确保您位于您的主目录中,并使用curl
它从其存档中检索最新 LTS 版本的 Node.js 的安装脚本。
cd ~
curl -sL https://deb.nodesource.com/setup_14.x -o nodesource_setup.sh
nano
您可以使用或您喜欢的文本编辑器检查此脚本的内容:
nano nodesource_setup.sh
检查完脚本后,在下面运行它sudo
:
sudo bash nodesource_setup.sh
PPA 将添加到您的配置中,并且您的本地包缓存将自动更新。从 Nodesource 运行安装脚本后,您可以安装 Node.js 包:
sudo apt install nodejs
要在这些初始步骤之后检查您安装了哪个版本的 Node.js,请键入:
node -v
Output
v14.4.0
注意:从 NodeSource PPA 安装时,Node.js 可执行文件称为nodejs
,而不是node
.
该nodejs
包包含nodejs
二进制文件以及npmNode 模块的包管理器,因此您无需npm
单独安装。
npm
使用主目录中的配置文件来跟踪更新。它将在您第一次运行时创建npm
。执行此命令以验证是否npm
已安装并创建配置文件:
npm -v
Output
6.14.5
为了使某些npm
包能够工作(例如,那些需要从源代码编译代码的包),您需要安装build-essential
包:
sudo apt install build-essential
您现在拥有必要的工具来处理npm
需要从源代码编译代码的包。
安装了 Node.js 运行时后,让我们继续编写 Node.js 应用程序。
第 2 步 — 创建 Node.js 应用程序
让我们编写一个向任何 HTTP 请求返回“Hello World”的Hello World应用程序。此示例应用程序将帮助您设置 Node.js。您可以将其替换为您自己的应用程序 — 只需确保修改您的应用程序以侦听适当的 IP 地址和端口。
首先,让我们创建一个名为的示例应用程序hello.js
:
cd ~
nano hello.js
在文件中插入以下代码:
<span style="color:#000000"><span style="background-color:#f3f5f9"><span style="color:#535772"><code class="language-javascript"><span style="color:#0069ff">const</span> http <span style="color:#666a71">=</span> <span style="color:#e0276a">require</span><span style="color:#666a71">(</span><span style="color:#08966b">'http'</span><span style="color:#666a71">)</span><span style="color:#666a71">;</span>
<span style="color:#0069ff">const</span> hostname <span style="color:#666a71">=</span> <span style="color:#08966b">'localhost'</span><span style="color:#666a71">;</span>
<span style="color:#0069ff">const</span> port <span style="color:#666a71">=</span> <span style="color:#225196">3000</span><span style="color:#666a71">;</span>
<span style="color:#0069ff">const</span> server <span style="color:#666a71">=</span> http<span style="color:#666a71">.</span><span style="color:#e0276a">createServer</span><span style="color:#666a71">(</span><span style="color:#666a71">(</span>req<span style="color:#666a71">,</span> res<span style="color:#666a71">)</span> <span style="color:#666a71">=></span> <span style="color:#666a71">{</span>
res<span style="color:#666a71">.</span>statusCode <span style="color:#666a71">=</span> <span style="color:#225196">200</span><span style="color:#666a71">;</span>
res<span style="color:#666a71">.</span><span style="color:#e0276a">setHeader</span><span style="color:#666a71">(</span><span style="color:#08966b">'Content-Type'</span><span style="color:#666a71">,</span> <span style="color:#08966b">'text/plain'</span><span style="color:#666a71">)</span><span style="color:#666a71">;</span>
res<span style="color:#666a71">.</span><span style="color:#e0276a">end</span><span style="color:#666a71">(</span><span style="color:#08966b">'Hello World!\n'</span><span style="color:#666a71">)</span><span style="color:#666a71">;</span>
<span style="color:#666a71">}</span><span style="color:#666a71">)</span><span style="color:#666a71">;</span>
server<span style="color:#666a71">.</span><span style="color:#e0276a">listen</span><span style="color:#666a71">(</span>port<span style="color:#666a71">,</span> hostname<span style="color:#666a71">,</span> <span style="color:#666a71">(</span><span style="color:#666a71">)</span> <span style="color:#666a71">=></span> <span style="color:#666a71">{</span>
console<span style="color:#666a71">.</span><span style="color:#e0276a">log</span><span style="color:#666a71">(</span><span style="color:#08966b">`</span><span style="color:#08966b">Server running at http://</span><span style="color:#666a71">${</span>hostname<span style="color:#666a71">}</span><span style="color:#08966b">:</span><span style="color:#666a71">${</span>port<span style="color:#666a71">}</span><span style="color:#08966b">/</span><span style="color:#08966b">`</span><span style="color:#666a71">)</span><span style="color:#666a71">;</span>
<span style="color:#666a71">}</span><span style="color:#666a71">)</span><span style="color:#666a71">;</span>
</code></span></span></span>
保存文件并退出编辑器。
此 Node.js 应用程序侦听指定的地址 ( localhost
) 和端口 ( 3000
),并返回“Hello World!” 带有200
HTTP 成功代码。由于我们正在监听localhost
,远程客户端将无法连接到我们的应用程序。
要测试您的应用程序,请键入:
node hello.js
您将收到以下输出:
Output
Server running at http://localhost:3000/
注意:以这种方式运行 Node.js 应用程序将阻止其他命令,直到应用程序被按 终止CTRL+C
。
要测试应用程序,请在您的服务器上打开另一个终端会话,然后连接localhost
到curl
:
curl http://localhost:3000
如果您得到以下输出,则表明应用程序正常工作并正在侦听正确的地址和端口:
Output
Hello World!
如果您没有获得预期的输出,请确保您的 Node.js 应用程序正在运行并配置为侦听正确的地址和端口。
一旦您确定它正在工作,请按 终止应用程序(如果您还没有)CTRL+C
。
第 3 步 — 安装 PM2
接下来让我们安装 PM2,一个 Node.js 应用程序的进程管理器。PM2 使守护应用程序成为可能,以便它们作为服务在后台运行。
用于npm
在您的服务器上安装最新版本的 PM2:
sudo npm install pm2@latest -g
该-g
选项告诉全局npm
安装模块,以便它在系统范围内可用。
让我们首先使用pm2 start
命令hello.js
在后台运行您的应用程序:
pm2 start hello.js
这也会将您的应用程序添加到 PM2 的进程列表中,每次启动应用程序时都会输出:
Output
... [PM2] Spawning PM2 daemon with pm2_home=/home/sammy/.pm2 [PM2] PM2 Successfully daemonized [PM2] Starting /home/sammy/hello.js in fork_mode (1 instance) [PM2] Done. ┌────┬────────────────────┬──────────┬──────┬───────────┬──────────┬──────────┐ │ id │ name │ mode │ ↺ │ status │ cpu │ memory │ ├────┼────────────────────┼──────────┼──────┼───────────┼──────────┼──────────┤ │ 0 │ hello │ fork │ 0 │ online │ 0% │ 25.2mb │ └────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘
如上所述,PM2 自动分配一个App name
(基于文件名,不带.js
扩展名)和一个 PM2 id
。PM2 还维护其他信息,例如PID
进程的名称、当前状态和内存使用情况。
如果应用程序崩溃或被杀死,在 PM2 下运行的应用程序将自动重新启动,但我们可以采取额外的步骤让应用程序在系统启动时使用startup
子命令启动。此子命令生成并配置启动脚本以在服务器启动时启动 PM2 及其托管进程:
pm2 startup systemd
结果输出的最后一行将包含一个以超级用户权限运行的命令,以便将 PM2 设置为在引导时启动:
Output
[PM2] Init System found: systemd sammy [PM2] To setup the Startup Script, copy/paste the following command: sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u sammy --hp /home/sammy
从输出中运行命令,使用您的用户名代替sammy
:
sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u sammy --hp /home/sammy
作为附加步骤,我们可以保存 PM2 进程列表和相应的环境:
pm2 save
您现在已经创建了一个在启动时为您的用户运行的 systemd单元。反过来,pm2
此实例运行.pm2
hello.js
使用以下命令启动服务systemctl
:
sudo systemctl start pm2-sammy
如果此时您遇到错误,您可能需要重新启动,您可以使用sudo reboot
.
检查 systemd 单元的状态:
systemctl status pm2-sammy
有关 systemd 的详细概述,请查看Systemd Essentials: Working with Services, Units, and the Journal。
除了我们已经介绍的那些之外,PM2 还提供了许多子命令,允许您管理或查找有关您的应用程序的信息。
使用此命令停止应用程序(指定 PM2App name
或id
):
pm2 stop app_name_or_id
重新启动应用程序:
pm2 restart app_name_or_id
列出当前由 PM2 管理的应用程序:
pm2 list
使用其获取有关特定应用程序的信息App name
:
pm2 info app_name
PM2 过程监视器可以使用monit
子命令启动。这将显示应用程序状态、CPU 和内存使用情况:
pm2 monit
请注意,pm2
不带任何参数运行也将显示带有示例用法的帮助页面。
现在您的 Node.js 应用程序正在由 PM2 运行和管理,让我们设置反向代理。
第 4 步 — 将 Nginx 设置为反向代理服务器
您的应用程序正在运行并正在侦听localhost
,但您需要设置一种方式让您的用户访问它。为此,我们将 Nginx Web 服务器设置为反向代理。
在先决条件教程中,您在文件中设置了 Nginx 配置。打开此文件进行编辑:/etc/nginx/sites-available/example.com
sudo nano /etc/nginx/sites-available/example.com
在server
块内,您应该有一个现有的location /
块。将该块的内容替换为以下配置。如果您的应用程序设置为侦听不同的端口,请将突出显示的部分更新为正确的端口号:
server {
...
location / {
proxy_pass http://localhost:3000;
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;
}
...
}
这将服务器配置为响应其根的请求。假设我们的服务器在 at 可用example.com
,通过 Web 浏览器访问会将请求发送到,侦听at的端口。https://example.com/
hello.js
3000
localhost
您可以向同一服务器块添加其他location
块,以提供对同一服务器上其他应用程序的访问。例如,如果您还在 port 上运行另一个 Node.js 应用程序3001
,您可以添加此位置块以允许通过以下方式访问它:https://example.com/app2
server {
...
location /app2 {
proxy_pass http://localhost:3001;
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;
}
...
}
为应用程序添加完位置块后,保存文件并退出编辑器。
确保您没有通过键入以下内容引入任何语法错误:
sudo nginx -t
重启 Nginx:
sudo systemctl restart nginx
假设您的 Node.js 应用程序正在运行,并且您的应用程序和 Nginx 配置正确,您现在应该能够通过 Nginx 反向代理访问您的应用程序。通过访问您的服务器的 URL(其公共 IP 地址或域名)来尝试一下。
结论
恭喜!现在,您的 Node.js 应用程序在 Ubuntu 20.04 服务器上的 Nginx 反向代理后面运行。这种反向代理设置足够灵活,可以让您的用户访问您想要共享的其他应用程序或静态 Web 内容。