🚀 Публичный API для голоса. Дополнительные ноды и настройка NGINX Upstream load balancer (Балансировка нагрузки)


load.png

Задача

В одной из своих статей я описывал способ создания и настройки публичной API ноды на примере своей собственной ноды -
wss://api.golos.cf
Сейчас, спустя пару месяцев, ее стали использовать многие разработчики и нагрузки стали пиковые, что часто служит причиной вывода ноды из строя.
Пришло время масштабировать Public API путем объединения нескольких нод в одной точке доступа к API
Напомню, что в моих нодах включены все API плагины, что делает ноду требовательной к моим ресурсам, но зато позволяет пользователям использовать весь набор вызовов.

Решение

Если у вас уже есть публичная нода и доступ к Public API настроен через NGINX как описано в моей статье для масштабирования вам понадобится добавить всего несколько строк и установить дополнительные ноды.
(Если вы просто добавили внешний IP сервера в config.ini - вам нужно будет сменить его на localhost и создать проксирование на NGINX для настройки load balancer)


Load balancer будет состоять из 4 серверов, на каждый будет установлена полная нода голоса (со всеми плагинами) и nginx.

На сервере 1 будет открыт доступ к ноде для всех желающих по WSS на порте 443 (ssl/tls) - как это сделать описано по ссылкам выше.
В качестве живого примера wss://api.golos.cf

На серверах 2,3,4 доступ к нодам будет заблокирован для всех, кроме ip адреса сервера 1. Ноды будут иметь условный порт :777

Когда кто-то будет подключаться к wss://api.golos.cf nginx на сервере 1 будет распределять нагрузку между серверами 2,3,4 перенаправляя запрос на наименее нагруженный сервер. В случае недоступности серверов 2,3,4 - запрос будет обрабатываться нодой на сервере 1.

fe.png

Настройка - подготовка сервера

Если у вас сервер на обычных SSD вводим команду
df -h
Ищем shm и смотрим на его размер. Если менее 32Gb - увеличим его или добавим shm если такого не оказалось. Размер shm не должен превышать объем ОЗУ, хотя и может.
nano /etc/fstab - открываем конфиг в редакторе и добавляем строку

none /dev/shm tmpfs defaults,size=32G 0 0

Проверяем

mount -o remount /dev/shm
df -h

Далее в config.ini golosd добавляем строки

shared-file-dir = /dev/shm
shared-file-size = 32G

Если ваш сервер на быстрых NVMe SSD путь в shared-file-dir можно указать на произвольную директорию.

Устанавливаем ноды на произвольное количество серверов по моей инструкции способ создания и настройки публичной API ноды. Или по другим инструкциям, например через docker, в этом случае убедитесь, что установлен nginx.

Nginx.conf сервер 1

Подключим файл с настройками в nginx
Открываем в редакторе конфиг
nano /etc/nginx/nginx.conf
Ищем часть с include и добавляем перед ними

limit_req_zone $binary_remote_addr zone=ws:10m rate=1r/s
include /путь/к/удобной/папке/Nginx.conf;

Открываем файл конфига Nginx.conf и настраиваем поведение load balancer


Используем директиву nginx - upstream docs

upstream websockets {
least_conn;
server ws1.chain.cf:777 weight=5 max_fails=4 fail_timeout=30;
server ws2.chain.cf:777 weight=2 max_fails=3 fail_timeout=10s;
server ws3.chain.cf:777;
server 127.0.0.1:9090 backup;
}

Строки server ws*.chain.cf:777 - это адреса дополнительных нод для балансера.

least_conn

Задаёт для группы метод балансировки нагрузки, при котором запрос передаётся серверу с наименьшим числом активных соединений, с учётом весов серверов. Если подходит сразу несколько серверов, они выбираются циклически (в режиме round-robin) с учётом их весов.

backup
Этот параметр указывает, что к указанному серверу можно обращаться только в случае, если все остальные откажут. Я указал в качестве backup сервер 1 на котором стоит нода по той причине, что именно он распределяет нагрузку и если он будет чрезмерно перегружен, это отразится на всех остальных.

weight=</em>*
Параметр вес - это приоритет сервера, может быть полезно если в вашем кластере разные по мощности сервера.
Например в конфиге выше есть сервер с weight=5, weight=2 , без weight и backup
Это значит, что если мы получим 8 запросов, то 5 уйдут на ноду с весом 5, 2 на ноду с весом 2 и один на ноду без веса. При этом, если ноды выше не доступны, запрос будет направлен на backup ноду.

max_fails=4 fail_timeout=30
Это параметр создает условие, при котором сервер становится недоступным в течении 30 секунд если случилось 4 неудачных подключения подряд.

После описания upstream кластера с балансировкой, нужно перенаправить все запросы отправляемые к api.golos.cf на него. Это достигается в продолжении конфига:

