如何在 CentOS 7 上为生产设置 Node.js 应用程序

介绍

Node.js 是一个开源的 JavaScript 运行环境,可轻松构建服务器侧和网络应用程序. 该平台运行在 Linux、OS X、FreeBSD 和 Windows 上,其应用程序是用 JavaScript 编写的。

在本教程中,我们将介绍两个 CentOS 7 服务器组成的生产准备的 Node.js 环境的设置;一个服务器将运行由 PM2 管理的 Node.js 应用程序,而另一个服务器将通过 Nginx 反向代理向应用程序服务器提供用户访问该应用程序。

此教程的Ubuntu版本可以找到(https://andsky.com/tech/tutorials/how-to-set-up-a-node-js-application-for-production-on-ubuntu-14-04)。

前提条件

本指南使用两个 CentOS 7 服务器 具有私人网络(在同一数据中心)。私人网络可以在新服务器上配置(在选择额外选项部分中)。

  • app:我们将安装 Node.js 运行时的服务器,您的 Node.js 应用程序和 PM2.
  • web:我们将安装 Nginx 网络服务器的服务器,该服务器将作为您的应用程序的反向代理。

<$>[注] 注: 请参阅 DigitalOcean 文档 - 如何在 Droplets 上启用私人网络如果您打算使用目前没有配置私人网络的现有服务器。

在开始本指南之前,您应该有一个常规的非根用户,在您的两个服务器上都配置了sudo权限 - 这是您应该登录服务器的用户。

app服务器上执行的命令:

1[environment second]
2an_example_command_on_app

web服务器上执行的命令:

1[environment third]
2an_example_command_on_web

您可以为本教程使用单个服务器,但您必须在途中进行一些更改. 只需使用 localhost IP 地址,即 127.0.0.1,无论使用 app 服务器的私人 IP 地址。

以下是您在遵循本教程后设置的图表:

Reverse Proxy to Node.js Application

如果您想通过域名访问您的 web服务器,而不是其公共IP地址,请购买域名,然后按照以下教程:

让我们通过在 app服务器上安装 Node.js 运行时间来开始。

步骤 1 – 安装 Node.js

我们将在 app服务器上安装最新版本的 Node.js LTS。

SSH到您的 app服务器使用sudo特权的常规,非根用户。

app服务器上,让我们使用 curl 来下载 NodeSource RPM Repository 配置文件:

1[environment second]
2curl -L -o nodesource_setup.sh https://rpm.nodesource.com/setup_10.x

CURL将使用HTTPS协议将设置脚本下载到您的服务器,输出包括有关下载的信息:

1[secondary_label Output]
2  % Total    % Received % Xferd Average Speed Time Time Time Current
3                                 Dload Upload Total Spent Left Speed
4100 11109 100 11109 0 0 70128 0 --:--:-- --:--:-- --:--:-- 70757

接下来,您应该检查脚本的内容。下列命令将打开您的服务器控制台中的 NodeSource 设置脚本,然后您可以与 NodeSource 设置脚本(来自 NodeSource Distributions Github 存储库)进行交叉引用,以确认下载的脚本是正确的:

1[environment second]
2vi nodesource_setup.sh

一旦满足于文件,输出‘vi’通过键入‘:q’到‘quit’并返回命令行。

现在,让我们运行设置脚本来安装 NodeSource RPM 存储库,这将使我们能够从yum包管理器中访问 NodeSource 的存储库:

1[environment second]
2sudo -E bash nodesource_setup.sh

脚本输出有关设置的信息,以便我们参考:

 1[secondary_label Output]
 2## Installing the NodeSource Node.js 10.x repo...
 3
 4## Inspecting system...
 5
 6+ rpm -q --whatprovides redhat-release || rpm -q --whatprovides centos-release || rpm -q --whatprovides cloudlinux-release || rpm -q --whatprovides sl-release
 7+ uname -m
 8
 9## Confirming "el7-x86_64" is supported...
10
11+ curl -sLf -o /dev/null 'https://rpm.nodesource.com/pub_10.x/el/7/x86_64/nodesource-release-el7-1.noarch.rpm'
12
13## Downloading release setup RPM...
14
15+ mktemp
16+ curl -sL -o '/tmp/tmp.2aCcULVx8n' 'https://rpm.nodesource.com/pub_10.x/el/7/x86_64/nodesource-release-el7-1.noarch.rpm'
17
18## Installing release setup RPM...
19
20+ rpm -i --nosignature --force '/tmp/tmp.2aCcULVx8n'
21
22## Cleaning up...
23
24+ rm -f '/tmp/tmp.2aCcULVx8n'
25
26## Checking for existing installations...
27
28+ rpm -qa 'node|npm' | grep -v nodesource
29
30## Run `sudo yum install -y nodejs` to install Node.js 10.x and npm.
31## You may also need development tools to build native addons:
32     sudo yum install gcc-c++ make
33## To install the Yarn package manager, run:
34     curl -sL https://dl.yarnpkg.com/rpm/yarn.repo | sudo tee /etc/yum.repos.d/yarn.repo
35     sudo yum install yarn

在安装 Node.js 之前,清除缓存将确保 yum 使用网络连接来从我们的新 NodeSource 存储库中获取 Node.js (这将防止任何可能导致过时包的冲突):

1[environment second]
2sudo yum clean all

接下来,我们将下载并使用当前启用的yum repos 的所有元数据,以确保我们的yum查询尽快完成:

1[environment second]
2sudo yum makecache fast

要从npm编译和安装本地插件,我们还需要安装构建工具:

1[environment second]
2sudo yum install -y gcc-c++ make

现在我们可以安装 Node.js 包的最新版本:

1[environment second]
2sudo yum install -y nodejs

要验证 Node 是否已安装,请使用此命令检查其版本:

1[environment second]
2node -v

您的输出将显示您正在运行的版本号:

1[secondary_label Output]
2v10.16.3

Node.js 运行时间已安装,并准备运行应用程序. 让我们写一个 Node.js 应用程序。

步骤 2:创建 Node.js 应用程序

现在我们将创建一个Hello World应用程序,它只会返回任何HTTP请求的Hello World,这是一个样本应用程序,可以帮助您设置Node.js,您可以用自己的应用程序来代替它,只需确保您修改应用程序以聆听适当的IP地址和端口。

由于我们希望我们的 Node.js 应用程序能够服务于来自我们的反向代理服务器(web)的请求,我们将使用我们的 app服务器的私人网络接口进行服务器间的通信。

如果您正在使用 DigitalOcean Droplet 作为您的服务器,您可以通过 Metadata 服务搜索服务器的私人 IP 地址。

1[environment second]
2curl -sw "\n" http://169.254.169.254/metadata/v1/interfaces/private/0/ipv4/address

您将想要复制输出(私人IP地址),因为它将用于配置Node.js应用程序。

接下来,创建并打开您的 Node.js 应用程序进行编辑. 对于本教程,我们将使用vi来编辑名为hello.js的样本应用程序:

1[environment second]
2vi hello.js

将下列代码插入文件中,并确保将 app服务器的私有 IP 地址替换为两个突出标注的APP_PRIVATE_IP_ADDRESS项目,如果您想要,您也可以在两个位置更换突出标注的端口,即80 (请确保使用非管理员端口,即1024或更大):

1[label hello.js]
2var http = require('http');
3http.createServer(function (req, res) {
4  res.writeHead(200, {'Content-Type': 'text/plain'});
5  res.end('Hello World\n');
6}).listen(8080, 'APP_PRIVATE_IP_ADDRESS');
7console.log('Server running at http://APP_PRIVATE_IP_ADDRESS:8080/');

现在,通过按ESC来退出--INSERT--模式来保存和退出,然后按:wqquit在一个命令中。

这个 Node.js 应用程序只是在指定的 IP 地址和端口上收听,并以200 HTTP 成功代码返回Hello World,这意味着该应用程序只能从同一私人网络上的服务器访问,例如我们的 web 服务器。

如果您想测试您的应用程序是否工作,请在 app服务器上运行此节点命令:

1[environment second]
2node hello.js

<$>[注] 注: 以这种方式运行 Node.js 应用程序将阻止额外的命令,直到应用程序通过点击 CTRL+C 被杀死。

如果我们先测试我们的 web服务器能够在 app上与 Node.js 应用程序进行通信,那么它将节省大量的 Nginx 调试。

为了测试应用程序,请打开另一个终端会话并连接到您的 web服务器. 由于 Web 服务器位于相同的私人网络上,它应该能够使用curl访问 app服务器的私人 IP 地址。

1[environment third]
2curl http://APP_PRIVATE_IP_ADDRESS:8080

如果您看到以下输出,应用程序正在正常工作,并在正确的 IP 地址和端口上收听:

1[secondary_label Node Application Output]
2Hello World

如果您看不到正确的输出,请确保您的 Node.js 应用程序正在运行,并配置为听取正确的 IP 地址和端口。

app服务器上,请确保通过按CTRL+C来杀死应用程序。

步骤 3 – 安装和使用 PM2

现在我们将安装 PM2,这是一种 Node.js 应用程序的流程管理器。

我们将使用 Node Packaged Modules (NPM),这基本上是与 Node.js 一起安装的 Node 模块的包管理器,在我们的 app 服务器上安装 PM2。

1[environment second]
2sudo npm install pm2@latest -g

我们将讨论一些PM2的基本用途。

您要做的第一件事是使用pm2 start命令在背景中运行您的应用程序hello.js:

1[environment second]
2pm2 start hello.js

这还会将您的应用程序添加到 PM2 的流程列表中,该列表每次启动应用程序时都会输出:

1[secondary_label Output]
2┌──────────┬────┬──────┬───────┬────────┬─────────┬────────┬─────────────┬──────────┐
3│ App name │ id │ mode │ pid   │ status │ restart │ uptime │ memory      │ watching │
4├──────────┼────┼──────┼───────┼────────┼─────────┼────────┼─────────────┼──────────┤
5│ hello    │ 0  │ fork │ 30099 │ online │ 0       │ 0s     │ 14.227 MB   │ disabled │
6└──────────┴────┴──────┴───────┴────────┴─────────┴────────┴─────────────┴──────────┘

正如您所看到的,PM2 会自动分配一个 App name(基于文件名,不含 .js 扩展)和一个 PM2 id

如果应用程序故障或死亡,在PM2下运行的应用程序将自动重新启动,但需要采取额外的步骤来让应用程序在系统启动(启动或重新启动)上启动。

启动子命令生成并配置一个启动脚本,以便在服务器机器人上启动PM2及其管理流程。

1[environment second]
2sudo pm2 startup systemd

您将看到如下输出,表明已安装了 PM2 服务:

 1[secondary_label Output]
 2[PM2] Generating system init script in /etc/systemd/system/pm2.service
 3[PM2] Making script booting at startup...
 4[PM2] -systemd- Using the command:
 5      su root -c "pm2 dump && pm2 kill" && su root -c "systemctl daemon-reload && systemctl enable pm2 && systemctl start pm2"
 6[PM2] Dumping processes
 7[PM2] Stopping PM2...
 8[PM2] All processes have been stopped and deleted
 9[PM2] PM2 stopped
10[PM2] Done.

为了确保 PM2 知道在启动时要启动哪些应用程序,我们需要保存当前的流程列表。

1[environment second]
2pm2 save

您将看到如下输出,表明已保存 PM2 流程列表:

1[secondary_label Output]
2[PM2] Saving current process list...
3[PM2] Successfully saved in /home/deployer/.pm2/dump.pm2

现在,您的 PM2 管理应用程序应该自动启动。

PM2提供了许多子命令,允许您管理或搜索有关您的应用程序的信息。 请注意,运行pm2没有任何论点会显示一个帮助页面,包括示例使用,该页面比本教程的本节更详细地涵盖了PM2的使用。

使用此命令停止应用程序(指定PM2应用程序名称id):

1[environment second]
2pm2 stop example

使用此命令重新啟動應用程式(指定 PM2「應用程式名稱」或「id」):

1[environment second]
2pm2 restart example

目前由 PM2 管理的应用程序列表也可以通过列表子命令查看:

1[environment second]
2pm2 list

有关特定应用程序的更多信息可以通过使用info子命令找到(指定 PM2 App nameid):

1[environment second]
2pm2 info example

PM2 流程监视器可以通过monit子命令拉上,显示应用程序状态、CPU 和内存使用量:

1[environment second]
2pm2 monit

<$>[注] **注:**运行PM2的monit命令将阻止额外的命令,直到应用程序被击中CTRL+C

现在您的 Node.js 应用程序正在运行,并由 PM2 管理,让我们设置反向代理。

步骤 4 — 设置一个 Nginx 反向代理服务器

现在你的应用程序正在运行,并在私人IP地址上倾听,你需要为你的用户设置一种访问方式. 我们将为此设置一个Nginx网络服务器作为反向代理程序。本教程将从头开始设置一个Nginx服务器。如果你已经有了一个Nginx服务器设置,你可以简单地将位置块复制到你选择的服务器块(确保位置不会与你的Web服务器的任何现有内容相冲突)。

web 服务器上,让我们使用 yum 安装 epel-release 包:

1[environment third]
2sudo yum install epel-release

然后安装 Nginx:

1[environment third]
2sudo yum install nginx

现在打开 Nginx 配置文件来编辑:

1[environment third]
2sudo vi /etc/nginx/nginx.conf

首先,在默认服务器块中找到server_name定义的行,它应该看起来像这样:

1[label nginx.conf excerpt — server_name (before)]
2server_name _;

更新服务器名称以用您自己的域名替换 underscore(_)为server_name指令(或如果您没有域设置,则 IP 地址)。

1[label nginx.conf excerpt — server_name (after)]
2server_name your-domain;

然后,在同一个默认服务器块中找到定义位置/的行(通常在 server_name 下面几个行),它应该看起来像这样:

1[label nginx.conf excerpt — location / (before)]
2        location / {
3        }

用以下代码块替换它,并确保将 app服务器私人IP地址替换为APP_PRIVATE_IP_ADDRESS

1[label /etc/nginx/nginx.conf excerpt — location / (after)]
2    location / {
3        proxy_pass http://APP_PRIVATE_IP_ADDRESS:8080;
4        proxy_http_version 1.1;
5        proxy_set_header Upgrade $http_upgrade;
6        proxy_set_header Connection 'upgrade';
7        proxy_set_header Host $host;
8        proxy_cache_bypass $http_upgrade;
9    }

假设我们的服务器在您的域,通过网页浏览器访问http://your-domain/将请求发送到应用程序服务器的私人IP地址在端口8080,该请求将被接收并由Node.js应用程序响应。

您可以向相同的服务器块添加额外的位置块,以便在相同的 web服务器上提供访问其他应用程序,例如,如果您在端口 8081'上的 **app**服务器上运行了另一个 Node.js 应用程序,则可以添加此位置块,以便通过 http://your-domain/app2` 访问它:

1[label Nginx Configuration — Additional Locations]
2    location /app2 {
3        proxy_pass http://APP_PRIVATE_IP_ADDRESS:8081;
4        proxy_http_version 1.1;
5        proxy_set_header Upgrade $http_upgrade;
6        proxy_set_header Connection 'upgrade';
7        proxy_set_header Host $host;
8        proxy_cache_bypass $http_upgrade;
9    }

一旦你完成了编辑你的应用程序(s)的位置块(s),保存和退出,按ESC来退出--INSERT--模式,其次是:wqquit在一个命令。

在 Web 服务器上,重新启动 Nginx:

1[environment third]
2sudo systemctl start nginx

接下来,我们要确保每当服务器重新启动时, Nginx 都会运行:

1[environment third]
2sudo systemctl enable nginx

允许命令应提供以下输出

1[secondary_label Output]
2Created symlink from /etc/systemd/system/multi-user.target.wants/nginx.service to /usr/lib/systemd/system/nginx.service.

您也可以通过从systemctl请求其状态来确认 Nginx 正在运行并启用:

1[environment third]
2sudo systemctl status nginx

状态命令将为 Nginx 服务输出配置信息:

 1[secondary_label Output]
 2 nginx.service - The nginx HTTP and reverse proxy server
 3   Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; vendor preset: disabled)
 4   Active: active (running) since Mon 2019-10-14 09:37:23 UTC; 3min 29s ago
 5 Main PID: 12818 (nginx)
 6   CGroup: /system.slice/nginx.service
 7           ├─12818 nginx: master process /usr/sbin/nginx
 8           └─12819 nginx: worker process
 9
