Как настроить приложение Node.js для производства на CentOS 7

Вступление

Node.js is an open source Javascript runtime environment for easily building server-side and networking applications. Платформа работает на Linux, OS X, FreeBSD и Windows, а ее приложения написаны на JavaScript. Node.js applications can be run at the command line but we will teach you how to run them as a service, so they will automatically restart on reboot or failure, so you can use them in a production environment.

В этом руководстве мы рассмотрим настройку готовой к работе среды Node.js, которая состоит из двух серверов CentOS 7; один сервер будет запускать приложения Node.js, управляемые PM2, а другой предоставит пользователям доступ к приложению через обратный прокси-сервер Nginx к серверу приложений.

Версия этого руководства для Ubuntu находится вhere.

Предпосылки

В этом руководстве используются два сервера CentOS 7with private networking (в одном центре обработки данных). Частная сеть может быть настроена на новых серверах при их создании (в разделеSelect additional options). Мы будем ссылаться на них под следующими именами:

  • app: сервер, на котором мы установим среду выполнения Node.js, ваше приложение Node.js и PM2.

  • web: сервер, на котором мы установим веб-сервер Nginx, который будет действовать как обратный прокси для вашего приложения. Пользователи получат доступ к общему IP-адресу этого сервера, чтобы получить доступ к вашему приложению Node.js.

[.note] #Note: Обратитесь кDigitalOcean Documentation - How to Enable Private Networking on Droplets, если вы собираетесь использовать существующий сервер, на котором в настоящее время не настроена частная сеть.
#

Прежде чем вы начнете это руководство, у вас должен быть обычный пользователь без полномочий root с привилегиямиsudo, настроенными на обоих ваших серверах - это пользователь, под которым вы должны входить на свои серверы. Вы можете узнать, как настроить учетную запись обычного пользователя, следуя нашимinitial server setup guide for CentOS 7.

Команды, выполняемые на сервереapp:

an_example_command_on_app

Команды, выполняемые на сервереweb:

an_example_command_on_web

Для этого урока можно использовать один сервер, но вам придется внести несколько изменений по пути. Просто используйте локальный IP-адрес, т.е. 127.0.0.1, где бы ни использовался частный IP-адрес сервераapp.

Вот схема того, что будет после установки этого урока:

Reverse Proxy to Node.js Application

Если вы хотите иметь доступ к серверуweb через доменное имя, а не через его общедоступный IP-адрес, приобретите доменное имя и следуйте этим руководствам:

Начнем с установки среды выполнения Node.js на сервереapp.

[[step-1 -—- install-node-js]] == Шаг 1. Установка Node.js

Мы установим последнюю версию LTS Node.js на серверapp.

SSH на ваш серверapp, используя обычного пользователя без полномочий root с привилегиямиsudo.

На сервереapp воспользуемсяcurl для загрузки файла конфигурации репозитория RPMNodeSource:

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 setup script (из репозитория Github NodeSource Distributions) для подтверждения правильности загрузки сценария:

vi nodesource_setup.sh

Когда файл удовлетворителен, выйдите изvi, набрав от:q доquit, и вернитесь в командную строку.

Теперь давайте запустим скрипт установки, чтобы установить RPM-репозиторий NodeSource. Это позволит нам получить доступ к репозиторию NodeSource из диспетчера пакетовyum:

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 использует сетевое соединение для получения Node.js из нашего нового репозитория NodeSource (что предотвратит любые потенциальные конфликты, вызванные устаревшими пакетами):

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.

[[step-2 -—- created-the-node-js-application]] == Шаг 2 - Создание приложения Node.js

Теперь мы создадим приложение Hello World, которое просто возвращает"Hello World" на любые HTTP-запросы. Это пример приложения, которое поможет вам настроить Node.js, который вы можете заменить своим собственным приложением - просто убедитесь, что вы изменили свое приложение для прослушивания соответствующих IP-адресов и портов.

Поскольку мы хотим, чтобы наше приложение Node.js обслуживало запросы, поступающие с нашего обратного прокси-сервера (web), мы будем использовать интерфейс частной сети нашего сервераapp для межсерверной связи. Найдите адрес частной сети вашего сервераapp.

Если вы используете DigitalOcean Droplet в качестве сервера, вы можете найти частный IP-адрес сервера через службуMetadata. На сервере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

Вставьте следующий код в файл и обязательно замените частный IP-адрес сервераapp на оба выделенных элементаAPP_PRIVATE_IP_ADDRESS. Если вы хотите, вы также можете заменить выделенный порт8080 в обоих местах (обязательно используйте порт без прав администратора, т.е. 1024 или больше):

hello.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" с HTTP-кодом успеха200. Это означает, что приложение доступно только с серверов в той же частной сети, например, с нашего сервераweb.

Если вы хотите проверить, работает ли ваше приложение, запустите эту командуnode на сервереapp:

node hello.js

[.note] #Note: Запуск приложения Node.js таким образом заблокирует дополнительные команды до тех пор, пока приложение не будет убито нажатиемCTRL+C.
#

Это сэкономит много времени на отладке Nginx, если мы сначала проверим, что наш серверweb может взаимодействовать с приложением Node.js наapp.

Чтобы протестировать приложение, откройте другой сеанс терминала и подключитесь к серверуweb. Поскольку веб-сервер находится в той же частной сети, он должен иметь возможность получить доступ к частному IP-адресу сервераapp, используяcurl. Обязательно замените частный IP-адрес сервераapp наAPP_PRIVATE_IP_ADDRESS и порт, если вы его изменили:

curl http://APP_PRIVATE_IP_ADDRESS:8080

Если вы видите следующий вывод, приложение работает правильно и прослушивает правильный IP-адрес и порт:

Node Application OutputHello World

Если вы не видите правильный вывод, убедитесь, что ваше приложение Node.js запущено и настроено на прослушивание правильного IP-адреса и порта.

На сервереapp обязательно завершите работу приложения, нажавCTRL+C.

[[step-3 -—- install-and-using-pm2]] == Шаг 3 - Установка и использование PM2

Теперь мы установим PM2, который является менеджером процессов для приложений Node.js. PM2 предоставляет простой способ управления приложениями и их демонизации (запуск их в качестве службы).

Мы будем использовать Node Packaged Modules (NPM), который по сути представляет собой менеджер пакетов для модулей Node, который устанавливается с Node.js, чтобы установить PM2 на нашем сервереapp. Используйте эту команду для установки 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 name (на основе имени файла, без расширения.js) и PM2id. 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 более подробно, чем этот раздел руководства.

Остановите приложение с помощью этой команды (укажите PM2App name илиid):

pm2 stop example

Перезапустите приложение с помощью этой команды (укажите PM2App name илиid):

pm2 restart example

Список приложений, которыми в настоящее время управляет PM2, также можно просмотреть с помощью подкомандыlist:

pm2 list

Более подробную информацию о конкретном приложении можно найти с помощью подкомандыinfo (укажите PM2App name илиid):

pm2 info example

Монитор процесса PM2 можно вызвать с помощью подкомандыmonit. Это отображает статус приложения, процессор и использование памяти:

pm2 monit

[.note] #Note: Выполнение команды PM2monit заблокирует дополнительные команды до тех пор, пока приложение не будет убито нажатиемCTRL+C.
#

Теперь, когда ваше приложение Node.js запущено и управляется PM2, давайте настроим обратный прокси-сервер.

[[step-4 -—- setting-up-an-nginx-reverse-proxy-server]] == Шаг 4. Настройка обратного прокси-сервера Nginx

Теперь, когда ваше приложение работает и прослушивает частный IP-адрес, вам нужно настроить способ доступа пользователей к нему. Для этой цели мы настроим веб-сервер Nginx в качестве обратного прокси-сервера. Из этого туториала вы сможете настроить сервер Nginx с нуля. Если у вас уже настроен сервер Nginx, вы можете просто скопировать блокlocation в блок сервера по вашему выбору (убедитесь, что расположение не конфликтует с каким-либо существующим содержимым вашего веб-сервера).

На сервереweb давайте установим пакетepel-release с помощью yum:

sudo yum install epel-release

Затем установите Nginx:

sudo yum install nginx

Теперь откройте файл конфигурации Nginx для редактирования:

sudo vi /etc/nginx/nginx.conf

Сначала найдите строку, в которой определеноserver_name, в блоке сервера по умолчанию. Это должно выглядеть примерно так:

nginx.conf excerpt — server_name (before)

server_name _;

Обновите имя сервера, заменив символ подчеркивания (_) на ваше собственное доменное имя для директивыserver_name (или IP-адрес, если у вас не настроен домен).

nginx.conf excerpt — server_name (after)

server_name your-domain;

Затем найдите строку, в которой определенlocation / (обычно на несколько строк ниже server_name), в том же блоке сервера по умолчанию. Это должно выглядеть примерно так:

nginx.conf excerpt — location / (before)

        location / {
        }

Замените его следующим блоком кода и обязательно замените частный IP-адрес сервераapp наAPP_PRIVATE_IP_ADDRESS. Кроме того, измените порт (8080), если ваше приложение настроено на прослушивание другого порта:

/etc/nginx/nginx.conf excerpt — location / (after)

    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. Например, если вы также запускаете другое приложение Node.js на сервереapp на порту8081, вы можете добавить этот блок местоположения, чтобы разрешить доступ к нему через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.

Вы также можете подтвердить, что Nginx запущен и включен, запросив его статус уsystemctl:

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 предоставляет уровень безопасности, который реализует обязательное управление доступом (MAC) в ядре Linux. Каждый объект операционной системы (процесс, дескриптор файла, файл и т. Д.) Помечается контекстом 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
...

Теперь давайте проверим состояние логических значений по умолчанию, связанных с меткой SELinuxhttpd_t. Мы можем показать эту информацию, выполнив следующую команду:

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 Documentation предоставляет подробную информацию о каждом из логических значений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. Попробуйте это, получив доступ к URL-адресу вашего сервераweb (его общедоступному IP-адресу или имени домена).

[.note] #Note: Если вы также планировали использовать свой серверweb для размещения других сайтов (в качестве обычных виртуальных хостов), вам также потребуется установить дляhttpd_can_network_connect значение on .
#

Заключение

Теперь у вас есть приложение Node.js, работающее за обратным прокси-сервером Nginx. Эта настройка обратного прокси-сервера достаточно гибкая, чтобы предоставить вашим пользователям доступ к другим приложениям или статическому веб-контенту, которым вы хотите поделиться.

Кроме того, если вы хотите зашифровать передачу данных между вашим веб-сервером и вашими пользователями,here is a tutorial that will help you get HTTPS (TLS/SSL) support set up.

Related