Реализация отказоустойчивого Redis в рамках горизонтального масштабирования DirectumRX

25 7

В случае применения горизонтального масштабирования сервисных служб DirectumRX необходимо использовать отказоустойчивую инсталляцию Redis для сервисных служб сервис хранилищ DirectumRX. В статье рассмотрим наиболее простой вариант реализации.

Введение в решаемую задачу

Redis – это система управления базами данных класса NoSQL (не реляционные СУБД), размещаемых целиком в оперативной памяти. Для доступа к данным используется модель «ключ» - «значение». Такая СУБД используется зачастую для хранения кэшей в масштабируемых сервисах, для хранения изображений и данных небольшого размера.

Широкое распространение СУБД Redis получила за счет:

  • высокой скорости работы, т.к. все данные хранятся в оперативной памяти,
  • кроссплатформенности,
  • распространению по BSD лицензии (относится к СПО).

Широту распространения и применимость Redis можно оценить по огромному количеству документации со всевозможными кейсами на официальном сайте проекта: https://redis.io/documentation.

В случае применения горизонтального масштабирования сервисных служб DirectumRX необходимо использовать отказоустойчивую инсталляцию Redis для корректной работы с сервисом хранилищ DirectumRX и сервисом веб-доступа DirectumRX.

 

В Redis будут храниться оперативные данные, кэши и прочая информация, которая необходима для работы служб, находящихся в режиме масштабирования, чтобы процесс взаимодействия пользователя с системой не зависел от инсталляции, с который в текущий момент он работает.

В конфигурационных файлах служб необходимо указать необходимые параметры для подключения к Redis.

Для web-доступа:

<var name="ENABLE_SCALING" value="True"/>
<var name="REDIS_HOST" value="RedisHost"/>
<var name="REDIS_PORT" value="6379"/>

Для сервиса хранилищ:

<var name="STORAGE_SERVICE_ENABLED_SCALING" value="True" />
<var name="STORAGE_SERVICE_REDIS_HOST" value="RedisHost" />
<var name="STORAGE_SERVICE_REDIS_PORT" value="6379" />

ENABLE_SCALING и STORAGE_SERVICE_ENABLED_SCALING - признак включения горизонтального масштабирования.

REDIS_HOST и STORAGE_SERVICE_REDIS_HOST - путь до компьютера, на котором развернут экземпляр службы Redis. 

REDIS_PORT и STORAGE_SERVICE_REDIS_PORT - порт, по которому сервис взаимодействует со службой Redis. Значение по умолчанию 6379.

Разворачивание отказоустойчивого Redis 

