介绍
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 Web 服务器的服务器,它将充当您应用程序的反向代理。 用户将访问此服务器的公共 IP 地址以访问您的 Node.js 应用程序。
注意: 如果您打算使用当前未配置专用网络的现有服务器,请参阅 DigitalOcean 文档 - 如何在 Droplets 上启用专用网络。
在开始本指南之前,您应该在两台服务器上配置一个具有 sudo
权限的普通非 root 用户——这是您应该登录到服务器的用户。 您可以按照我们的 CentOS 7 初始服务器设置指南来学习如何配置普通用户帐户。
在 app 服务器上执行的命令:
an_example_command_on_app
在 web 服务器上执行的命令:
an_example_command_on_web
本教程可以使用单个服务器,但您必须在此过程中进行一些更改。 只需使用 localhost IP 地址,即 127.0.0.1
,只要使用 app 服务器的私有 IP 地址。
这是遵循本教程后您的设置的图表:
如果您希望能够通过域名而不是其公共 IP 地址访问您的 web 服务器,请购买域名,然后按照以下教程操作:
让我们开始在 app 服务器上安装 Node.js 运行时。
第 1 步 — 安装 Node.js
我们将在 app 服务器上安装最新的 LTS 版本的 Node.js。
使用具有 sudo
权限的常规非 root 用户通过 SSH 连接到您的 app 服务器。
在 app 服务器上,让我们使用 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
到 quit
退出 vi
并返回命令行。
现在让我们运行安装脚本来安装 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 应用程序,它简单地将 "Hello World"
返回到任何 HTTP 请求。 这是一个示例应用程序,可帮助您设置 Node.js,您可以将其替换为您自己的应用程序 - 只需确保修改您的应用程序以侦听适当的 IP 地址和端口。
因为我们希望我们的 Node.js 应用程序服务来自我们的反向代理服务器 (web) 的请求,我们将使用我们的 app 服务器的专用网络接口进行服务器间通信。 查找您的 app 服务器的私有网络地址。
如果您使用 DigitalOcean Droplet 作为服务器,您可以通过 Metadata 服务查找服务器的私有 IP 地址。 在 app 服务器上,现在使用 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
将以下代码插入文件中,并确保将 app 服务器的私有 IP 地址替换为突出显示的 APP_PRIVATE_IP_ADDRESS
项。 如果您愿意,您也可以在两个位置替换突出显示的端口 8080
(请务必使用非管理员端口,即 1024
或更大):
你好.js
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 服务器。
如果您想测试您的应用程序是否工作,请在 app 服务器上运行此 node
命令:
node hello.js
注意: 以这种方式运行 Node.js 应用程序将阻止其他命令,直到应用程序被按 CTRL+C
杀死。
如果我们首先测试我们的 web 服务器能够与 app 上的 Node.js 应用程序通信,它将节省大量 Nginx 调试。
为了测试应用程序,打开另一个终端会话并连接到您的 web 服务器。 因为web服务器在同一个私网,所以应该可以使用curl
访问app服务器的私网IP地址。 请务必在 app 服务器的私有 IP 地址中替换 APP_PRIVATE_IP_ADDRESS
,如果您更改了端口,请务必替换:
curl http://APP_PRIVATE_IP_ADDRESS:8080
如果您看到以下输出,则表明应用程序正常工作并正在侦听正确的 IP 地址和端口:
Node Application OutputHello World
如果您没有看到正确的输出,请确保您的 Node.js 应用程序正在运行,并配置为侦听正确的 IP 地址和端口。
在 app 服务器上,请务必通过按 CTRL+C
来终止应用程序。
第 3 步 — 安装和使用 PM2
现在我们将安装 PM2,它是 Node.js 应用程序的进程管理器。 PM2 提供了一种简单的方法来管理和守护应用程序(将它们作为服务运行)。
我们将使用 Node Packaged Modules (NPM),它基本上是与 Node.js 一起安装的 Node 模块的包管理器,在我们的 app 服务器上安装 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
PM2 过程监视器可以使用 monit
子命令启动。 这将显示应用程序状态、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
的行。 它应该看起来像这样:
nginx.conf 摘录——server_name(之前)
server_name _;
更新服务器名称,将下划线 (_
) 替换为 server_name
指令的您自己的域名(如果您没有设置域,则使用 IP 地址)。
nginx.conf 摘录——server_name(之后)
server_name your-domain;
然后,在同一个默认服务器块中找到定义 location /
的行(通常在 server_name 下面几行)。 它应该看起来像这样:
nginx.conf 摘录——位置/(之前)
location / { }
将其替换为以下代码块,并确保将 app 服务器私有 IP 地址替换为 APP_PRIVATE_IP_ADDRESS
。 此外,如果您的应用程序设置为侦听不同的端口,请更改端口 (8080
):
/etc/nginx/nginx.conf 摘录——位置/(之后)
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
可用,通过 Web 浏览器访问 http://your-domain/
会将请求发送到端口 8080
上的应用程序服务器的私有 IP 地址,该地址将被接收并回复通过 Node.js 应用程序。
您可以将额外的 location
块添加到同一服务器块,以提供对同一 web 服务器上其他应用程序的访问。 例如,如果您还在 app 服务器上的端口 8081
上运行另一个 Node.js 应用程序,则可以添加此位置块以允许通过 http://your-domain/app2
访问它:
Nginx 配置 - 其他位置
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
设置为在。
结论
现在,您的 Node.js 应用程序在 Nginx 反向代理之后运行。 这种反向代理设置足够灵活,可以让您的用户访问您想要共享的其他应用程序或静态 Web 内容。
此外,如果您希望对 Web 服务器和用户之间的传输进行加密,这里有一个教程可以帮助您设置 HTTPS (TLS/SSL) 支持 。