如何在CentOS7上设置用于生产的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 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

对文件满意后,通过键入 :qquit 退出 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-- 模式保存并退出,然后在单个命令中按 :wqwritequit

此 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 nameid):

pm2 stop example

使用此命令重新启动应用程序(指定 PM2 App nameid):

pm2 restart example

当前由 PM2 管理的应用程序列表也可以使用 list 子命令查找:

pm2 list

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

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-- 模式,然后按 :wqwritequit 在一个命令中。

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_connecthttpd_can_network_relayRedhat 文档 提供了每个 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) 支持