Серебряная пуля геораспределенных систем
В головах многих бизнес-заказчиков сидит мысль, что стоит добавить в приложение поддержку работы в нескольких дата-центрах, как надежность повышается в разы. И геораспределенные системы являются серебряной пулей отказоустойчивости. Ведь если произойдет отказ дата-центра, есть другой, и система все равно будет работать.
Я — руководитель департамента эксплуатации и разработки сервисных систем ECOMMPAY IT. В своем докладе на конференции HighLoad++ Весна 2021 я рассказал, правда ли, что геораспределенные системы потенциально могут повышать надежность. И разобрал ситуации, когда геораспределенность не помогает, а добавляет потенциальных проблем и заставляет задумываться о новых вещах. Расшифровку доклада читайте под катом.
Как зачастую начинается история построения геораспределенных систем? К вам приходит руководство и говорит: «А давайте сейчас нашу систему поставим в два дата-центра. Если даже один из них откажет, у нас есть второй!» Но бизнес не понимает (да он и не должен понимать), какая сложность стоит за этим. Понимать это должны вы, и говорить об этом с бизнесом придётся вам.
Давайте разберем ответ на важный вопрос.
Зачем строить распределенные системы?
Например, чтобы обеспечивать более высокий уровень надежности. Это самая первая, но не единственная причина, по которой вы можете захотеть построить геораспределенную систему. Кроме этого, очевидно, она приближает вычислительные мощности к конечному пользователю.
Для разного рода систем это может быть критически важно. Например, если вы строите систему, которая показывает контекстную рекламу на сайтах, то для нее критически важна скорость ответа. В этом случае, чем ближе ваша система находится к конечному пользователю, тем быстрее вы покажете нужную рекламу (и тем быстрее она загрузится). Когда у вас есть несколько дата-центров, вы можете масштабировать нагрузку между ними, и тем самым обрабатывать большее количество пользователей и запросов, а значит зарабатывать больше денег.
Архитектура
Когда мы строим геораспределенные системы, первый вопрос, которым задаемся: как нам синхронизировать данные между дата-центрами?
Возможные варианты синхронизации данных
В вопросе синхронизации данных есть три подхода:
Асинхронная синхронизация, то есть принимаем запрос на одной площадке, а на другую передаем данные после того, как этот запрос обработали.
Синхронная обработка запросов. Мы принимаем данные на любой из площадок, сохраняем по всему скоупу всех наших установок, после чего возвращаем данные клиенту в ответ на запрос.
Гибридный подход. Записываем часть данных в асинхроне, часть данных в синхроне.
Давайте разберемся, какой подход стоит выбирать в той или иной ситуации.
Синхронный подход
Предположим, мы хотим синхронизировать наши данные абсолютно синхронно и обеспечивать их полную консистентность. Для того чтобы понять, хорошо это или плохо, посмотрим на схематичную картинку сетевого лага:
Здесь приведено время доступа сетевого лага от пользователя, который находится в Москве, до конкретных наших точек присутствия в Европе. Это несколько ДЦ, которые расположены в различных европейских городах.
Самая близкая точка — это точка присутствия в Хельсинки. До нее из Москвы сетевой лаг составляет примерно 40 ms. Если же мы строим систему, которая будет синхронно сохранять данные, то автоматически любой запрос увеличится от 40 ms (за это время приходит запрос к ближайшей точке) до (40 + Δ) ms, где Δ накладывается на то, чтобы передать запрос между всеми дата-центрами. В нашем случае, если мы гоняем данные синхронно, это примерно от 45 до 90 ms, в зависимости от текущей задачи.
Допустим, мы готовы этим пожертвовать, но как это сделать?
Зачастую в системах есть какие-то классические реляционные базы данных. Вы хотите построить распределенную систему, открываете Google и набираете: «Как построить распределенную систему?». Если вы допишите «синхронную», то можете найти прекрасную вещь под названием Galera cluster:
Выглядит, как серебряная пуля — просто ставим и получаем коробки консистентность и надежность.
Так подумали наши коллеги из одной транснациональной корпорации.
У них было три площадки (Лондон, Даллас и Сингапур). Для понимания, сетевая задержка между Лондоном и Сингапуром составляет примерно 130 ms, между Лондоном и Далласом примерно 95 ms. У ребят была распределенная система, commite time на одну транзакцию составлял полсекунды. Если что-то случалось, переключение клиента составляло как минимум 10 секунд. Они попробовали так работать два месяца, после чего откатились обратно к режиму, когда один ДЦ активный, а два других находятся в режиме полугорячего резерва.
Получается, что Galera cluster все-таки не дал серебряную пулю? Решение рабочее, и его можно использовать. Но надо понимать, какую логику оно несет за собой, и как эта логика ложится на ваше приложение.
Предположим, у нас три ДЦ в принципиально разных локациях. Значит мы не можем использовать синхронную репликацию. Посмотрим в сторону асинхронной.
Асинхронный подход
Другая IT компания, у которых были два ЦОДа (в Москве и Питере) попыталась сделать асинхронную репликацию в MySQL. Изначально Питерский ЦОД был резервным, а Московский — основным. Они сделали асинхронный мультимастер и поставили туда асинхронную репликацию.
Этот проект был запущен в эксплуатацию в пятницу вечером. До понедельника отмечалось его успешное завершение. А в обед понедельника все IT подняли по тревоге, потому что у финансистов в двух финансовых проводках оказались разные данные. В итоге через неделю в компании все откатили назад, и сейчас снова работают на одном активном ЦОДе и одном неактивном.
Так что же, и синхронный, и асинхронный подход — это плохо? Что же делать?
Опыт из реальной жизни
У нас стояла задача: построить геораспределенный биллинг.
Мы занимаемся транзакциями клиента (например, вы совершаете покупку в магазине) и живем на маленький-маленький процент (обычно 0,001%) от этих транзакций. Но задача биллинга: посчитать этот процент, потому что от каждой транзакции он разный.
ЦОДов у нас пять. Это значит, что нужно обеспечить реализацию этого биллинга с простыми, условиями:
Требование 1. Максимальная скорость ответа
Требование 2. Синхронный контроль баланса
Условия вроде бы взаимоисключающие, нужно сесть в уголок плакать. Но вместо этого мы посмотрели, какие типы транзакций в биллинге у нас есть. Их два:
Транзакция PAY IN.
В этом случае вы передаете деньги, и они идут к нашему клиенту (юридическому лицу).
Транзакция PAY OUT.
Типичная ее пример — это когда вам на карту несколько раз в месяц приходит зарплата. То есть мы передаем деньги со счетов юридических лиц на счета физических лиц.
Посмотрев на эти два типа транзакций и условия, мы получили достаточно интересное наблюдение: не надо выполнять условия, которые были изначально, для всех типов транзакций. Максимальную скорость ответа по-настоящему надо обеспечить только для PAY IN, а для PAY OUT нужен синхронный контроль баланса. Объясню, почему.
Представьте себе, что вы делаете покупку в магазине. Приложили карточку к терминалу, и на нем слово «обработка» висит 5 минут, — вы нервничаете, кассир тихо нажимает на кнопку, рядом уже стоит охранник, который готов достать дубинку (или тесак) —, а вокруг общая нервозность. Думаю, что если у вас каждая транзакция будет проходить так, то уже через день вы задумаетесь о смене банка. Потому что вы привыкли: приложили карточку, и сразу же прошла оплата. В этом вопросе важна скорость.
Но когда вы платите, счет клиента увеличивается всегда, потому что деньги от вас переходят к нему. Там не нужно настолько сильно контролировать баланс.
А вот в PAY OUT транзакции, когда со счета юридического лица деньги идут в обратную сторону, не принципиальна скорость. Например, когда вы получаете зарплату, вы даже не знаете, в какой конкретно момент времени она поступит на счет. Вам известно, что вы ее получите 15 числа, а во сколько (в 12 или в 11), вам не принципиально.
Получается интересная история: для одного типа транзакций есть одно требование, для другого — иное.
И тогда у нас родилась интересная структура, которую мы рассмотрим на примере трех дата-центров.
Архитектура системы
Мы разделили баланс на три части. У каждого юридического лица его баланс тоже поделен, и каждая из частей принадлежит своему ДЦ. Конкретный дата-центр может изменять только определенную часть баланса.
Суммарный баланс каждого клиента состоит из суммы трех балансов по всем ДЦ. Когда нам надо изменить баланс, на какой бы ДЦ ни пришел запрос, мы меняем локальный баланс именно там. Если локальный баланс увеличивается, проблем нет. Если локальный баланс уменьшается, в рамках конкретного ДЦ, мы контролируем уровень этого локального баланса для того чтобы он не ушел в отрицательные значения.
Если в локальном ДЦ не хватает денежных средств, идет широковещательное сообщение с фразой: «А ну-ка, займи мне, пожалуйста!», и происходит внутренний перевод с одного ДЦ на другой. Если денежных средств на всех ДЦ хватает для проведения транзакции, мы ее проводим.
В итоге мы пришли к следующей архитектуре:
PAY IN транзакции происходят в асинхронном режиме, то есть транзакция всегда придет на один ДЦ, а потом данные по принципу eventual consistency доедут до других;
PAY OUT транзакции чаще всего происходят в асинхронном режиме, если хватает локальных балансов. Если же нет, все происходит в синхронном режиме с задержкой времени проведения транзакции. Потому что для PAY OUT транзакции это допустимо.
Из этого можно сделать следующий вывод:
Архитектуру синхронизации данных между удаленными площадками нужно строить исходя из архитектуры системы и бизнес-требований, которые есть.
Если же архитектура вашей системы не позволяет реализовать этот подход, нужно подумать о том, чтобы ее изменить.
А теперь подумаем вот о чем: является ли синхронизация данных единственной сложностью? Следующая проблема, которая возникает — это проблема роутинга.
Очень просто, когда у вас stateless-система, вы работаете с одним запросом — проблемы роутинга не существует. К вам приходит любой запрос из доступных дата-центров, любая из доступных площадок отправляет ответ и забывает про пользователя. Но по факту у вас всегда stateful система, и в зависимости от того, куда придет запрос пользователя, ее поведение меняется.
Варианты роутинга
Базовые варианты, которые предоставляет любая технологическая платформа (или вы сами их можете построить), таковы:
распределение на основе параметров ЦОДов (нагрузка, количество коннектов);
распределение на основе параметров клиента (IP-адрес, браузер или любые другие параметры);
распределение на основании функции (равномерное, по весам и т. д.). Например, каждого второго отправляем налево, каждого первого — направо.
Все эти варианты зачастую не работают, когда у вас во время взаимодействия с клиентом существует более чем один запрос.
Проблематика
Если у вас более чем один запрос, регулярно может возникать такая ситуация:
Есть два дата-центра. Первый запрос пришел на один ДЦ, второй на другой. Там нет данных, мы отвалились с ошибкой, недовольный пользователь ушел.
По факту получается, что есть дополнительное условие роутинга:
Роутинг надо осуществлять иногда с привязкой пользователей в рамках сессии, а иногда без привязки.
Есть ли стандартные инструменты, чтобы сделать это? Они существуют. Но стандартные варианты роутинга имеют ограничения. Давайте попробуем рассмотреть их:
1. На основании внутренних параметров. В этом случае ограничением является обязательное наличие параметра роутинга в каждом запросе к системам.
2. На основании параметров клиента. В этом случае проблема в том, что распределение может быть неравномерным, а карта будет перестраиваться каждый раз при изменении конфига.
3. Редиректом после первичного запроса на прямой адрес площадки. Но это может быть проблемой для API клиентов и при использовании POST запросов.
4. На основании sticky cookie. Но в случае API интеграции куку некуда выставить.
Что сделали мы?
У нас есть браузерные и API-интеграции и первичные POST/GET-запросы. Поэтому не осталось иного выбора, кроме как реализовать схему, в которой в каждом запросе от пользователей есть одинаковый идентификатор. Вернее, пара уникальных идентификаторов:
project_id — идентификатор клиента;
payment_id — идентификатор платежа.
На основании этой пары мы определяем, куда отправить запрос.
Однако все это может работать в конкретном случае. Серебряной пули нет. Но роутинг сможет также применяться для того, чтобы обеспечить приближение ваших процессинговых решений и ваших систем к конкретному пользователю.
Когда вы занимаетесь вопросом приближения решений к конкретному пользователю, скорее всего, сталкиваетесь с таким понятием, как AnyCast.
Роутинг: выбор ближайшего ЦОД
AnyCast — это сетевой протокол, который позволяет отдавать различные сетевые маршруты для одних и тех же систем и адресов DNS.
Но AnyCast реализован на основании протокола BGP. А если мы используем его, автоматически приходим к понятию собственной автономной сети.
Автономная сеть — это собственный набор IP адресов. Хотели бы вы владеть своими IP адресами, стать LIR (Local internet registrator)?
Если вы примете решение о том, что вам нужны собственные IP адреса, вы будете похожи на такого котика:
Почему?
Как минимум, потому, что IP v4 адреса закончились. Есть IP v6 адрес, но проблема в том, что еще не все работают по IP v6. Если вы не хотите отрезать львиную долю клиентов, то не можете взять IP v6 адреса.
Если вам нужны IP v4 адреса, вы должны подать заявку на получение статуса LIR (это как раз обозначает, что вы становитесь владельцем автономной системы), вам говорят: «ОК!» и ставят в очередь на неопределенный срок. Также вы можете купить IP адреса на у других владельцев AS.
Есть другой путь: интернет-адреса закончились в Европе, но они не закончились, например, в Африке. Можно подать заявку туда, и вам сразу выдадут IP адрес, который будет привязан к Африке. Но здесь есть проблема: в этом случае вы должны вести бизнес в Африке, иначе у вас могут быть проблемы.
Остановимся на том, что каким-то образом вы купили IP адреса. Дальше начинается куча разных проблем, с которыми вы раньше не сталкивались:
Сетевиков нет. Вы можете сказать, что любых специалистов нет на рынке. Это так, но сетевики — это отдельная категория людей, которых действительно почти не существует. Найти качественного сетевика — большая проблема.
Нужен резерв провайдера. Раньше вы работали либо в облаке, либо на собственном оборудовании, арендовали машино-место, стойку и сеть у провайдера, в ДЦ. И он сам заботился о том, чтобы в случае аварии переключить вас на другой ДЦ. Теперь это магическим образом не работает, потому что вы сами управляете IP адресами и сами решаете, куда отдаете анонсы.
Естественно, для вас открывается новый дивный мир, в котором регулярно происходят сетевые катаклизмы.
Последний нюанс, который присутствует в России— вы познакомитесь с РКН.
РКН в России
Это организация, про которую мы часто говорим в Телеграм-каналах.
Если вы получите в России автономную сеть, то к вам придет РКН и скажет:
— Передайте информацию о том, что у вас есть автономная сеть.
Вы передадите, потом он придет снова и скажет:
— Так, а теперь давайте полную конфигурацию и полные конфиги вашего оборудования, желательно распечатки, заверенные подписью вашего генерального директора. И обновляйте их каждый раз, когда обновляете оборудование!
Но это еще не все. Есть еще НСДИ (Национальная Система Доменных Имен) — это суверенный интернет, и он уже работает. Если у вас есть собственная автономная сеть, согласно требованиям Роскомнадзора, вы обязаны в первую очередь использовать корневые доменные сервера, которые им поддерживаются. Раньше это требование распространялось только на телекоммуникационных операторов, а теперь и на владельцев автономных сетей. График работы этих серверов можно посмотреть в специальном кабинете. Но часы приема там никто не знает.
Это проблемы, с которыми вы можете столкнуться в том случае, если захотите полностью управлять своим роутингом, приближать клиента к себе и организовывать информационные системы, которые расположены в географически разнесенных ДЦ.
Выводы
Тип синхронизации данных и организация процесса зависит от системы и бизнес-кейсов.
Я уже говорил, что тип организации данных и синхронизация их между ДЦ зависит исключительно от внутренней логики архитектуры ваших систем. Может быть, если ваши дата-центры близки, вам подойдет синхронная передача данных. Но в большинстве случаев нужно думать, как жить с асинхронной передачей. Асинхронная модель приводит к eventual consistency, вы не можете изменять одни и те же данные в разных ДЦ, и вам надо как-то с этим бороться — роутингом пользователей, дополнительными ухищрениями и т.д.
Не ищите «серебряные пули». Их нет. Любое готовое решение, про которое вы прочтете в интернете, не будет работать, если вы четко не понимаете, как именно вы хотите его воплотить в жизнь. И даже в этом случае вряд ли чужое решение не подойдет на 100%.
Важен вопрос роутинга пользователей для statefuI-системы. Если вы хотите строить геораспределенные системы, столкнетесь с проблемой роутинга пользователей. И эту нестандартную проблему вам придется решать.
Собственные пулы IP-адресов — это иной уровень контроля над архитектурой и иной уровень проблем. Если вы хотите приближать свои системы к конечному пользователю, столкнетесь с понятием автономной системы. И это принесет дополнительные проблемы, с которыми вы никогда не сталкивались.
Есть облачные хостинги типа Amazon, Mail.ru Cloud Solutions, Яндекс.облака, которые часть проблем могут попробовать решить за вас. Но, опять же, надо четко отдавать себе отчет, как они работают.
Конференция Saint Highload++ пройдет 20 и 21 сентября в Санкт-Петербурге. Мы будем ждать вас в DESIGN DISTRICT DAA in SPB.
Билеты можно купить здесь. С 10 июля цена на них станет выше. А еще вы можете сами стать нашим спикером!
Хотите бесплатно получить материалы мини-конференции Saint HighLoad++ 2020? Подписывайтесь на нашу рассылку.