company_banner

Fault tolerance: как предоставлять надежный сервис в случае сбоя оборудования

    Обеспечение устойчивости к сбоям — задача нетривиальная. Для нее нет стандартного решения. Есть некие общие паттерны, компоненты. Но даже в рамках одной организации для обеспечения фолт-толерантности разных узлов применяются разные решения. Что уж говорить о сравнении подходов в разных организациях.

    Кто-то оставляет проблему на «авось», кто-то вешает баннер на «пятисотку» и пытается зарабатывать на сбоях деньги. Кто-то пользуется стандартными решениями от поставщиков баз данных или сетевых устройств. А кто-то уходит в модные нынче «облака».



    Ясно одно — по мере роста бизнеса обеспечение устойчивости к сбоям (даже не процедур восстановления после сбоев) становится всё более острой проблемой. От количества аварий в год начинает зависеть репутация компании, при больших временах простоя становится неудобно пользоваться сервисом, и т.д. Причин много.

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


    Обычно архитектура веб-приложения устроена следующим образом (и наша архитектура не является исключением):



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

    Основные трудности начинаются на уровне хранения данных. Главная задача этой подсистемы — сохранить и приумножить информацию, необходимую для функционирования всей системы в целом. Часть этих данных можно иногда терять, а остальное является невосполнимым, и их потеря означает фактически смерть проекта. В любом случае, если данные частично потеряны, повреждены или временно недоступны, работоспособность системы резко снижается.

    На самом деле, конечно, всё намного сложнее. При отказе веб-сервера система обычно не возвращается в предыдущую точку. Связано это с тем, что в рамках одной бизнес-транзакции необходимо внести изменения в нескольких различных хранилищах (при этом механизм распределенных транзакций в слое хранилищ данных обычно не реализован, да и слишком дорог в реализации и эксплуатации). Другими возможными причинами расхождения в данных являются ошибки в программном коде, неучтенные сценарии, безалаберность разработчиков и администраторов («человеческий фактор»), жесткие ограничения на сроки разработки и ещё куча других важных и неважных причин, по которым программисты пишут не совсем идеальный код. Но в большинстве ситуаций это и не нужно (не все же приложения пишутся для банковского и финансового сектора). Либо система автоматически умеет восстанавливать консистентность своих данных, либо существуют полуручные средства восстановления системы после сбоев, либо даже и этого не требуется (пример: пользователь отредактировал свои настройки, но из-за сбоя часть настроек не изменилась. В большинстве случаев это не смертельно, особенно если интерфейс честно скажет пользователю, что не смог применить изменения).

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

    В качестве базы данных в последние годы мы используем Тарантул (это наш опенсорсный проект, очень быстрая, удобная база данных, которая легко расширяется при помощи хранимых процедур. См. сайт http://tarantool.org).

    Итак, наша цель — максимальная доступность данных. Тарантул как программное обеспечение проблем обычно не вызывает, т. к. он стабилена, нагрузка на сервер хорошо прогнозируется, сервер не уйдет неожиданно из-за возросшей нагрузки. Перед нами встает проблема надежности оборудования. Иногда серваки сгорают, диски сыплются, стойки вырубаются, маршрутизаторы выходят из строя, линки к датацентрам пропадают… А нам нужно, несмотря ни на что, обслуживать наших пользователей.

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

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

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

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

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

    Не могу не рассказать об одном повсеместно используемом у нас компоненте, который у нас стоит на каждом сервере. Мы его называем Капрон. Капрон выполняет функции мультиплексора запросов к базам данных (MySQL и Tarantool), поддерживая с ними пул постоянных соединений, инкапсулирует в себе всю информацию о конфигурации баз данных, шардинге и балансировке нагрузки. Капрон позволяет скрывать особенности протокола БД, предоставляя своим клиентам более простой и ясный интерфейс. Очень удобная штука. И идеальный кандидат для помещения туда описанной ранее логики.

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

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



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

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

    Мы планируем не останавливаться на достигнутом и попытаемся устранить некоторые очевидные недоработки:

    1) В случае сетевого таймаута, мы пробуем послать запрос в реплику. В случае модифицирующего запроса это напрасный труд, т. к. реплика не сможет его обработать. Имеет смысл не посылать в реплику априори модифицирующие команды. Решается на уровне ручного конфигурирования типов команд.

    2) Сейчас сетевой таймаут не означает, что данные на мастере не будут изменены. Хоть мы и вернем клиенту, что не смогли обработать его запрос. Справедливости ради стоит отметить, что данный недостаток присутствовал и раньше. Ситуация может быть исправлена за счет введения на стороне Тарантула ограничения на время обработки команд (если изменения не применены за указанное время, они автоматически откатываются, и запрос фэйлится). Выставляем таймаут на обработку 1 секунду, а сетевой таймаут в Капроне — 2 секунды.

    3) Ну и конечно, ждем от разработчиков Тарантула синхронную мастер-мастер-репликацию. Это позволит равномерно распределить запросы между несколькими мастер-копиями и успешно обрабатывать запросы даже в случае недоступности одного из серверов!

    Дмитрий Исайкин, ведущий разработчик Почты Mail.Ru
    Mail.Ru Group 691,03
    Строим Интернет
    Поделиться публикацией
    Комментарии 20
      +2
      А сие чудо (капрон) — это часть tarantool или существует в отдельном виде? Opensource? Хотелось-бы посмотреть на нее в контексте балансировки MySQL.
        +5
        Это отдельное приложение. Не опенсорс. Возможно, когда-нибудь вынесем в опенсорс, но пока там внутри слишком много нашей внутримейлрушной бизнес-логики. Балансировка MySQL пока не поддерживается.
        0
        А как вы определяете доступность сервера?
          +1
          Успел или нет сервер уложиться в таймаут.
          0
          Недостаток такой архитектуры — наличие единой точки отказа в виде лоад бэлэнсера, который у вас называется Капроном. Если упадет Капрон, упадет вся система. Вероятность, что упадет сервер с Капроном, грубо говоря, такая же, что и вероятность, что упадет один из слейвов. Или я не понял и у каждого веб сервера свой собственный Капрон?
            0
            Да, у каждого сервера запущен свой процесс Капрона. Это локальная прокся.
              0
              Тогда понятно.

              А данные (каждого пользователя, например) хранятся всегда только на одном сервере или дублируются на N серверов для надёжности?
                0
                Все данные дублируются минимум на двух серверах (мастер и реплика).
                  0
                  Если в результате какого-то сбоя данные на мастере будут отличаться от тех же данных на слейве, восстановление консистентности будет происходить в автоматическом режиме (и если да, то как?) или это надо будет делать вручную?
                    0
                    Синхронизация данных выполняется автоматически на стороне тарантула. Каждое состояние базы данных имеет свой числовой идентификатор. При изменении состояния идентификатор инкрементируется. На реплике крутится процесс синхронизации, который запрашивает и применяет к своей копии данных все изменения, произошедшие с мастером (изменения применяются последовательно, при этом наследуется идентификатор состояния).
                    В случае сетевых или других проблем синхронизация может произойти позднее, но в конце концов обязательно происходит.
            0
            Выставляем таймаут на обработку 1 секунду, а сетевой таймаут в Капроне — 2 секунды.


            Что если мастер применит апдейт, но ответ веб-серверу дойдет позже, чем 2 секунды (лаг сети, gc)?

            ждем от разработчиков Тарантула синхронную мастер-мастер-репликацию


            Я правильно понял, что ответ веб-серверу не возвращается, пока изменение не применено на обоих мастерах? Тогда для отказа системы достаточно падения линка между мастерами. Как планируете обрабатывать такую ситуацию?
              0
              Что если мастер применит апдейт, но ответ веб-серверу дойдет позже, чем 2 секунды (лаг сети, gc)?

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

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


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

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

                  В текущей схеме все операции чтения идут на мастер, а реплика простаивает. Операции записи производятся и на мастере, и на реплике, просто на реплику они доезжают с задержкой.
                  В случае синхронной репликации чтение можно будет производить с любого сервера и нагрузка от чтения поделится между ними поровну. Запись будет по-прежнему производиться на обоих серверах, но уже синхронно. Это увеличит время, затрачиваемое на запись (из-за необходимости дожидаться, пока изменения доедут до второго сервера), но лишнюю нагрузку это создать не должно.

                  Получается, что нагрузка падает с (w + r) до (w + 0.5r) на первом сервере, и возрастает с (w) до (w + 0.5r) на втором. В целом максимальная нагрузка, которую сможет выдержать кластер, возрастает практически вдвое.

                  Если же хранилище практически write-only, то такая схема уже не работает. Это да. Ну так ее никто и не будет использовать для таких хранилищ.
                    0
                    Хотел еще спросить в чем преимущество такой схемы перед синхронной мастер-слэйв репликации, но понял сам — можно ходить по более оптимальному маршруту для записи. Если до мастера А ближе, чем до В, мы идем С-А-В-А-С, где С — клиент кластера, вместо С-В-А-В-С, если бы В был единственным мастером.
                      0
                      Да, вы правы. Плюс не нужно разделять запросы на пишущие и читающие. Всегда идешь в любой из серверов и не паришься, мастер это или реплика.
                      0
                      А что происходит, ежели при записи на одну из реплик, по какой-либо из причин запись «не пройдет» (и в то же время, на мастере будет успешно выполнена)?
                        0
                        Клиент пишет только в один из серверов. Дальнейшей репликацией занимается сам Тарантул. В зависимости от политики (синхронная или асинхронная репликация) ответ на запрос клиент получит сразу или после успешной синхронизации мастеров.

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

                          Получается, в асинхронном случае, возможно временная рассогласованность данных; когда мастер еще не успел перелить апдейты на реплики, однако сказал клиенту, что изменения сохранены, а потом «умер». Мы лезем на реплику и зачитываем еще не обновленные («старые») варианты данных, а клиент думает, что они уже обновлены. Или все-таки это не так критично в данном случае?
                            0
                            Да, в асинхронном случае реплика может немного отставать. Именно поэтому все запросы (и на чтение, и на запись) делаются на мастере. Реплика используется только в случае физического выхода из сторя сервера с мастером.

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

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