Перейти к основному содержимому

Развёртываем NodeJS-приложение с балансировкой нагрузки и отказоустойчивостью

· 9 мин. чтения
Alex Congritta

В интернете огромное количество статей по поводу запуска NodeJS-приложения на сервере. Но большинство из этих статей доводят до того, что на сервере просто запускается приложение, при этом часто опускаются моменты с тем, как заставить его запускаться вместе с системой, как распределять нагрузку и как сделать его отказоустойчивым.

В этой статье я выскажу свой многолетний опыт деплоя NodeJS-приложений (то, как это делаю именно я)

Подготовка приложения

Для начала приложение, конечно, нужно разработать. Если вы не знаете как сделать приложение на NodeJS - эта статья не для вас. Возвращайтесь сюда, когда у вас на руках будет рабочий NodeJS проект.

Синхронизация с GitHub

После того, как вы доделали ваше приложение, его исходники нужно выложить в GitHub или любой другой хостинг Git-репозиториев

Подготовка сервера

Многие VPS-провайдеры на этапе выбора ОС предлагают готовую сборку с установленным NodeJS, но более хорошим и рациональным вариантом будет установка чистой ОС, а поверх неё всего необходимого.

После установки ОС сперва обновите её:

Если у вас Ubuntu:

sudo apt update && apt upgrade -y

Если у вас Debian:

apt update && apt upgrade -y
# выполнять под рутом

Если у вас Fedora, CentOS:

sudo dnf update

Далее, установите необходимые пакеты. Ибо неизвестно, насколько сырой дистрибутив вам дал ваш VPS-провайдер:

Если у вас Ubuntu:

sudo apt install -y curl wget git whois net-tools nano

Если у вас Debian:

apt install -y curl wget git whois net-tools nano
# запускать под рутом

Если у вас Fedora, CentOS, RHEL:

sudo dnf install curl wget git whois nano

Затем установите NodeJS. Команды для установки можно взять отсюда: https://github.com/nodesource/distributions. Желательно не устанавливать NodeJS из репозиториев операционной системы, т.к. как правило, там устаревшие версии.

Кроме NodeJS установите yarn (если он вам нужен) и pm2

Установка yarn (команды нужно выполнять под root):

corepack enable # если не сработало, то: npm i -g corepack
corepack prepare yarn@stable --activate

Установка pm2 (команду нужно выполнять под root):

npm i -g pm2

Далее вам нужно создать пользователя, под которым будут запускаться ваши скрипты. Нельзя никогда запускать скрипты от имени пользователя root, т.к. в этих скриптах может сидеть троян, который может нанести пиздюлей вашей системе. Поверьте, таких много.

Создать пользователя:

useradd ИМЯПОЛЬЗОВАТЕЛЯ -m -s /bin/bash

И сразу задайте ему пароль командой:

passwd ИМЯПОЛЬЗОВАТЕЛЯ

Если вы сидите под рутом, можно быстро переключиться на любого пользователя:

su ИМЯПОЛЬЗОВАТЕЛЯ

Чтобы перейти обратно на рута:

logout # или сочетание клавиш Ctrl+D

Есть ещё такая штука, что у пользователя будет старая версия yarn (начинается на "1."). Если это так, выполните под пользователем команду:

corepack prepare yarn@stable --activate

Новые версии yarn на момент написания статей начинаются на "3."

Выбор папки под проекты

Под хранение проектов можно выделить домашнюю папку пользователя или создать в любом месте папку и выдать права на пользователя

Импортируем проект из GitHub

Если ваш проект хранится в приватном репозитории (будь то GitHub, BitBucket, GitLab), вашему серверу нужно выдать доступ в приватные репозитории. Проще всего это будет сделать через SSH:

Генерируем SSH-ключ на сервере (если вы его ещё не создавали):

ssh-keygen

Отвечаем положительно на все вопросы. Если хотите, можете задать пароль на ваш ключ во время его создания. Пароль будет спрашиваться каждый раз, когда вы куда-то подключаетесь по SSH от имени вашего сервера.

