如何在 CentOS 7 上设置用于生产的 Node.js 应用程序
介绍
Node.js 是一个开源的 Javascript 运行时环境,用于轻松构建服务器端和网络应用程序。该平台在 Linux、OS X、FreeBSD 和 Windows 上运行,其应用程序是用 JavaScript 编写的。 Node.js 应用程序可以在命令行运行,但我们将教您如何将它们作为服务运行,这样它们会在重启或失败时自动重启,因此您可以在生产环境中使用它们。
在本教程中,我们将介绍如何设置由两台 CentOS 7 服务器组成的生产就绪 Node.js 环境;一台服务器将运行由 PM2 管理的 Node.js 应用程序,而另一台服务器将通过 Nginx 反向代理向应用程序服务器提供对应用程序的访问。
可以在此处找到本教程的 Ubuntu 版本。
先决条件
本指南使用两台具有专用网络的 CentOS 7 服务器(在同一数据中心)。可以在创建新服务器时在它们上配置专用网络(在 Select additional options
部分)。我们将使用以下名称来引用它们:
- app:我们将在其中安装 Node.js 运行时、您的 Node.js 应用程序和 PM2 的服务器。
- web:我们将在其中安装 Nginx 网络服务器的服务器,它将充当您的应用程序的反向代理。用户将访问此服务器的公共 IP 地址以访问您的 Node.js 应用程序。
注意:如果您打算使用当前未配置专用网络的现有服务器,请参阅 DigitalOcean 文档 - 如何在 Droplet 上启用专用网络。
在你开始本指南之前,你应该有一个普通的非根用户,在你的两台服务器上都配置了 sudo
权限——这是你应该登录到你的服务器的用户。您可以按照我们的 CentOS 7 初始服务器设置指南了解如何配置普通用户帐户。
在应用服务器上执行的命令:
- an_example_command_on_app
在网络服务器上执行的命令:
- an_example_command_on_web
本教程可以使用单个服务器,但您必须在此过程中进行一些更改。只要使用本地主机 IP 地址,即 127.0.0.1
,只要使用应用服务器的私有 IP 地址即可。
以下是按照本教程进行设置后的示意图:

如果您希望能够通过域名而不是其公共 IP 地址访问您的 Web 服务器,请购买域名,然后按照以下教程操作:
- 如何使用 DigitalOcean 设置主机名
- 如何从通用域注册商指向 DigitalOcean 名称服务器
让我们开始在应用服务器上安装 Node.js 运行时。
第 1 步 — 安装 Node.js
我们将在应用服务器上安装最新的 Node.js LTS 版本。
使用具有 sudo
权限的常规非 root 用户通过 SSH 连接到您的应用服务器。
在应用服务器上,让我们使用 curl
下载 NodeSource RPM Repository 配置文件:
- curl -L -o nodesource_setup.sh https://rpm.nodesource.com/setup_10.x
CURL
将使用 HTTPS 协议将设置脚本下载到您的服务器,输出包括与下载相关的信息:
Output % Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 11109 100 11109 0 0 70128 0 --:--:-- --:--:-- --:--:-- 70757
接下来,您应该检查脚本的内容。以下命令将在服务器控制台中打开 NodeSource 安装脚本,然后您可以将其与 NodeSource 安装脚本(来自 NodeSource Distributions Github 存储库)进行交叉引用,以确认脚本已正确下载:
- vi nodesource_setup.sh
对文件满意后,通过键入 :q
退出 vi
以 quit
并返回到命令行。
现在让我们运行安装脚本来安装 NodeSource RPM 存储库。这将使我们能够从 yum
包管理器中访问 NodeSource 的存储库:
- sudo -E bash nodesource_setup.sh
该脚本输出有关设置的信息以供我们参考:
Output## Installing the NodeSource Node.js 10.x repo...
## Inspecting system...
+ rpm -q --whatprovides redhat-release || rpm -q --whatprovides centos-release || rpm -q --whatprovides cloudlinux-release || rpm -q --whatprovides sl-release
+ uname -m
## Confirming "el7-x86_64" is supported...
+ curl -sLf -o /dev/null 'https://rpm.nodesource.com/pub_10.x/el/7/x86_64/nodesource-release-el7-1.noarch.rpm'
## Downloading release setup RPM...
+ mktemp
+ curl -sL -o '/tmp/tmp.2aCcULVx8n' 'https://rpm.nodesource.com/pub_10.x/el/7/x86_64/nodesource-release-el7-1.noarch.rpm'
## Installing release setup RPM...
+ rpm -i --nosignature --force '/tmp/tmp.2aCcULVx8n'
## Cleaning up...
+ rm -f '/tmp/tmp.2aCcULVx8n'
## Checking for existing installations...
+ rpm -qa 'node|npm' | grep -v nodesource
## Run `sudo yum install -y nodejs` to install Node.js 10.x and npm.
## You may also need development tools to build native addons:
sudo yum install gcc-c++ make
## To install the Yarn package manager, run:
curl -sL https://dl.yarnpkg.com/rpm/yarn.repo | sudo tee /etc/yum.repos.d/yarn.repo
sudo yum install yarn
在安装 Node.js 之前,清除 yum
中的所有缓存信息非常重要。清除缓存将确保 yum
使用网络连接从我们新的 NodeSource 存储库获取 Node.js(这将防止由过时的包引起的任何潜在冲突):
- sudo yum clean all
接下来,我们将为当前启用的 yum
存储库下载并使用所有元数据。这将确保我们的 yum
查询尽快完成:
- sudo yum makecache fast
要从 npm
编译和安装本机附加组件,我们还需要安装构建工具:
- sudo yum install -y gcc-c++ make
现在我们可以安装最新版本的 Node.js 包:
- sudo yum install -y nodejs
通过使用以下命令检查其版本来验证是否安装了 Node:
- node -v
您的输出将显示您正在运行的版本号:
Outputv10.16.3
Node.js 运行时现已安装,并准备好运行应用程序。让我们编写一个 Node.js 应用程序。
第 2 步 — 创建 Node.js 应用程序
现在我们将创建一个 Hello World 应用程序,它只向任何 HTTP 请求返回 Hello World
。这是一个示例应用程序,可帮助您设置 Node.js,您可以将其替换为您自己的应用程序——只需确保修改您的应用程序以侦听适当的 IP 地址和端口即可。
因为我们希望我们的 Node.js 应用程序服务于来自我们的反向代理服务器(网络)的请求,所以我们将使用我们的应用程序服务器的专用网络接口进行服务器间通信。查找您的应用服务器的私有网络地址。
如果您使用 DigitalOcean Droplet 作为您的服务器,您可以通过 Metadata 服务查找服务器的私有 IP 地址。在应用服务器上,现在使用 curl
命令检索 IP 地址:
- curl -sw "\n" http://169.254.169.254/metadata/v1/interfaces/private/0/ipv4/address
您需要复制输出(私有 IP 地址),因为它将用于配置 Node.js 应用程序。
接下来,创建并打开您的 Node.js 应用程序以进行编辑。对于本教程,我们将使用 vi
来编辑一个名为 hello.js
的示例应用程序:
- vi hello.js
将以下代码插入文件,并确保用应用服务器的私有 IP 地址替换两个突出显示的 APP_PRIVATE_IP_ADDRESS
项。如果需要,您还可以在两个位置替换突出显示的端口 8080
(确保使用非管理端口,即 1024
或更高):
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
}).listen(8080, 'APP_PRIVATE_IP_ADDRESS');
console.log('Server running at http://APP_PRIVATE_IP_ADDRESS:8080/');
现在通过按 ESC
退出 --INSERT--
模式保存并退出,然后按 :wq
write
和 quit
在一个命令中。
这个 Node.js 应用程序只是监听指定的 IP 地址和端口,并返回 Hello World
和 200
HTTP 成功代码。这意味着该应用程序只能从同一专用网络上的服务器访问,例如我们的 Web 服务器。
如果您想测试您的应用程序是否正常工作,请在应用程序服务器上运行此 node
命令:
- node hello.js
注意:以这种方式运行 Node.js 应用程序将阻止其他命令,直到通过按 CTRL+C
终止应用程序。
如果我们首先测试我们的 Web 服务器能够与应用程序上的 Node.js 应用程序通信,它将节省大量 Nginx 调试。
为了测试应用程序,打开另一个终端会话并连接到您的 Web 服务器。因为 Web 服务器位于同一专用网络上,所以它应该能够使用 curl
访问应用服务器的专用 IP 地址。请务必将 APP_PRIVATE_IP_ADDRESS
替换为应用服务器的私有 IP 地址,如果您更改了端口,请替换为:
- curl http://APP_PRIVATE_IP_ADDRESS:8080
如果您看到以下输出,则应用程序正常运行并侦听正确的 IP 地址和端口:
Node Application OutputHello World
如果您没有看到正确的输出,请确保您的 Node.js 应用程序正在运行,并且配置为侦听正确的 IP 地址和端口。
在应用程序服务器上,一定要按 CTRL+C
终止应用程序。
第 3 步 — 安装和使用 PM2
现在我们将安装 PM2,它是 Node.js 应用程序的进程管理器。 PM2 提供了一种简单的方法来管理和守护应用程序(将它们作为服务运行)。
我们将使用 Node Packaged Modules (NPM),它基本上是一个与 Node.js 一起安装的 Node 模块的包管理器,用于在我们的应用程序服务器上安装 PM2。使用此命令安装 PM2:
- sudo npm install pm2@latest -g
我们将介绍 PM2 的一些基本用途。
您要做的第一件事是使用 pm2 start
命令在后台运行您的应用程序 hello.js
:
- pm2 start hello.js
这也会将您的应用程序添加到 PM2 的进程列表中,每次启动应用程序时都会输出该列表:
Output┌──────────┬────┬──────┬───────┬────────┬─────────┬────────┬─────────────┬──────────┐
│ App name │ id │ mode │ pid │ status │ restart │ uptime │ memory │ watching │
├──────────┼────┼──────┼───────┼────────┼─────────┼────────┼─────────────┼──────────┤
│ hello │ 0 │ fork │ 30099 │ online │ 0 │ 0s │ 14.227 MB │ disabled │
└──────────┴────┴──────┴───────┴────────┴─────────┴────────┴─────────────┴──────────┘
如您所见,PM2 自动分配一个App 名称(基于文件名,没有.js
扩展名)和一个PM2 id。 PM2 还维护其他信息,例如进程的 PID、当前状态和内存使用情况。
如果应用程序崩溃或被杀死,在 PM2 下运行的应用程序将自动重新启动,但需要采取额外的步骤让应用程序在系统启动时启动(引导或重新启动)。幸运的是,PM2 提供了一种简单的方法来执行此操作,即 startup
子命令。
startup
子命令生成并配置启动脚本以在服务器启动时启动 PM2 及其托管进程。您还必须指定您正在运行的初始化系统,在我们的例子中是 systemd
:
- sudo pm2 startup systemd
你会看到类似下面的输出,说明PM2服务已经安装:
Output[PM2] Generating system init script in /etc/systemd/system/pm2.service
[PM2] Making script booting at startup...
[PM2] -systemd- Using the command:
su root -c "pm2 dump && pm2 kill" && su root -c "systemctl daemon-reload && systemctl enable pm2 && systemctl start pm2"
[PM2] Dumping processes
[PM2] Stopping PM2...
[PM2] All processes have been stopped and deleted
[PM2] PM2 stopped
[PM2] Done.
为了确保 PM2 知道在启动时启动哪些应用程序,我们需要保存当前进程列表。要保存列表:
- pm2 save
会看到如下输出,说明PM2进程列表已经保存:
Output[PM2] Saving current process list...
[PM2] Successfully saved in /home/deployer/.pm2/dump.pm2
现在你的 PM2 管理的应用程序应该在启动时自动启动。
PM2 提供了许多子命令,允许您管理或查找有关您的应用程序的信息。请注意,在不带任何参数的情况下运行 pm2
将显示一个帮助页面,包括示例用法,该页面比本教程的这一部分更详细地介绍了 PM2 的用法。
使用此命令停止应用程序(指定 PM2 App name
或 id
):
- pm2 stop example
使用此命令重新启动应用程序(指定 PM2 App name
或 id
):
- pm2 restart example
当前由 PM2 管理的应用程序列表也可以使用 list
子命令查找:
- pm2 list
可以使用 info
子命令(指定 PM2 App name 或 id)找到有关特定应用程序的更多信息:
- pm2 info example
可以使用 monit
子命令启动 PM2 进程监视器。这会显示应用程序状态、CPU 和内存使用情况:
- pm2 monit
注意:运行 PM2 的 monit
命令将阻止其他命令,直到通过按 CTRL+C
终止应用程序。
现在您的 Node.js 应用程序正在运行并由 PM2 管理,让我们设置反向代理。
第 4 步 — 设置 Nginx 反向代理服务器
现在您的应用程序正在运行并侦听私有 IP 地址,您需要为您的用户设置一种访问它的方法。为此,我们将设置一个 Nginx Web 服务器作为反向代理。本教程将从头开始设置 Nginx 服务器。如果您已经设置了 Nginx 服务器,则只需将 location
块复制到您选择的服务器块中(确保该位置不与您的 Web 服务器的任何现有内容冲突)。
在 Web 服务器上,让我们使用 yum 安装 epel-release
包:
- sudo yum install epel-release
然后安装 Nginx:
- sudo yum install nginx
现在打开Nginx配置文件进行编辑:
- sudo vi /etc/nginx/nginx.conf
首先,在默认服务器块中找到定义 server_name
的行。它应该看起来像这样:
server_name _;
更新服务器名称,将下划线 (_
) 替换为您自己的域名,以替换 server_name
指令(如果您没有设置域,则为 IP 地址)。
server_name your-domain;
然后,在相同的默认服务器块中找到定义 location /
的行(通常在 server_name 下面的几行)。它应该看起来像这样:
location / {
}
将其替换为以下代码块,并确保将 APP_PRIVATE_IP_ADDRESS
替换为应用服务器私有 IP 地址。此外,如果您的应用程序设置为侦听不同的端口,请更改端口 (8080
):
location / {
proxy_pass http://APP_PRIVATE_IP_ADDRESS: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;
}
这会将 Web 服务器配置为响应其根目录中的请求。假设我们的服务器在 your-domain
可用,通过网络浏览器访问 http://your-domain/
会将请求发送到端口上应用程序服务器的私有 IP 地址8080
,将由 Node.js 应用程序接收和回复。
您可以将其他 location
块添加到同一服务器块,以提供对同一 Web 服务器上其他应用程序的访问。例如,如果您还在端口 8081
上的应用服务器上运行另一个 Node.js 应用程序,您可以添加此位置块以允许通过 http://your-domain 访问它/app2
:
location /app2 {
proxy_pass http://APP_PRIVATE_IP_ADDRESS: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;
}
完成应用程序的位置块编辑后,按 ESC
保存并退出 --INSERT--
模式,然后按 :wq
到 write
和 quit
在一个命令中。
在 Web 服务器上,重新启动 Nginx:
- sudo systemctl start nginx
接下来我们要确保 Nginx 在服务器重新启动时运行:
- sudo systemctl enable nginx
enable
命令应提供以下输出
OutputCreated symlink from /etc/systemd/system/multi-user.target.wants/nginx.service to /usr/lib/systemd/system/nginx.service.
您还可以通过从 systemctl
请求其状态来确认 Nginx 正在运行并已启用:
- sudo systemctl status nginx
status 命令会输出 Nginx 服务的配置信息:
Output● nginx.service - The nginx HTTP and reverse proxy server
Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; vendor preset: disabled)
Active: active (running) since Mon 2019-10-14 09:37:23 UTC; 3min 29s ago
Main PID: 12818 (nginx)
CGroup: /system.slice/nginx.service
├─12818 nginx: master process /usr/sbin/nginx
└─12819 nginx: worker process
Oct 14 09:37:23 centos-s-1vcpu-1gb-sgp1-01 systemd[1]: Starting The nginx HTTP and reverse proxy server...
Oct 14 09:37:23 centos-s-1vcpu-1gb-sgp1-01 nginx[12814]: nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
Oct 14 09:37:23 centos-s-1vcpu-1gb-sgp1-01 nginx[12814]: nginx: configuration file /etc/nginx/nginx.conf test is successful
Oct 14 09:37:23 centos-s-1vcpu-1gb-sgp1-01 systemd[1]: Failed to read PID from file /run/nginx.pid: Invalid argument
Oct 14 09:37:23 centos-s-1vcpu-1gb-sgp1-01 systemd[1]: Started The nginx HTTP and reverse proxy server.
最后,为 Nginx 提供通过 Security-Enhanced Linux (SELinux) 中继流量的能力。 SELinux 提供了一个在 Linux 内核中实现强制访问控制 (MAC) 的安全层。每个操作系统对象(进程、文件描述符、文件等)都标有 SELinux 上下文,该上下文定义了对象可以执行的权限和操作。
Nginx 被标记为 httpd_t
上下文,因此,有许多配置被 SELinux 阻止,除非明确允许。为了演示这一点,运行以下命令以确认 Nginx 服务被标记为 httpd_t
:
- ps -eZ
该命令提供进程状态信息,搜索Nginx具体进程信息查看标签。您将以类似于以下的方式看到 httpd_t
:
Output...
system_u:system_r:httpd_t:s0 10208 ? 00:00:00 nginx
system_u:system_r:httpd_t:s0 10209 ? 00:00:00 nginx
...
现在让我们检查与 httpd_t
SELinux 标签相关的默认布尔值的状态。我们可以通过运行以下命令来显示此信息:
- getsebool -a
对于本教程,我们只对 httpd
相关的布尔值感兴趣:
Output...
httpd_anon_write --> off
httpd_builtin_scripting --> on
httpd_can_check_spam --> off
httpd_can_connect_ftp --> off
httpd_can_connect_ldap --> off
httpd_can_connect_mythtv --> off
httpd_can_connect_zabbix --> off
httpd_can_network_connect --> off
httpd_can_network_connect_cobbler --> off
httpd_can_network_connect_db --> off
httpd_can_network_memcache --> off
httpd_can_network_relay --> off
httpd_can_sendmail --> off
httpd_dbus_avahi --> off
httpd_dbus_sssd --> off
httpd_dontaudit_search_dirs --> off
httpd_enable_cgi --> on
httpd_enable_ftp_server --> off
httpd_enable_homedirs --> off
httpd_execmem --> off
httpd_graceful_shutdown --> on
httpd_manage_ipa --> off
httpd_mod_auth_ntlm_winbind --> off
httpd_mod_auth_pam --> off
httpd_read_user_content --> off
httpd_run_ipa --> off
httpd_run_preupgrade --> off
httpd_run_stickshift --> off
httpd_serve_cobbler_files --> off
httpd_setrlimit --> off
httpd_ssi_exec --> off
httpd_sys_script_anon_write --> off
httpd_tmp_exec --> off
httpd_tty_comm --> off
httpd_unified --> off
httpd_use_cifs --> off
httpd_use_fusefs --> off
httpd_use_gpg --> off
httpd_use_nfs --> off
httpd_use_openstack --> off
httpd_use_sasl --> off
httpd_verify_dns --> off
...
需要特别注意的两个布尔值是 httpd_can_network_connect
和 httpd_can_network_relay
。 Redhat 文档提供了有关每个 httpd
布尔值及其相关函数的详细信息(如果您希望了解有关每个布尔值的更多信息),尽管以下是与本教程相关的两个布尔值的解释:
...
httpd_can_network_connect: When disabled, this Boolean prevents HTTP scripts and modules from initiating a connection to a network or remote port. Enable this Boolean to allow this access.
httpd_can_network_relay: Enable this Boolean when httpd is being used as a forward or reverse proxy.
...
由于我们的配置只是中继流量,我们只需要告诉 SELinux httpd
服务器,在我们的例子中是 Nginx,可以使用网络在我们设置的反向代理配置中中继流量。我们将使用 -P
标志,以确保更改是永久性的(省略此标志将导致 httpd_can_network_relay
在服务器重新启动时恢复到默认状态,即关闭):
- sudo setsebool -P httpd_can_network_relay on
假设您的 Node.js 应用程序正在运行,并且您的应用程序和 Nginx 配置正确,您应该能够通过 Web 服务器的反向代理访问您的应用程序。通过访问您的 Web 服务器的 URL(其公共 IP 地址或域名)来尝试一下。
注意:如果您还计划使用您的 Web 服务器来托管其他站点(作为传统的虚拟主机),那么您还需要将 httpd_can_network_connect
设置为 on。
结论
现在,您的 Node.js 应用程序在 Nginx 反向代理后面运行。这种反向代理设置非常灵活,可以让您的用户访问您想要共享的其他应用程序或静态 Web 内容。
此外,如果您希望对 Web 服务器和用户之间的传输进行加密,这里的教程将帮助您设置 HTTPS (TLS/SSL) 支持。