company_banner

Orchestrator и VIP как HA-решение для кластера MySQL

    В Ситимобил мы используем базу данных MySQL в качестве основного хранилища постоянных данных. У нас есть несколько кластеров баз данных под различные сервисы и цели.

    Постоянная доступность мастера является критическим показателем работоспособности всей системы и ее отдельных частей. Автоматическое восстановление кластера в случае отказа мастера сильно снижает время реагирования на инцидент и время простоя системы. В этой статье я рассмотрю схему обеспечения высокой доступности (HA) кластера MySQL на основе MySQL Orchestrator и виртуальных IP адресов (VIP).



    HA-решение на основе VIP


    Сначала кратко расскажу о том, что из себя представляет наша система хранения данных.

    Мы используем классическую схему репликации с одним мастером, доступным на запись, и множеством реплик, которые используются только на чтение. Кластер может содержать промежуточный мастер — узел, который одновременно является и репликой, и мастером для других. Клиенты обращаются к репликам через HAProxy, что позволяет равномерно распределять нагрузку и легко масштабироваться. Использование HAProxy обусловлено историческими причинами, и сейчас мы в процессе миграции на ProxySQL.

    Репликация выполняется в полусинхронном режиме на основе GTID. Это значит, что как минимум одна реплика должна записать транзакцию в журнал, прежде чем та будет признана успешной. Такой режим репликации обеспечивает оптимальный баланс между производительностью и сохранностью данных в случае выхода из строя главного узла. В основном все изменения передаются от мастера к репликам с помощью Row Based Replication (RBR), но часть узлов может иметь mixed binlog format.

    Оркестратор периодически обновляет состояние топологии кластера, анализирует полученную информацию и в случае возникновения проблем может запустить процедуру автоматического восстановления. За саму процедуру отвечает разработчик, поскольку её можно реализовать разными способами: на основе VIP, DNS, с использованием служб обнаружения сервисов (service discovery) или самописных механизмов.

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

    Что нужно знать об этом решении, прежде чем двигаться дальше:

    • VIP — это IP-адрес, который не привязан к конкретному физическому сетевому интерфейсу. При выходе узла из строя или при плановых работах мы можем переключить VIP на другой ресурс с минимальным временем простоя.
    • Освобождение и выдача виртуального IP-адреса — дешевые и быстрые операции.
    • Для работы с VIP требуется доступ к серверу по SSH, либо использование специальных утилит, например, keepalived.

    Рассмотрим возможные проблемы с нашим мастером и представим, как должен отработать механизм автоматического восстановления.

    Пропала сетевая связность до мастера, либо возникла проблема на уровне «железа», и сервер недоступен


    1. Оркестратор обновляет топологию кластера, каждая реплика сообщает о недоступности мастера. Оркестратор запускает процесс выбора реплики, подходящей на роль нового мастера, и начинает восстановление.
    2. Пытаемся снять VIP со старого мастера — безуспешно.
    3. Реплика переключается на роль мастера. Топология перестраивается.
    4. Добавляем новый сетевой интерфейс с VIP. Поскольку снять VIP не удалось, в фоновом режиме запускаем периодическую отправку запроса gratuitous ARP. Этот вид запроса/ответа позволяет обновить на подключенных коммутаторах таблицу соответствия IP- и MAC-адресов, тем самым уведомляя о переезде нашего VIP. Это минимизирует вероятность split brain при возврате старого мастера.
    5. Все новые соединения сразу же перенаправляются на новый мастер. Старые соединения завершаются неудачно, выполняются повторные обращения к БД на уровне приложения.

    Сервер работает в нормальном режиме, произошел отказ на уровне СУБД


    Алгоритм аналогичен предыдущему случаю: обновление топологии и запуск процесса восстановления. Так как сервер доступен, мы успешно освобождаем VIP на старом мастере, переносим его на новый и отправляем несколько ARP-запросов. Возможный возврат старого мастера не должен повлиять на перестроенный кластер и работу приложения.

    Другие проблемы


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

    Виртуальный сетевой интерфейс всегда добавляется временно, то есть после перезагрузки сервера VIP автоматически не назначается. Каждый экземпляр БД по умолчанию запускается в режиме только для чтения, оркестратор автоматически переключает новый мастер на запись и пробует установить read only на старом мастере. Эти действия направлены на уменьшение вероятности split brain.

    В процессе восстановления могут возникнуть проблемы, о которых стоит также уведомлять через UI оркестратора помимо стандартных средств мониторинга. Мы расширили REST API, добавив такую возможность (PR сейчас находится на рассмотрении).

    Общая схема HA-решения представлена ниже.



    Выбор нового мастера


    Оркестратор достаточно умён и старается выбрать наиболее подходящую реплику в качестве нового мастера по следующим критериям:

    • отставание реплики от мастера;
    • версия MySQL мастера и реплики;
    • тип репликации (RBR, SBR или mixed);
    • расположение в одном или разных дата-центрах;
    • наличие errant GTID — транзакции, которые были выполнены на реплике и отсутствуют на мастере;
    • также учитываются пользовательские правила выбора.

    Не каждая реплика является идеальным кандидатом на роль мастера. Например, реплика может использоваться для резервного копирования данных, либо сервер имеет более слабую конфигурацию «железа». Оркестратор поддерживает ручные правила, с помощью которых можно настроить свои предпочтения по выбору кандидата от наиболее предпочтительных до игнорируемых.

    Время реагирования и восстановления


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

    • slave_net_timeout — количество секунд, в течение которых реплика ожидает поступления новых данных или heartbeat-сигнала от мастера, прежде чем соединение признается потерянным и выполняется переподключение. Чем меньше значение, тем быстрее реплика сможет определить, что связь с мастером нарушена. Мы устанавливаем это значение равным 5 секундам.
    • MASTER_CONNECT_RETRY — количество секунд между попытками переподключения. В случае сетевых проблем низкое значение этого параметра позволит быстро переподключиться и предотвратить запуск процесса восстановления кластера. Рекомендуемое значение — 1 секунда.
    • MASTER_RETRY_COUNT — максимальное количество попыток переподключения.
    • MASTER_HEARTBEAT_PERIOD — интервал в секундах, после которого мастер отправляет heartbeat-сигнал. По умолчанию равен половине значения slave_net_timeout.

    Параметры оркестратора:

    • DelayMasterPromotionIfSQLThreadNotUpToDate — если равен true, то роль мастера не будет применена на реплике-кандидате до тех пор, пока SQL-поток реплики не выполнит все непримененные транзакции из Relay Log. Мы используем эту опцию, чтобы не терять транзакции в условиях отставания всех реплик-кандидатов.
    • InstancePollSeconds — частота построения и обновления топологии.
    • RecoveryPollSeconds — частота анализа топологии. В случае обнаружения проблемы запускается восстановление топологии. Это константа, равная 1 секунде.

    Каждый узел кластера опрашивается оркестратором один раз в InstancePollSeconds секунд. При обнаружении проблемы состояние кластера принудительно обновляется, а затем принимается окончательное решение о выполнении восстановления. Экспериментируя с различными параметрами БД и оркестратора, нам удалось снизить длительность реагирования и восстановления до 30 секунд.

    Тестовый стенд


    Тестирование HA-схемы мы начали с разработки локального тестового стенда и дальнейшего внедрения в тестовое и боевое окружения. Локальный стенд полностью автоматизирован на основе Docker и позволяет экспериментировать с конфигурацией оркестратора и сети, масштабировать кластер от 2-3 серверов до нескольких десятков и проводить учения в безопасной среде.

    Во время учений мы выбираем один из методов эмуляции проблемы: мгновенно отстрелить мастер с помощью kill -9, мягко завершить процесс и остановить сервер (docker-compose stop), имитировать проблемы с сетью с помощью iptables -j REJECT или iptables -j DROP. Мы ожидаем такие результаты:

    • оркестратор обнаружит проблемы с мастером и обновит топологию не более чем за 10 секунд;
    • автоматически запустится процедура восстановления: изменится сетевая конфигурация, роль мастера перейдёт к реплике, топология перестроится;
    • новый мастер станет доступен для записи, живые реплики не будут потеряны в процессе перестроения;
    • данные начнут записываться в новый мастер и реплицироваться;
    • общее время восстановления составит не более 30 секунд.

    Как вы знаете, система может вести себя по-разному в тестовом и production-окружениях из-за разной конфигурации «железа» и сети, различий в синтетической и реальной нагрузке и т.д. Поэтому периодически мы проводим учения в реальных условиях, проверяя, как ведет себя система при потере сетевой связности или деградации ее отдельных частей. В будущем хотим построить полностью идентичную инфраструктуру для обеих сред и автоматизировать ее тестирование.

    Выводы


    Работоспособность главного узла системы хранения данных является одной из основных задач команды SRE и эксплуатации. Внедрение оркестратора и HA-решения на основе VIP позволило добиться следующих результатов:

    • надежное обнаружение проблем с топологией кластера БД;
    • автоматическое и быстрое реагирование на инциденты, связанные с мастером, что снижает время простоя системы.

    Однако решение имеет свои ограничения и недостатки:

    • масштабирование HA-схемы на несколько ЦОДов потребует наличия единой L2-сети между ними;
    • прежде чем назначить VIP на новом мастере, нам нужно освободить его на старом. Процесс является последовательным, что увеличивает время восстановления;
    • освобождение VIP требует SSH-доступа к серверу, либо любого другого способа вызова удаленных процедур. Поскольку сервер или БД испытывает проблемы, вызвавшие процесс восстановления, мы не можем быть уверены, что снятие VIP завершится удачно. А это может привести к появлению двух серверов с одинаковым виртуальным IP-адресом и проблеме split brain.

    Чтобы избежать split brain, можно использовать метод STONITH («Shoot The Other Node In The Head»), который полностью изолирует или отключает проблемный узел. Существуют и другие способы реализации высокой доступности кластера: комбинация VIP и DNS, обнаружение служб и прокси-сервисы, синхронная репликация и прочие способы, которые имеют свои недостатки и преимущества.

    Я рассказал о нашем подходе к созданию отказоустойчивого кластера MySQL. Он прост в реализации и обеспечивает приемлемый уровень надежности в текущих условиях. По мере развития всей системы в целом и инфраструктуры в частности этот подход, несомненно, будет эволюционировать.
    Ситимобил
    Компания

    Комментарии 41

      0
      А можно добавить информацию почему не выбрали galera?
        0
        Тут существует несколько причин.

        1) Кадровая — людей, которые умеют траблшутить галеру на рынке достаточно мало.
        2) Галера работает на основе синхронной репликации. То есть в случае замедления работы на одном из серверов, это будет сказываться на всем кластере. Да и в штатном режиме такой режим работы медленнее. При этом наше ПО адекватно реагирует на вменяемое отставание реплик.

        Когда мы начнем задумываться об отказоустойчивости по ДЦ, то мы обязательно вернемся к этому вопросу. Пока думаем, что наши проблемы решаются без получения дополнительной головной боли.
          0
          Да, синхронность галеры может доставить проблем там где нужна скорость. Но вроде как у вас сервис как раз таки требующий надежности хранения данных. Кроме того по идее можно иметь группу нод галеры + слейвы, то есть тормоза будут только на запись. Ну и получается что если сейчас возникнет проблема с ДЦ то сервис склеивает лапки, так?
            0
            На самом деле надежность хранения не сверхкритична. Поездки на такси более-менее стейтлесс штука. Нам куда важнее при непредсказуемом росте нагрузки осуществлять поездки.

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

            На случай ухода ДЦ у нас есть свои механизмы восстановления работоспособности (пока с небольшим даунтаймом), но об этом в следующих сериях ;-)
              0
              Ну и да — с точки зрения надежности уход мастер-базы у нас не приводит к потере транзакций. Полусинхронная репликация же.
                0
                Важно иметь баланс между производительностью и надежностью.

                По галере: для внедрения нужен как минимум опыт в команде по её использованию и сопровождению в продакшене под нагрузкой. Галера накладывает ряд ограничений на схему, допустимые операции, локи + обычно всплывают подводные камни во время использования такого рода систем. Поэтому решение выбираем, исходя из текущих реалий (об этом можно прочитать у Ивана в статье) и тем как быстро и на приемлемом уровне можем устранить SPOF.
                  0

                  А почему не решения на Consul KV для переключения типа как тут к примеру https://blog.pythian.com/mysql-high-availability-with-proxysql-consul-and-orchestrator/ ?

                    0
                    Есть в планах как дальнейшее улучшение схемы, но сначала собираемся интегрировать ProxySQL.
                      0
                      А сейчас без проксиsql какие проблемы или неудобства возникают?
                      И сколько у вас мастеров если не секрет?
                        0
                        Сейчас 5. Без proxysql сложно наращивать кластер web-серверов из-за линейного роста числа коннектов к базам.
                          0
                          А переназначение мастеров будете и в планах делать скриптами вызываемые оркестратором?
                            0
                            Возможно, какая-то логика в скриптах останется. Но основная часть переедет на Consul и его шаблоны.
                            0
                            А сейчас у вас в конечных приложениях как стоит куда подключаться? К одному ипу?
                              0
                              Когда нужно цепляться к мастеру — VIP конкретного мастера. Когда нужно цепляться к слейву — локальный haproxy, который уже балансит по слейвам. Хотя вобще-то лучше к локальному proxysql, задача на это есть в бэклоге, но пока не приоритет.
                                0
                                А на уровне приложения они по DNS имени коннектиться и если да то время переключения как контролируете?
                                  0
                                  По IP
                          0
                          с proxysql был негативный опыт, когда вдруг прокси перевел ноду из read-only в read-write и поломанная репликация в итоге
                            0

                            А удалось выяснить причину такого поведения?

                              0
                              Нет, так как из прода это сразу убрали, а на форуме proxysql по данной проблеме ничего особо не посоветовали. Кстати вспомнил что на предыдущей работе стоял haproxy для мускула и тоже пришлось отказаться — haproxy вносил ощутимые тормоза.
                                0
                                А какая там была версия и когда по времени это было?
                                  0
                                  Было в прошлом году, версия proxysql 2.0.2
                                  Были созданы две группы, только-чтение для слейва и чтение-запись для мастера и несколько правил для роутинга запросов. Все работало примерно месяц, пока не случился факап.
                                  UPD. Зашел к ним на гитхаб и сразу вижу такое github.com/sysown/proxysql/issues/2592
                                    0
                                    У вас какие то логи или что то осталось?
                                      0
                                      Увы, ничего не осталось.
                                        0
                                        И какое другое решение сейчас используете?
                                          0
                                          Был pacemaker, но тоже убрали. Ничего не используем сейчас, тупо мастер слейв :)
                0
                Повесив вип на мастер вы создали себе головную боль сплитбрейнов сами. А могли бы поставить 2 или более хапрокси (лучше proxysql), на них вип и проксировать к мастеру, а у всех нод мускуля свои статичные адреса (лучше днс) и отстрел (из балансировщика и конфигов других нод мускуля) даже если стонит нельзя реализовать. Балансировщики stateless, их можно свободно уничтожать и поднимать и сервис за ними будет избавлен от более сложных в диагностике и исправлений ошибок. Так же получить зомби машину на более простом сервисе, как балансировщик, сложнее, что поднимет стабильность.
                  0

                  Мы сейчас изучаем и постепенно внедряем ProxySql. Когда решение отработаем, планируем перейти на Consul+Orchestrator+Proxy.
                  По splitbrain: ситуация возможна, если не смогли зайти по ssh на сервер и он не перезагружался. На этот случай подстелили соломку в виде бесконечного arping + ETA дежурного инженера около 3 минут. Эти риски сейчас приемлемы в сравнении с получаемыми преимуществами

                    0

                    Убрать риски и принять их это разные вещи. Я говорил о том что с помощью технологий рисков можно избежать.

                  0
                  А о vitess.io не думали? Очень интересный инструмент.
                    0
                    Сейчас используется в сервисе чатов для шардирования. Vitess имеет интеграцию с оркестратором для обеспечения отказоустойчивости. Но всё же vitess и оркестратор решают разные задачи.
                      0
                      Ну можно же полноценно использовать Vitess без оркестратора? Или отказоустойчивость штатными средствами Vitess будет недостаточно?

                      Я ещё не до конца дочитал документацию по Vitess, но раз уж вы его используете, то такой вопрос:
                      Умеет ли vitess.io без Orchestrator автоматически сделать слейв мастером в случае сбоя мастера?
                        0

                        Использовать, конечно, можно. Но для переключения нужно использовать оркестратор. Вот из документации:


                        High availability: Vitess integrates with Orchestrator, which is capable of performing a failover to a new master within seconds of failure detection. This is usually sufficient for most applications.
                          0
                          Спасибо. Исходя из информации на сегодняшний день, стоит ли полностью переходить на Vitess?
                            0
                            Зависит от задачи, может вам шардирование и не нужно. Лично не знаком с витессом, но коллеги вроде довольны, хорошо справляется со своей основной функцией — шардированием данных в MySQL
                              0
                              До шардирования пока ещё далеко, но вот при выборе кластера, к примеру, Vitess может быть заменителем Percona XtraDB Cluster или только работать поверх него (обслуживать запросы)?
                    0
                    parshinpn, добрый день.

                    Я вижу, что здесь Вы указываете Orchestrator-у считывать ClusterAlias из БД.

                    "DetectClusterAliasQuery": "select ifnull(max(cluster_name), '') as cluster_alias from meta.cluster where anchor=1",


                    Значит существует таблица cluster в базе meta, которая реплицируется на другие хосты. В этой таблице хранится ClusterAlias одинаковый для всех хостов.

                    Вот здесь у Вас используется ClusterAlias.

                    Скажите, пожалуйста, не сталкивались ли Вы с такой ситуацией, когда Orchestrator присваивал ClusterAlias произвольным хостам, которые были вынесены в отдельные кластеры в процессе FailoverProcesses?

                    Что можете посоветовать в вот такой ситуации?
                    github.com/openark/orchestrator/issues/1132

                      0

                      Привет,
                      Да, мы используем ClusterAlias, создавая БД meta и реплицируя ее на все узлы кластера — согласно документации оркестратора.


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


                      Правильно ли я понял, что во время failover у вас кластер из 3 нод развалился на 3 отдельных нереплицирующихся узла?

                        0

                        С приведенным поведением в issue ни разу не доводилось встречаться. Хотя мы при тестировании и iptables, и kill пользовались. Возможно, это связанно с использованием консула

                          0
                          Правильно ли я понял, что во время failover у вас кластер из 3 нод развалился на 3 отдельных нереплицирующихся узла?


                          Да, но проблема не в этом. Это не просто 3 отдельных нереплицирующихся узла, а в понимании оркестратора 3 отдельных кластера у каждого по 1 нереплицирующемуся узлу.

                          У каждого из этих кластеров есть свои ClusterName и ClusterAlias:

                          1) Старый мастер, ClusterName = 10.0.0.31:3306, ClusterAlias = 10.0.0.31:3306
                          2) Новый мастер, ClusterName = 10.0.0.32:3306, ClusterAlias = testcluster
                          3) Реплика, которая не смогла подключиться, ClusterName = 10.0.0.33:3306, ClusterAlias = 10.0.0.33:3306

                          При такой схеме если мы попросим оркестратора отдать адрес мастера testcluster, он вернет 10.0.0.32:3306 и это врено.

                          Проблема заключается в том, что Оркестратор через некоторое время простоя без каких либо причин присвоил ClusterAlias = testcluster кластеру с репликой, то есть другому кластеру.

                          После этого картина изменилась следующим образом:

                          1) Старый мастер, ClusterName = 10.0.0.31:3306, ClusterAlias = 10.0.0.31:3306
                          2) Новый мастер, ClusterName = 10.0.0.32:3306, ClusterAlias = 10.0.0.32:3306
                          3) Реплика, которая не смогла подключиться, ClusterName = 10.0.0.33:3306, ClusterAlias = testcluster

                          А при такой схеме если мы попросим оркестратора отдать адрес мастера testcluster, он вернет 10.0.0.33:3306 и это ошибка!

                          И это не противоречит его логике.

                          Понятное дело, что 1 кластер развалился на 3 отдельных и требуется вмешательство администратора. Но реакция администратора может занять определенное время, за которое оркестратор уже создал проблему.

                          Зависит ли Ваша система от связи ClusterAlias и IP адреса, который хранится под этим ClusterAlias у оркестратора?
                            0
                            ClusterAlias мы используем для именования кластеров в оркестраторе. На основе этих имен мы также:
                            — используем имя в failover (на основе его определяем VIP, SSH параметры и т.п для этого кластера)
                            — используем имя в кронах выдачи приоритетов репликам по выбору мастера.

                            Все внешние клиенты ходят на мастер по VIP (сейчас через ProxySQL), на реплики через HAProxy. Также у оркестратора есть anti-flapping: когда ставится блокировка на failover, у нас она составляет один час.

                            Если вашу ситуацию можно стабильно воссоздать на тестовом стенде, то я мог бы посмотреть более детально.
                              0
                              Баг «плавающий», но можете попробовать. Всю необходимую информацию о конфигурации можем предоставить.
                              Обсуждение данной проблемы и ответ разработчика orchestrator на github.com/openark/orchestrator/issues/1132

                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                      Самое читаемое