На официальном сайте Redis выделяется 2 способа обеспечения отказоустойчивости:

  1. Использование Redis Sentiel (https://redis.io/topics/sentinel).
  2. Использование Redis Cluster (https://redis.io/topics/cluster-spec).

Redis Sentiel

Вариант с использованием Redis Sentiel (Следящий узел Redis) был реализован в версии Redis 2.4 и состоит в том, что для мониторинга доступности мастера используется дополнительный сервис Redis Sentiel. Он же выполняет настройку узлов реплик, в случае выхода из строя мастера. Определяет какой из SLAVE узлов станет MASTER и выполнит перенастройку на ходу.

Реализует классическую схему:

SLAVE-узлов может быть множество (до 1000 по данным с официального сайта), для продуктивной работы рекомендуется использовать не менее двух SLAVE-узлов.

Обычно схема настраивается таким образом, что на MASTER- и на SLAVE-узлах настраивается сервис Redis Sentiel и при выходе из строя MASTER-узла, оставшиеся следящие узлы принимают решение о введении в работу нового MASTER.

Redis Cluster

Вариант кластеризации Redis Cluster реализован для версии redis 3.0 и выше, является решением для создания и управления кластером с сегментацией и репликацией данных. Выполняет задачи управления узлами, репликации, синхронизации данных на узлах и обеспечения доступа клиентского приложения к MASTER-узлу в случае выхода из строя одного из нескольких MASTER-узлов.


 

Redis Cluster наиболее надежный вариант. Работает в режиме мультимастера, у каждого MASTER-узла может быть один или более SLAVE-узлов (до 1000).

Представленная схема требует, как минимум 3-6 узлов. Запросы перенаправляются от клиентов на нужный MASTER или SLAVE — но это требует поддержки режима кластера Redis самим клиентским приложением.

В данной статье будем рассматривать наиболее простой вариант – Redis Sentiel.

Создание отказоустойчивой инсталляций по схеме Redis Sentiel

Актуальная версия Redis доступна для скачивания с официального сайта разработчика продукта https://redis.io/download. Однако на сайте доступен дистрибутив только для ОС Linux. В своё время развивался проект Microsoft по портированию Redis на ОС Windows (https://github.com/MicrosoftArchive/Redis), однако в настоящее время проект остановил развитие на версии 3.2.100, поэтому в данной статье будем рассматривать наиболее актуальный вариант разворачивания – на ОС Linux.

В качестве тестовых узлов будем использовать два виртуальных хоста redis1 и redis2 с установленным дистрибутивом Linux Debian 10.

Первым делом подключаем репозиторий Redis для получения актуальных версий:

1. Создадим файл /etc/apt/sources.list.d/dotdeb.list и пропишем адреса репозитория:

deb http://packages.dotdeb.org stable all
deb-src http://packages.dotdeb.org stable all

2. Получим GPG-ключ для работы с репозиторием:

wget https://www.dotdeb.org/dotdeb.gpg
apt-key add dotdeb.gpg 

При необходимости, устанавливаем дополнительные компоненты:

apt install gnupg

3. Устанавливаем Redis:

apt install redis-server

Проверим версию:

root@redis1:/home/user# redis-server -v
Redis server v=5.0.3 sha=00000000:0 malloc=jemalloc-5.1.0 bits=64 build=afa0decbb6de285f

Пусть redis1 будет выступать в качестве MASTER-узла, а redis2 – в качестве SLAVE-узла.

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

Для redis1 в конфигурационном файле /etc/redis/redis.conf указываем:

#указываем пароль, по которому MASTER будет разрешать работать с ним.
requirepass TestPass 

Для redis2  в конфигурационном файле /etc/redis/redis.conf указываем:

# Указываем адрес MASTER и порт.
slaveof redis1 6379 
# Указываем пароль для работы с мастером.
masterauth TestPass 
# Также указываем пароль, чтобы с узлом можно было работать только по паролю.
requirepass TestPass 

Выполним перезапуск сервисов redis-server на обоих узлах:

root@redis1:/etc/redis# /etc/init.d/redis-server stop
[ ok ] Stopping redis-server (via systemctl): redis-server.service.
root@redis1:/etc/redis# /etc/init.d/redis-server start
[ ok ] Starting redis-server (via systemctl): redis-server.service.

root@redis2:/etc/redis# /etc/init.d/redis-server stop
[ ok ] Stopping redis-server (via systemctl): redis-server.service.
root@redis2:/etc/redis# /etc/init.d/redis-server start
[ ok ] Starting redis-server (via systemctl): redis-server.service.

Проверяем на стороне MASTER, что узлы стали репликами и получили необходимые роли:

root@redis1:/etc/redis# redis-cli -a TestPass info replication
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.9.96,port=6379,state=online,offset=28,lag=0
master_replid:56b0a702d5823d107b0ca1ca2c80f8ef650a4b28
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:28
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:28

На стороне SLAVE видим такую же ситуацию:

root@redis2:/etc/redis# redis-cli -a TestPass info replication
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
# Replication
role:slave
master_host:redis1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:4
master_sync_in_progress:0
slave_repl_offset:14
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:56b0a702d5823d107b0ca1ca2c80f8ef650a4b28
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:14
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:14

Теперь необходимо настроить реплику таким образом, чтобы она автоматически восстанавливалась в случае выхода из строя одного из узлов. Для этого нам понадобиться следящий сервис Redis Sentinel.

Исходя из документации https://redis.io/topics/sentinel, следящий сервис Redis Sentinel умеет выполнять следующие операции:

  1. Мониторинг: следящий узел проверяет доступность MASTER и SLAVE узлов и способен оправлять оповещения о недоступности узлов.
  2. Обеспечение отказоустойчивости: если MASTER-узел вышел из строя, следящий узел может перевести SAVE-узел в режим MASTER, а также перенастраивает остальные SLAVE-узлы, и они начинают работать с новым MASTER.
  3. Управление конфигурациями. Конфигурирует узлы MASTER и SLAVE.

Разместим для чистоты эксперимента следящий сервис на отдельную ВМ redis3. 

Подключаем аналогичным способом репозиторий Redis и устанавливаем пакет redis-sentinel:

apt install redis-sentinel 

После установки необходимо внести настройки в конфигурационный файл следящего узла /etc/redis/sentinel.conf:

# Настраиваем мониторинг узла redis1 по порту 6379. 
# Последняя цифра 1 - означает количество следящих узлов в кворуме, 
# мнение которых учитывается в случае необходимости изменить MASTER-узел.
# То есть можно поднять несколько следящих узлов, 
# которые будут выполнять мониторинг MASTER-узла.
sentinel monitor master01 redis1 6379 1

# Ждем 3 секунды, прежде чем принимать решение о сбое узла.
sentinel down-after-milliseconds master01 3000

# Зададим таймаут восстановления MASTER-узла
sentinel failover-timeout master01 6000

# Указываем сколько SLAVE-узлов необходимо перенастраивать одновременно. 
# Необходимо указывать минимальное количество, чтобы не получилось так, 
# что все реплики станут недоступны одновременно.
sentinel parallel-syncs master01 1

# Обязательно прописываем прослушиваемые порты.
bind 192.168.9.97 127.0.0.1 ::1

# И прописываем пароль MASTER-узла.
sentinel auth-pass master01 TestPass

Перезапустим сервис после внесения настроек:

root@redis3:/etc/redis# /etc/init.d/redis-sentinel restart
[ ok ] Restarting redis-sentinel (via systemctl): redis-sentinel.service.

Проверим, что следящий сервис увидел MASTER и SLAVE:

root@redis3:/etc/redis# redis-cli -p 26379 info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=master01,status=ok,address=192.168.9.95:6379,slaves=1,sentinels=1

Начинаем эксперименты.

Сымитируем сбой, остановим сервис redis-server на узле redis1 и получим ткущую информацию следящего узла:

root@redis3:/etc/redis# redis-cli -p 26379 info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=master01,status=ok,address=192.168.9.96:6379,slaves=1,sentinels=1

Видим, MASTER поменялся.

Восстановим работу узла redis1 и проверим его состояние: 

root@redis3:/var/log/redis# redis-cli -h redis1 -p 6379 -a TestPass info replication
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
# Replication
role:slave
master_host:192.168.9.96
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:15977
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:6c0c7d0eedccede56f211f2db74a98c4d0ff6d56
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:15977
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:15977

Видим, что узел получил роль SLAVE, а узел redis2 является MASTER-узлом.

Сымитируем сбой узла redis2 и проверим состояние следящего узла:

root@redis3:/var/log/redis# redis-cli -p 26379 info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=master01,status=ok,address=192.168.9.95:6379,slaves=1,sentinels=1

И состояние узла redis1:

root@redis3:/var/log/redis# redis-cli -h redis1 -p 6379 -a TestPass info replication
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
# Replication
role:master
connected_slaves:0
master_replid:6e9d67d6460815b925319c2bafb58bf2c435cffb
master_replid2:6c0c7d0eedccede56f211f2db74a98c4d0ff6d56
master_repl_offset:33610
second_repl_offset:26483
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:33610

Отлично, механизм работает. Но теперь возникла проблема, как мы будем подключать наши сервисы DirectumRX, ведь для них нужен адрес единственного узла. Решим вопрос с помощью сервисной службы HAProxy.

Проксирование обращения к узлам Redis

В качестве reverse-прокси для узлов Redis может выступать любой проксирующий tcp-сервис. В данной статье рассмотрим использование HAProxy поскольку это специализированный инструмент предназначенный для обеспечения высокой доступности и балансировки нагрузки и используется повсеместно известными онлайн сервисами. Подробнее о HAProxy можно почитать на странице разработчика http://www.haproxy.org/#desc

Установим HAProxy на узел redis3:

root@redis3:/var/log/redis# apt search haproxy

В конфигурационный файл HAProxy /etc/haproxy/haproxy.cfg, добавим настройки для проксирования запросов к узлам Redis:

…
frontend ft_redis
        bind *:6379 name redis
        mode tcp
        default_backend bk_redis

backend bk_redis
        mode tcp
        option tcp-check
        tcp-check connect
        # Не забываем, что все узлы принимают запросы только при наличии пароля.
        tcp-check send AUTH\ TestPass\r\n
        tcp-check expect string +OK
        tcp-check send PING\r\n
        tcp-check expect string +PONG
        tcp-check send info\ replication\r\n
        # Работаем только с MASTER, т.к. SLAVE по умолчанию работает только на чтение.
        tcp-check expect string role:master
        tcp-check send QUIT\r\n
        tcp-check expect string +OK
        server Redis1 redis1:6379 check inter 3s
        server Redis2 redis2:6379 check inter 3s

В данной конфигурации указано, что проксировать будем любые запросы, приходящие на все интерфейсы текущей виртуальной машины по адресу на порт 6379. Запросы будем передавать на узел, который ответит, что он имеет роль MASTER.

Перезапустим сервис haproxy:

root@redis3:/etc/haproxy# /etc/init.d/haproxy restart

Попробуем подключиться с помощью клиента redis-cli и создадим тестовый ключ:

root@redis3:/etc/haproxy# redis-cli -p 6379 -a TestPass
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:6379> SET TestKey "Some test string"
OK
127.0.0.1:6379> GET TestKey
"Some test string"
127.0.0.1:6379> info keyspace
# Keyspace
db0:keys=1,expires=0,avg_ttl=0

Остановим узел redis1 и запросим снова список ключей:

127.0.0.1:6379> info keyspace
Error: Server closed the connection
(3.01s)
127.0.0.1:6379> info keyspace
# Keyspace
db0:keys=1,expires=0,avg_ttl=0
(2.01s)

Видим, что на некоторое время соединение оборвалось, но затем подключение снова восстановилось и все данные остались на месте.

Теперь достаточно прописать в конфигурационных файлах сервисов DirectumRX адрес reverse-прокси для подключения к Redis.

Вместо заключения

При настройке на реальной инсталляции, необходимо, чтобы следящих сервисов было несколько (по правилам на каждом узле реплики) и HAProxy работал в режиме кластера. В данной статье кластеризация HAProxy рассматриваться не будет, мы рассмотрели только основные принципы настройки отказоустойчивого Redis для сервисов DirectumRX.

Если статья наберет больше 30 лайков, продолжим данную тему и рассмотрим настройку Redis Cluster, а также в продолжении рассмотрим настройки балансировщиков на базе HAProxy и Ngnix применимо для DirectumRX.

Дисклеймер 

Рекомендации представленные в статье не являются официальной документацией какой бы то ни было компании. Рекомендации предоставляются на основании личного опыта автора по настройке системы DirectumRX и серверных компонентов. Автор статьи не несет ответственности и не предоставляет гарантий в связи с публикацией фактов, сведений и другой информации, а также не предоставляет никаких заверений или гарантий относительно содержащихся здесь сведений. 

Алексей Зубин

Андрей, отличная статья!

Есть несколько вопросов:

1. судя из статьи получаем, что необходимо минимум 3 сервера для Redis и еще пара для HAProxy (тут кстати тоже возможно потребуется 3 следящий). Итого - минимум 5.

При этом внедрять планируется это все как минимум в отказоустойчивую/масштабируемую архитектуру DirectumRX, в которой итак будет минимум 4 сервера (2 на СУБД и 2 на сервер приложений). Возможно имеет смысл как-то совместить с уже используемыми серверами, тем более нагрузка на сервера с Redis судя по всему скорее низкая, чем высокая?

2. Есть ли опыт использования на продуктивных системах именно такой схемы отказоустойчивого Redis?

Андрей Ардашев

Алексей, спасибо за комментарий.

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

Частичное совмещение возможно, к примеру HAProxy можно совместить с балансировщиком, только если балансировщик не вынесен в DMZ. Поскольку Redis для Windows больше не поддерживается, лучше разворачивать Redis на машинах с Linux, но если в качестве СУБД используется PostgreSQL на Linux, то возможность развертывания там экземпляров Redis можно рассмотреть.

Андрей Ардашев: обновлено 30.08.2019 в 14:09
Андрей Ардашев: обновлено 30.08.2019 в 14:10
Алексей Зубин

>>однако в настоящее время проект остановил развитие на версии 3.0.504

немного поправлю: последняя актуальная версия для Windows 3.2.100, скачать можно по ссылке  https://github.com/MicrosoftArchive/redis/releases/download/win-3.2.100/Redis-x64-3.2.100.msi,

но таки да, на этом Redis на Windows приостанавливает свою историю. И если хочется полной функциональности, то надо переходить на Linux-версии (хотя для DirectumRX на текущий момент достаточно и функциональности Win-версии).

Со своей стороны тоже немного погружался в вопрос и пришел примерно к следующим выводам:

  • если хотим развернуть отказоустойчивый Redis один из вариантов разворачивания в Windows Failover Cluster, т.к. это по сути обычная служба (этот вариант видится еще более интересным, если используется СУБД от MS и она уже развернута в отказоустойчивом кластере);
  • второй вариант актуален если используется СУБД Postgre в Linux. Тут можно задействовать стороннее ПО, например, Pacemaker который может использоваться как для построения отказоустойчивой СУБД, так и умеет работать с тем же Virtual IP (тем самым исключая HAProxy из схемы), т.е. видится, что вариант должен быть более универсальным.

И тут было бы интересно узнать мнение сообщества:

используется ли у кого-то отказоустойчивый/масштабируемый Redis в компании. Если да, то какая архитектура реализована у вас?

Алексей Зубин: обновлено 30.08.2019 в 14:30
Андрей Ардашев

Алексей, спасибо за комментарий. Версию подправил в статье.

И да, согласен. Коллеги, если у кого-то есть опыт по настройкам Redis, пожалуйста отпишитесь в комментариях.

Андрей Ардашев: обновлено 30.08.2019 в 15:04
Роман Деменков

Postgres, elastic, redis, haproxy... Того и гляди следующий Директум будет на electron, nodejs и т. д. 

Денис Архипов

А разве Redis Cluster является решения для отказоустойчивости? Это же горизонтальное масштабирование сервиса и инструмент повышения производительности. И это явно указано в той же документации. Да он закрывает вопросы в том числе и по отказоустойчивости, но изначальная его цель в другом, а отказоустойчивость приятный бонус.

Андрей Ардашев

Денис, да, все верно. Реализуем горизонтальное масштабирование и одновременно отказоустойчивость. В случае DirectumRX Redis не используется для работы с нагруженными данными, ценным является именно отказоустойчивость за счет кластеризации.

Авторизуйтесь, чтобы написать комментарий