10Oct 14 09:37:23 centos-s-1vcpu-1gb-sgp1-01 systemd[1]: Starting The nginx HTTP and reverse proxy server...
11Oct 14 09:37:23 centos-s-1vcpu-1gb-sgp1-01 nginx[12814]: nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
12Oct 14 09:37:23 centos-s-1vcpu-1gb-sgp1-01 nginx[12814]: nginx: configuration file /etc/nginx/nginx.conf test is successful
13Oct 14 09:37:23 centos-s-1vcpu-1gb-sgp1-01 systemd[1]: Failed to read PID from file /run/nginx.pid: Invalid argument
14Oct 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 会阻止一系列配置。

1[environment third]
2ps -eZ

此命令提供流程状态信息,搜索 Nginx 特定的流程信息以查看标签. 您将看到 httpd_t,类似于以下方式:

1[secondary_label Output]
2...
3system_u:system_r:httpd_t:s0 10208 ?        00:00:00 nginx
4system_u:system_r:httpd_t:s0 10209 ?        00:00:00 nginx
5...

现在让我们检查与httpd_t SELinux标签相关的默认布莱恩的状态,我们可以通过运行以下命令来显示这些信息:

1[environment third]
2getsebool -a

我们只对本教程的httpd相关的布莱恩感兴趣:

 1[secondary_label Output]
 2...
 3httpd_anon_write --> off
 4httpd_builtin_scripting --> on
 5httpd_can_check_spam --> off
 6httpd_can_connect_ftp --> off
 7httpd_can_connect_ldap --> off
 8httpd_can_connect_mythtv --> off
 9httpd_can_connect_zabbix --> off