Далее идём в настройки аккаунта на GitHub в раздел "SSH and GPG keys"

и нажимаем кнопочку "New SSH key"

Имя можете написать любое, а сам ключ можете получить этой командой:

cat ~/.ssh/id_rsa.pub # если вы выбрали стандартный путь при генерации ключа

Теперь сервер может видеть все ваши приватные репозитории. Можно так же указать ключ в настройках одного конкретного репозитория, но GitHub почему-то не даёт возможности указать один и тот же ключ на несколько репозиториев. В итоге либо один конкретный репозиторий, либо все сразу.

Чтобы проверить, есть ли у вашего сервера доступ к GitHub, выполните команду:

ssh git@github.com

После чего, если всё хорошо, GitHub скажет, что всё хорошо:

Для того, чтобы импортировать проект из GitHub:

cd ПАПКА_В_КОТОРОЙ_ХРАНЯТСЯ_ПРОЕКТЫ
git clone git@github.com:ВАШ_ЛОГИН/НАЗВАНИЕ_ПРОЕКТА

Делаем автоматическую пересборку и перезапуск проекта при обновлении

В Git есть хуки. Это скрипты, которые выполняются, когда вы совершаете действие с репозиторием (например, делаете коммит или делаете git pull).

Запишем хук post-merge, который будет срабатывать каждый раз, когда мы обновляем проект на сервере через git pull

nano ПАПКА_С_ПРОЕКТОМ/.git/hooks/post-merge

Записываем в файл следующее:

#!/bin/bash

cd ПУТЬ_К_ПАПКЕ_С_ПРОЕКТОМ \
&& yarn \
&& yarn build \ # если это предусмотрено в вашем проекте
&& (pm2 restart "НАЗВАНИЕ_ПРОЕКТА_КАК_В_НАЗВАНИИ_РЕПОЗИТОРИЯ" || \
pm2 start yarn --name "НАЗВАНИЕ_ПРОЕКТА_КАК_В_НАЗВАНИИ_РЕПОЗИТОРИЯ" -- start)

При необходимости, если знаете BASH, можете переделать скрипт под себя

Закрываем редактор через Ctrl+O, Enter, Ctrl+X

Сделаем наш файл исполняемым:

chmod +x ПАПКА_С_ПРОЕКТОМ/.git/hooks/post-merge

Всё. Теперь, если вы что-то меняете в проекте, вы выкладываете изменения на GitHub, а потом заходите на сервер в папку с проектом и делаете git pull, скрипт выше автоматически выполнится.

Чтобы выполнить этот скрипт принудительно, просто выполните этот файл. Очень полезно при первом запуске проекта на сервере:

ПАПКА_С_ПРОЕКТОМ/.git/hooks/post-merge

Добавляем проект в автозапуск

Тут нам поможет pm2. Если вы введёте:

pm2 status

Вы увидите состояние ваших запущенных проектов. Кстати pm2 может управлять не только NodeJS-проектами, но и любыми другми, в т.ч. Python и PHP-проектами. Документация на pm2.io

А команда

pm2 monit

покажет полную информацию о запущенном проекте:

Так вот. pm2 умеет хранить состояние проектов (включены/выключены). Для этого нужно выполнить:

pm2 save

И чтобы при перезагрузке сервера запущенные проекты запускались, нужно выполнить

pm2 startup

Чуточку масштабирования

Помните, что NodeJS не заточен под многопоточную работу. Если у вас на сервере 16 ядер, NodeJS будет оптимально использовать только некоторые из них. К тому же если у вас в проекте используется база данных (или Redis), то одно подключение к базе делится на весь проект. То есть да, если у вас высокая посещаемость, то сервер может работать медленно, потому что тупо все запросы к базе летят через одно подключение, которое создаётся при запуске проекта.

Будет здорово, если вы, как разработчик, не пытались затачивать архитектуру вашего NodeJS-проекта под многопоточность (не использовали модули worker_threads или cluser). Кодовая архитектура проекта должна быть написана стандартным способом, расчитанная на один поток.

