Введение

Привет! Меня зовут Иван Откидач, я DevOps-инженер в команде DBA. Моя основная специализация — NoSQL-базы данных, в частности Redis и MongoDB. С каждым месяцем количество Redis, находящихся на нашей поддержке, растёт, поэтому обеспечение их стабильной и надёжной работы — один из приоритетов нашего подразделения. В этой статье мы разберем, как устроен механизм отказоустойчивости в Redis Cluster и почему он может давать сбои в multi-AZ-инфраструктуре. Также покажем один из практических подходов к решению этой проблемы.

Основные схемы развертывания Redis:

  • Single instance — как один узел без встроенной отказоустойчивости (мы с таким обычно не работаем).

  • Sentinel — как решение для автоматического failover без шардирования (аналогично роли Patroni в PostgreSQL HA-топологии)

  • Redis Cluster — распределённый кластер с шардированием данных и автоматическим переключением master-нод.

Типы развертывания Redis
Типы развертывания Redis

Redis у нас запускаются как systemd-сервисы на VM во внутреннем облаке компании, о котором можно почитать тут: Как мы спроектировали и запустили собственную облачную платформу на 20К виртуальных машин — опыт Wildberries / Хабр

VM, на которых работают наши Redis Cluster и Redis Sentinel, поровну распределены между тремя зонами доступности, что, как кажется на первый взгляд, должно по умолчанию обеспечивать высокую доступность при падении одной из Availability Zone. Для Redis Sentinel в нашей инфраструктуре всё действительно так. А вот в Redis Cluster есть нюанс.

В multi-AZ-инфраструктуре Redis Cluster не всегда обеспечивает автоматическое восстановление после отказа зоны доступности, даже если у каждого мастера есть по две реплики. Причина в том, что механизм failover в Redis Cluster завязан не только на наличии replica-нод, но и на кворуме мастеров: для выборов нового мастера требуется большинство (больше половины) master-узлов кластера. Если при потере одной AZ кластер лишается этого большинства, автоматический failover блокируется, а часть hash-слотов остаётся без обслуживающих мастеров. Это штатное поведение, заложенное в Redis как защита от split-brain, а не сбой логики кластера (redis.io).

Именно эта практическая проблема стала отправной точкой для данной статьи. Её цель — не только подсветить сообществу проблему, почему Redis Cluster оказывается неустойчивым при неудачном распределении master-нод между зонами, но и показать одно из возможных решений: сервис, который я разработал для автоматического переключения мастеров. Далее разберём, как работает стандартный failover в Redis Cluster, в какой момент он перестаёт справляться самостоятельно и каким образом внешний сервис может взять на себя контроль стейта, когда встроенного механизма кластера уже недостаточно.

Архитектура Redis Cluster с точки зрения отказоустойчивости

Redis Cluster хранит данные не на одном сервере, а распределяет их между несколькими master-узлами. Для этого всё пространство ключей делится на 16 384 hash-слота, и каждый слот закрепляется за конкретным master. Replica-узлы держат копии мастеров и нужны прежде всего для отказоустойчивости: если master выходит из строя, одна из его реплик может занять его место.

Как работает автоматический failover

Автоматическое восстановление в Redis Cluster работает не само по себе, а через процедуру failover. Сначала кластер должен признать master недоступным, после чего одна из его реплик пытается стать новым master. Но для этого ей недостаточно просто «увидеть» проблему: повышение должно быть подтверждено большинством master-узлов кластера. Иными словами, Redis Cluster опирается на кворум мастеров — больше половины от их общего числа. Без такого большинства новый master не может быть легитимно выбран (redis.io).

Пример потери AZ и невозможности восстановиться

Именно здесь появляется основная проблема multi-AZ-размещения. Если больше половины master-узлов оказалось в одной зоне доступности, то потеря этой AZ означает потерю кворума. Реплики в оставшихся зонах могут быть живы, данные на них могут быть актуальны, но автоматически восстановить кластер они не смогут: у них нет достаточного числа голосов, чтобы завершить failover.