10httpd_can_network_connect --> off
11httpd_can_network_connect_cobbler --> off
12httpd_can_network_connect_db --> off
13httpd_can_network_memcache --> off
14httpd_can_network_relay --> off
15httpd_can_sendmail --> off
16httpd_dbus_avahi --> off
17httpd_dbus_sssd --> off
18httpd_dontaudit_search_dirs --> off
19httpd_enable_cgi --> on
20httpd_enable_ftp_server --> off
21httpd_enable_homedirs --> off
22httpd_execmem --> off
23httpd_graceful_shutdown --> on
24httpd_manage_ipa --> off
25httpd_mod_auth_ntlm_winbind --> off
26httpd_mod_auth_pam --> off
27httpd_read_user_content --> off
28httpd_run_ipa --> off
29httpd_run_preupgrade --> off
30httpd_run_stickshift --> off
31httpd_serve_cobbler_files --> off
32httpd_setrlimit --> off
33httpd_ssi_exec --> off
34httpd_sys_script_anon_write --> off
35httpd_tmp_exec --> off
36httpd_tty_comm --> off
37httpd_unified --> off
38httpd_use_cifs --> off
39httpd_use_fusefs --> off
40httpd_use_gpg --> off
41httpd_use_nfs --> off
42httpd_use_openstack --> off
43httpd_use_sasl --> off
44httpd_verify_dns --> off
45...