Прикол в том, что вам сам проект нужно запустить несколько раз. В разных экземплярах. То есть у них одинаковые конфиги (в т.ч. подключение к базе данных), а различается только порт, на котором запущен HTTP-сервер. То есть мы должны получить то, что любой из проектов готов принять запрос от клиента (браузера) и успешно обработать его.

Такое "горизонтальное масштабирование" в рамках одного сервера позволит лучше распределить работу проекта на ядра процессора и избавиться от фатальных ошибок, которые убивают запущенный проект (в идеале в коде проекта предусмотреть, чтобы таких ошибок не было). Это называется отказоустойчивость.

Далее нам нужно грамотно настроить Nginx

Установка и настройка Nginx

Nginx нам нужен для того, чтобы привязать домен к конкретному NodeJS проекту. Если вы планируете использовать SSL (HTTPS), то им должен заниматься Nginx; сам проект должен работать по обычному HTTP.

Установим Nginx

Если у вас Ubuntu:

sudo apt install -y nginx

Если у вас Debian:

apt install -y nginx
# выполнять под рутом

Если у вас Fedora, CentOS:

sudo dnf install nginx

Если вы хотите SSL (HTTPS), листайте вниз или посмотрите содержание статьи

Направляем домен на запущенный NodeJS проект

Убедитесь, что на данном этапе знаете, на каком порту запущен ваш NodeJS-проект.

Идём в папку с конфигами Nginx:

cd /etc/nginx/sites-available

Если у вас такой папки нет, то тут дела обстоят сложнее. Возможно это папка /etc/nginx/conf.d. Если и это не сработает, то загуглите этот момент или установите Ubuntu на сервер.

В папке с конфигами создаём текстовый файл, который нужно назвать как-нибудь по-английски (я обычно называю его названием домена). В некоторых случаях nginx захочет, чтобы название файлов заканчивалось на .conf, поэтому для перестраховки можете добавить в конце .conf

nano /etc/nginx/sites-available/ДОМЕН_САЙТА.conf
# выполнять под рутом

Пишем в файл следующее:

upstream myproject {
server 127.0.0.1:8000;
# здесь список локальных адресов ваших запущенных NodeJS-проектов
}

server {
listen 80;

server_name СЮДА_ДОМЕН_САЙТА;

location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://myproject;
proxy_redirect off;
proxy_buffering off;
}
}

Закрываем редактор через Ctrl+O, Enter, Ctrl+X

Убедитесь, что ваш домен направлен на ваш сервер. Если вы не знаете, как это делается, то идите на сайт, где его покупали и просите об этом поддержку.

Далее нам нужно активировать наш сайт. Для этого выполним команду:

ln -s /etc/nginx/sites-available/ДОМЕН_САЙТА.conf /etc/nginx/sites-enabled/ДОМЕН_САЙТА.conf
# выполнять под рутом

После чего проверим, что наш конфиг-файл написан правильно:

nginx -t

Если ошибки не повылетали, значит всё правильно и можно перезапускать Nginx:

systemctl reload nginx
# выполнять под рутом

С этого момента если вы зайдёте в браузер и введёте в адресную строку ваш домен, у вас должен открыться ваш NodeJS-проект. Если это не так, мне очень жаль. Придётся перепроверять все свои действия, которые вы делали по этой статье. Если совсем всё плохо, обратитесь к любому человеку, который уже всё это запускал.

Делаем HTTPS

Для этого установите certbot. Эта штука автоматически установит SSL-сертификат и сделает всё как надо:

Если у вас Ubuntu:

sudo apt install -y certbot python3-certbot-nginx

Если у вас Debian:

apt install -y certbot python3-certbot-nginx
# выполнять под рутом

Если у вас Fedora, CentOS:

sudo dnf install certbot python3-certbot-nginx

После чего, выполните команду:

certbot
# выполнять под рутом

Если вы выполняете в первый раз, скрипт попросит вашу почту и согласие с использованием.

Далее скрипт выведет список всех сайтов, что нашёл в ваших конфигах. Вам нужно указать нужный сайт и дождаться положительного ответа. После чего, ваш сайт теперь начнёт работать по HTTPS