Неотказоустойчивое распределение ролей в Redis Cluster на примере трех шардов
Неотказоустойчивое распределение ролей в Redis Cluster на примере трех шардов

Пример выглядит так: в кластере есть 3 masters и 6 replicas. Если 2 masters размещены в одной AZ, а эта зона падает, то в системе остаётся только 1 master из 3. Для кворума нужно как минимум 2 голоса, поэтому автоматический failover становится невозможен. В результате часть hash slots остаётся без владельца, и кластер не может нормально обслуживать запросы. В зависимости от конфигурации, это может приводить к полной остановке обработки запросов на уровне всего кластера.

Для кластера нет сущности «зона доступности» — он опирается только на сетевую связность между узлами, обмен служебными сообщениями и кворум master-нод. Узлы обмениваются состоянием через cluster bus и gossip protocol, но эта логика ничего не знает о том, находятся ли два мастера в одной AZ или в разных. Поэтому с точки зрения Redis Cluster два мастера в одной зоне и два мастера в разных зонах — это одинаковая ситуация, пока между ними есть сеть.

Из этого вытекает второе ограничение: Redis сам не гарантирует равномерное распределение masters между зонами доступности. Кластер умеет хранить карту hash-слотов, проводить выборы и продвигать реплики, но не следит за тем, чтобы ни одна AZ не получила большинство master-узлов.

Именно поэтому multi-AZ-размещение в Redis Cluster не равно multi-AZ-отказоустойчивости. Само по себе распределение нод по трём зонам ещё не гарантирует, что кластер переживёт потерю одной из них. Нужно отдельно контролировать топологию master-узлов. Такой подход прямо описан в материалах Redis для multi-zone deployment, но это уже требование к архитектуре и эксплуатации, а не встроенное свойство Redis Open Source Cluster.

Отказоустойчивое распределение ролей в Redis Cluster на примере трех шардов
Отказоустойчивое распределение ролей в Redis Cluster на примере трех шардов

Формализация проблемы

Пусть N — общее количество master-узлов в Redis Cluster. Тогда для автоматического failover нужно, чтобы в живых оставалось большинство мастеров:

alive_masters > N / 2

Идея решения: внешний AZ-aware контроллер (наш сервис — Redis Auto Failover)

Open Source решений подобной проблемы мне найти не удалось, но, предполагаю, у основных публичных cloud-провайдеров используются похожие самописные механизмы.

Наше решение — сервис Redis Auto Failover (RAF).

После изучения проблемы стало понятно, что подходящего open source-инструмента под нашу задачу фактически нет, поэтому я начал пилить собственный сервис. Так появился RAF — внешний AZ-aware контроллер, который следит за размещением master-узлов и помогает заранее приводить кластер в устойчивое состояние. Но до продакшена путь был не быстрым: мы долго гоняли сервис на dev-контуре, моделировали разные аварийные сценарии, проверяли корректность failover и дорабатывали логику так, чтобы решение было не просто рабочим, а действительно безопасным для эксплуатации.

Архитектура сервиса (детали и принцип работы сервиса)

RAF работает как управляющий контур над Redis Cluster: он периодически собирает снимок состояния, оценивает текущее размещение ролей, строит план безопасных переключений и выполняет его по шагам. Сервис опирается на два источника данных: корпоративный мониторинг и штатный Redis Cluster API. Из мониторинга он получает топологию, роли узлов, данные о мастерах и репликах, а также принадлежность каждого узла к зоне доступности. Используются встроенные механизмы failover Redis Cluster, без модификации его внутренней логики.

Откуда берётся информация о зоне

Привязка узла к AZ берётся из инфраструктурных метаданных, доступных через корпоративный мониторинг. Для сервиса это внешний атрибут, который затем сопоставляется с текущими ролями узлов внутри Redis Cluster.

Как получаем cluster state