特别值得注意的两个布莱恩是 httpd_can_network_connect 和 httpd_can_network_relay. Redhat 文档详细介绍了每一个 httpd 布莱恩及其相关功能(您应该了解更多关于每个布莱恩),尽管以下是与本教程相关的两个布莱恩的解释:

1...
2httpd_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.
3httpd_can_network_relay: Enable this Boolean when httpd is being used as a forward or reverse proxy.
4...

由于我们的配置只是传输流量,我们只需要告诉SELinux,httpd服务器,在我们的情况下 Nginx,可以使用网络传输流量在我们已经设置的反向代理配置中。

1[environment third]
2sudo setsebool -P httpd_can_network_relay on

假设您的 Node.js 应用程序正在运行,您的应用程序和 Nginx 配置是正确的,您应该能够通过 web 服务器的反向代理程序访问您的应用程序。

<$>[注] 注: 如果您也打算使用您的 web服务器来托管其他网站(作为常规的虚拟主机),那么您还需要将httpd_can_network_connect设置为打开。

结论

您现在有 Node.js 应用程序运行在 Nginx 反向代理程序背后. 此反向代理程序设置足够灵活,可为用户提供您想要共享的其他应用程序或静态 Web 内容的访问。

此外,如果您正在寻求加密您的网页服务器和用户之间的传输, 这里是一个教程,可以帮助您设置HTTPS(TLS/SSL)支持

Published At
Categories with 技术
comments powered by Disqus