server {
    listen 443 ssl; 
    ssl_certificate /tls/fullchain.pem; 
    ssl_certificate_key /tls/privkey.pem; 
    include /tls/options-ssl-nginx.conf;
    server_name api.golos.cf;
    root /home/html/;
    keepalive_timeout 65;
    keepalive_requests 100000;
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;

location ~ / {
       limit_req zone=ws burst=5;
        access_log off;
        proxy_pass http://websockets;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_next_upstream error timeout invalid_header http_500;
        proxy_connect_timeout 2;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

Первые несколько строк указывают используемый порт, 443 в данном случае позволяет подключаться к домену напрямую по зашифрованному wss. wss://api.golos.cf
Если я укажу 80 порт, то доступ к нодам будет ws://api.golos.cf (ws - без шифрования)
Если я укажу произвольный порт 12345, то доступ к нодам будет таким ws://api.golos.cf:12345
(Если используем шифрование, нужно указать пути к сертификатам.)

server_name - домен или ip сервера с нодой
location ~ / { ... } - указываем, что ноды будут доступны в корне домена, а не на определенной "странице".

limit_req zone=ws burst=5 - важный параметр:

Задаёт зону разделяемой памяти (zone) и максимальный размер всплеска запросов (burst). Если скорость поступления запросов превышает описанную в зоне, то их обработка задерживается так, чтобы запросы обрабатывались с заданной скоростью. Избыточные запросы задерживаются до тех пор, пока их число не превысит максимальный размер всплеска.

proxy_pass http://websockets; указываем куда перенаправлять запросы к api.golos.cf. websockets в данном случае указывает на upstream websockets {...} а начале конфига, а значит все запросы к api.golos.cf балансируются между серверами указанными в upstream websockets {...}

Nginx.conf сервера 2,3,4 ...

С остальными серверами настройка намного проще, в upstream мы укажем только локальный адрес ноды, который указан в config.ini у golosd

upstream websockets {
  server 127.0.0.1:9090;
}

В качестве порта укажем произвольный 777

server {
    listen 777; 
    server_name ws1(ИЛИ: 2,3,4).chain.cf;
    keepalive_timeout 65;
    keepalive_requests 100000;
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;

А в директории location заблокируем все подключения к ноде кроме подключений с ip адреса сервера 1

    location ~ / {
        allow IP.АДРЕС.СЕРВЕРА.1;
        deny all;
        access_log off;
        proxy_pass http://websockets;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_next_upstream error timeout invalid_header http_500;
        proxy_connect_timeout 2;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

}

После этих настроек можно перезапустить nginx командой
systemctl reload nginx

Описанная настройка работает на моей публичной ноде.

Дополнения


Если в конфиге указан dev/shm - в случае аварийной перезагрузки сервера вам придется делать replay блокчейна (это несколько часов для full ноды)
Если указана физическая директория - восстановить ноду можно будет не делая replay, однако если индекс будет поврежден, replay займет гораздо больше времени, так как диски медленнее RAM.

На серверах 2,3,4 - вместо nginx можно использовать встроенные iptables чтобы заблокировать доступ к нодам с чужих ip, а в конфиге ноды указать rpc-endpoint адрес вашего сервера. Но это гораздо менее гибкий способ по сравнениею с nginx проксированием.

Для поднятия кластера можно использовать docker, а в качестве серверной инфраструктуры использовать AWS, Google Cloud, Azure и т.п. - это может снизить стоимость если ваши ноды не используются 24. В случае постоянное нагрузки - такие облачные решения могут оказаться гораздо дороже обычных VPS/VDS. (О своем опыте выбора сервера я напишу в будущем)

Для использования нод единолично - кластер использовать не обязательно, поскольку можно оптимизировать свои скрипты.

Можно значительно сэкономить ресурсы отключив ненужные плагины в нодах, однако в силу особенностей дизайна API унаследованного со steemit - некоторые плагины имеют неочевидные кросс-зависимости, например для получения virtual ops из блоков, необходимо активировать тяжелый плагин истории аккаунта, чтобы получать корректную репутацию - необходимо включать follow плагин.
Чтобы не ограничивать функционал я включаю все плагины на паблик нодах.
Помните, что если вы включаете дополнительные плагины на работающей ноде - скорее всего вам приедтся запускать replay/resync для того, чтобы плагин правильно работал с данными.


Описанное выше вчера было применено к ноде wss://api.golos.cf в связи с переконфигруацией нода была недоступна некоторое время. Сейчас работа восстановлена.


Comments 7


Надеюсь, рано или поздно это и мне пригодится)

04.12.2017 16:30
0

Аналогично))

04.12.2017 18:43
0

@vik, огромное спасибо за то что делишься своим опытом!

04.12.2017 18:43
0

Извиняюсь за оффтоп, но у меня такой вопрос: статистика использования твоих апвоут-ботов, она секретная или нет?
Стало интересно, хотя бы на уровне "кол-во активных аккаунтов" для Голоса и для Стимита. Ну а если и по кол-ву апов можно узнать цифры - вообще шик. Может, ты уже писал об этом, просто я не нашел? Думаю, не я один любопытствую на эту тему :)

04.12.2017 21:03
0

Я считаю не этичным раскрывать логины пользователей ботов :)
Но общую статистику без имен пользователей сделать могу.

По количеству транзакций и пользователей. По размеру ботнетов.

Также могу сделать рейтинг по кураторам и фаворитам (кого пользователи ботов выбирают кураторами и фаворитами чаще всего)

Поскольку статистика - не приоритетная задача, не обещаю, что сделаю сегодня-завтра. Может быть в течении недели.

04.12.2017 21:11
0

Про раскрытие логинов я конечно не говорил, особенно моих :)
Я только общую статистику имел в виду. Спасибо, буду ждать!

04.12.2017 21:13
0