Фактическое состояние кластера сервис читает из Redis Cluster API: ему нужен актуальный снимок ролей и связей master-replica, достаточный для анализа и подготовки failover. Для самого переключения используется стандартная команда cluster failover, причём в graceful-режиме.

Как выбираем кандидата на promotion

Кандидат выбирается среди реплик нужного shard. Приоритет у такого варианта, который исправляет текущее размещение masters с минимальным числом переключений и без лишнего риска для кластера. Перед включением узла в план сервис дополнительно проверяет состояние кластера, доступность текущего мастера и будущего мастера, а также соблюдение допустимого уровня replication lag.

Этапы работы

1. Snapshot состояния

Сервис собирает единый снимок: топологию из мониторинга, AZ каждого узла, данные о мастерах и репликах, а также информацию, необходимую для подключения к Redis. Результат этого этапа — согласованная модель кластера, пригодная для анализа.

2. Анализ

На снимке состояния сервис проверяет текущее распределение master-ролей

3. План действий

Если требуется коррекция, RAF формирует proposals — набор предложений переключений, который должен исправить размещение мастеров. План оптимизируется по трём критериям: минимум failover-операций, минимум риска деградации и недопущение сценариев, ухудшающих устойчивость кластера.

4. Применение

План исполняется строго последовательно: одновременно допускается только одно переключение, а между операциями могут выдерживаться паузы, заданные конфигурацией сервиса. Непосредственно переключение выполняется через cluster failover в graceful-режиме.

5. Верификация

После каждого failover сервис проверяет, что кластер перешёл в ожидаемое состояние: новый master доступен, реплики синхронизированы, а итоговое распределение ролей соответствует целевому.

6. Алерты & Мониторинг

На все отклонения в работе сервиса настроен алертинг, уведомления генерируются при ошибках pre-check, ошибках failover и превышении лимитов или таймаутов. Дополнительно RAF экспортирует базовые метрики в формате Prometheus.

Верхнеуровневая схема работы R.A.F
Верхнеуровневая схема работы R.A.F

Влияние на пользователей

У RAF есть неизбежный «побочный эффект»: он не лечит Redis Cluster безболезненно, а осознанно увеличивает частоту управляемых failover (пусть и контролируемых), а значит — частоту кратковременных окон деградации для клиентов. Даже «graceful» CLUSTER FAILOVER включает фазу, когда старый мастер останавливает обработку запросов клиентов, а затем начинает отвечать редиректами на новый мастер.

Для правильно настроенного cluster-aware клиента это обычно выглядит как короткие таймауты/ретраи и 1–2 лишних RTT на часть запросов (на первых операциях после смены мастера на затронутых слотах). 

Наиболее болезненны при переключениях: транзакции и пайплайны, PUB/SUB, Lua-скрипты (EVALSHA) и блокирующие команды (очереди/стримы). Эти режимы завязаны на состояние соединения и/или на то, что выполнение привязано к конкретной ноде, поэтому при смене мастера чаще проявляются сомнительные результаты (неясно, выполнилась операция или нет), необходимость ручного восстановления контекста и риск дубликатов при ретраях. 

Подводим итоги

Отказоустойчивость в Redis Cluster — это компромисс: между автоматизацией и контролем, между устойчивостью к авариям и ценой дополнительных переключений для клиентов. RAF не устраняет его, но делает управляемым: сервис осознанно выполняет controlled failover там, где это нужно, чтобы заранее вывести кластер из опасной топологии и снизить риск длительной недоступности при потере AZ.

Для меня эта разработка — не просто техническое решение, а пример того, как инфраструктурная инженерия может напрямую влиять на надёжность бизнеса. По нашим подсчетам, сервис сэкономит компании до 200.9 млн.р в год. Я горжусь тем, что удалось не только закрыть реальный архитектурный пробел Redis Cluster в multi-AZ-сценарии, но и потенциально сэкономить компании значительные деньги, уменьшив риск простоев критичных систем.