Наверх ▲

Проектируем облачный веб-сервис "по-взрослому"

Сергей Рыжиков Сергей Рыжиков Генеральный директор компании "1С-Битрикс"

Сергей Рыжиков: Здравствуйте! Меня зовут Сергей Рыжиков, я - генеральный директор компании "1С-Битрикс".

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

Сразу оговорюсь: проект еще не завершен. Вы не сможете его сейчас посмотреть. Мы предполагаем открыть его в ноябре. Тем не менее, подготовка идет уже достаточно давно – можно сказать, с начала этого года. 

Проект пока условно называется "Bitrix24". К нему есть набор коммерческих требований. Они ставят нас в условия непрерывной работы.

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

Нам сразу нужно думать, как масштабироваться, как распределяться по различным территориям. В том числе географически, потому что проект будет располагаться сразу в трех языковых зонах: “.com”, ".de" и ".ru". Разные территории - разная доступность.  

Бизнес-требования к проекту привели к формированию некоего набора технических требований, которые звучат примерно так. 

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

Существенные объемы данных создали необходимость в шардировании, в том числе в зависимости от территории. Чтобы, например, основные объемы данных на территории США были "привязаны" к США, а российские – к России.

Многоарендная (англ. multitenancy) архитектура также создавала определенные неудобства. 

Существует такое понятие, как потребность в разделении логики и данных. Под логикой имеется в виду код продукта. 

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

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

Также нам требуется осуществлять динамическое масштабирование. Мы знаем, что пиковая нагрузка системы несколько часов в сутки будет в десятки раз превышать обычную. У него будет как обычный, так и ночной рабочий режим, при котором нагрузка - минимальна. 

Когда мы начинали проектировать систему, то решили: «Надо сделать так, чтобы машины сами "поднимались", останавливались, чтобы все работало, как надо». В результате появился комплекс задач, которые мы разделили на две большие группы.

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

Первая часть работ была связана с реконструкцией, развитием продукта и разработкой "облачной" платформы. Традиционно веб-приложение – это код (dotNet, PHP). Вы храните данные в базе. База при этом шардируется редко, веб-кластер редко поддерживает само приложение, не говоря уже о географическом веб-кластере.

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

Выполняя план, мы реализовали сначала первую часть задач по развитию платформы. Эта часть вошла в релизы, которые состоялись в начале года. Мы реализовали схему "ведущий-ведомый" (англ. master-slave), репликацию, внесли в продукт поддержку веб-кластеров стандартной конфигурации. Также создали возможность хранения сессии, репликации базы, вертикального шардинга. 

В результате проведенных мероприятий была создана конструкция, в которой один, два, три – сколько угодно серверов работают внутри. Нагрузка балансируется между этими серверами в зависимости от правил. Балансировщик может быть любым. Он не является составной частью платформы. При этом кэш используется на всех машинах. Базы используются только схему "ведущий-ведомый". Фактически, все узлы соединяются между собой. Им нужно иметь очень качественное соединение с соседними машинами.

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

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

Однако, имеется еще одно слабое место. Необходимо синхронизировать файлы между решениями. Фактически здесь мы используем Csync 2 и даем ряд рекомендаций по файловым системам, по тому, как синхронизировать контент. 

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

Тем не менее, мы закончили первый этап и приступили ко второму. 

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

У нас сейчас можно создать группу серверов. Внутри нее происходит только обмен. Как и на предыдущем этапе, такая конфигурация находится в каждой из этих точек. Требовалась готовность к ситуации, когда разрыв соединения между серверами и дата-центрами длится часами (может быть, и сутками, но при больших объемах информации в этом случае будут проблемы).

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

Ещё одно уязвимое место – файлы. Если происходит загрузка файлов, надо ли реплицировать файлы по всем узлам? Объем может быть очень большим.

 

По этой причине появляются "облачные" хранилища. Дело в том, что динамика на рынке очень сильно меняется. Мы решили, что не будем связываться с отдельным конкретным хранилищем, а попробуем сделать поддержку сразу целой группы. Мы реализовали поддержку Amazon S3, Azure, Google. Еще есть проект OpenStack, поддерживаемый Rackspace; в России его поддерживает Clodo. Я надеюсь, что подключатся и другие компании.

Что дает эта архитектура и вообще использование "облачных" файлов? Вы храните информацию не в самих базах данных, а складываете ее в "облако". У вас есть некий универсальный URL, и неважно, откуда берутся файлы. 

Таким образом, сняли еще одну проблему: не нужно реплицировать файлы между системами. Хотя нам пришлось пройтись по многим форумам, блогам. Мы побывали везде, где загружались фотографии и работали контент-редакторы. Вы не должны больше "складывать" файлы на диск. Вы должны научиться "складывать" их в "облако". Если нужно изменить размер фотографии, надо взять её из "облака", обработать и вернуть обратно. Все файлы и ссылки должны браться оттуда. 

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

 

Далее мы выбирали, куда пойти. Сегодня выбор сделан.  

Мы уже имели определенный опыт работы с виртуальными машинами и Amazon.

До этого мы держали серверы в США на своем оборудовании. Но в Новый Год сервер вышел из строя. Мы получили полтора дня простоя. У нас очень большой объем. Нам пришлось переезжать. Мы не были готовы к выходу из строя нескольких элементов в оборудовании. 

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

Конечно, для статического контента мы использовали S3. Единственной проблемой стало ограничение по количеству бакетов. Но можно не создавать много бакетов, а использовать единое хранилище.

S3, помимо всего прочего, дает CDN. Вы можете достаточно легко и быстро доставлять контент. Безусловно, это выгоднее, чем использовать EBS-диски - в общем, приемлемый API. 

 

Именно за счет использования S3 мы снизили стоимость CDN, нагрузку на веб-узлы. Фактически мы берем S3 и через внутренний процесс отдаем наружу. Не самое удачное решение, но пока, к сожалению, другого найти не удается. 

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

Интересные предложения есть у Rackspace. Google активно работает в этом сегменте. Не исключено, что, например, стартуя на S3, мы потом начнем хранить данные в Google, если их предложения по CDN окажутся более эффективными.

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

У Amazon есть ряд инструментов (Elastic Load Balancing, CloudWatch, Auto Scaling), которые дают нам возможность осуществлять загрузку и по расписанию, и по результатам мониторинга. Фактически, мы используем смешанный вариант, когда к определенному моменту времени инициируются сервисы, а в зависимости от нагрузки функционируют узлы.

 

Что мы имеем в результате? Все клиентские запросы поступают через балансировщик Amazon. Иногда хочется большей гибкости. Мы добились того, что у нас все экземпляры одинаковые. Каждый экземпляр и копия узла работают и обслуживают любого клиента. Мы обеспечили себе возможность выключать и включать машины и "раскидывать" запросы между ними случайным образом. Это оказалось не так просто, но дало хорошие возможности для масштабирования. 

При росте нагрузки мы "поднимаем" машины и экземпляры. Сейчас пока экспериментируем на Micro. Снижение нагрузки наблюдаем через CloudWatch.

Все узлы в нашей конфигурации (еще раз обращаю внимание!) идентичны. Запускаем мы их через AMI. У нас фактически готов образ нашей платформы, образ продукта, который будет обсуживать клиентов. Любой веб-узел может обслуживать одних и тех же клиентов.

Мы создаем общую машину. В минимальной конфигурации у нас их будет две, в максимальной – столько, сколько потребуется. Есть расчет на проблемные ситуации, когда машину нужно просто выключить, не разбираться с ней, а "поднять" новую. Уже написана автоматизация для перемещения между разными зонами. Это тоже может оказаться полезным.

 

Что делать, когда нужно обновлять ПО? Это необязательно ПО продукта. Это может быть серверное ПО.

Предположим, есть 30 узлов. Нужно выпустить обновление продукта. При этом, например, поменяли базу данных или ее структуру. 

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

 

Закономерный вопрос: что с базой? Здесь нам пришлось немного поработать над продуктом, чтобы "научить" его определять, установлено обновление в базу или нет. Мы смотрим, адекватна ли та база, с которой он работает, нужно ее обновить или нет. 

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

 

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

Мы рассуждали следующим образом... Стоит задача шардинга. Можно полностью переписать приложение или вообще его написать так, что вы сами научитесь "рвать" таблицу на "куски". Но специфика нашего приложения и проекта будет состоять в том, что у каждого пользователя может быть своя база или своя таблица.

Мы решили делать таким образом: у нас веб-интерфейс одинаков для всех клиентов, есть индивидуальные файлы, которые находятся в "облаке", и отдельные таблицы, отдельная табличная область, где хранится его набор таблиц, с которыми он работает. 

Что это дает? Это дает шардинг, но нужно решить вопрос с безопасностью. Каждая копия должна соединяться только со своим экземпляром. 

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

Здесь существуют еще "безопасники", которые выполняют много проверок. Они говорят: «Предположим, произошла компрометация одного из приложений. Нельзя открыть соединение к другой базе. У вас не должно быть такой возможности». Это решалось за счет написания отдельного приложения. Фактически, это модуль PHP, который обеспечивает маршрутизацию и работу в базе.

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

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

 

Мы выбрали хранилища RAID. RAID может состоять из одного диска, из двух, из четырех. Самыми эффективными оказались те, которые работают с четырех дисков. Это RAID-10.

Но рядом с этим возникает другая проблема – как теперь делать резервные копии. Нужно же научиться делать целостные копии базы данных. Обратите внимание: нам не нужно копировать веб. Мы можем их уничтожать, они нам не нужны – они все одинаковые. Нам не нужно копировать файлы, потому что они хранятся в "облаке" и с гарантией. По крайней мере, нас в этом уверяют. Надеюсь, что это всегда будет так. 

Остается, по сути, база. С базой самые большие проблемы. Как делать резервные копии RAID? В Amazon есть механизм снимков (англ. snapshot). Но чтобы сделать полноценную резервную копию, пришлось придумать, как "заморозить" систему на момент, когда она делается. Коллега, сколько сейчас секунд занимает приостановка? 

Коллега: Пару секунд.

Сергей Рыжиков: Фактически приложение не успевает заметить момент, когда мы выполняем "заморозку" файловых систем под RAID, делаем снимок и после этого продолжаем работать. Помимо схемы "ведущий-ведущий", у вас есть репликация, которая позволяет делать резервное копирование систем. В том числе, это помогает быстро перемещаться между зонами. 

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

 

Чему нам еще пришлось научиться? У нас было масштабирование по MySQL между ведущим сервером и ведомым. Это часть веб-кластерного решения. Мы можем много ведомых серверов набивать, и у нас проблемы с масштабированием на чтение вообще нет.

Есть только одна проблема с масштабированием на запись. Это, конечно, непростая штука. 

Здесь мы разработали несколько этапов. Вообще, каждый из них стоит денег. Мы решили, что на первом этапе мы будем масштабироваться в Amazon с помощью наших собственных скриптов. Сначала мы вертикально масштабируем ведомый сервер и потом за несколько секунд переключаемся на него, а приложение этого "не замечает".

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

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

 

Почему мы не выбрали RDS от Amazon? Есть прекрасное решение, его предлагают и говорят: «Надо попробовать, возьмите». Мы попробовали, посмотрели его в деле, но нам управляемости не хватило. На самом деле, решающим моментом стал удар молнии, после которого базы, размещенные в RDS, на очень долгое время вышли из строя. Это оказалось решающим фактором. Мы решили, что не возьмем сейчас RDS в качестве основной платформы.

 

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

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

У нас уже фактически сформировался целый инструментарий, который работает с API от Amazon, обеспечивает включение, выключение, зачастую автоматическое масштабирование или выполнение каких-то сервисных функций на этих машинах.

 

Один из наших приоритетов - надежность. Мы составили список аварийных сценариев, которые нам необходимы. 

Готов ответить на вопросы.

Вопросы и ответы

Вопрос из зала: Репликация по схеме "ведущий-ведущий" в MySQL как реализована? 

Сергей Рыжиков: Другой доклад, который мы собирались сделать на этой конференции, как раз про эту схему. Там вылезло много подводных камней. Но, в общем, задачу решили.

Вопрос из зала: Можно хотя бы одно ключевое слово? Что использовано, какая технология.

Сергей Рыжиков: Стандартный MySQL. У нас продукт взял некоторую часть на себя. Мы делаем смещение. В одной из зон генерируются (грубо) четные идентификаторы, во второй – нечетные. Зациклили их между собой, решили ряд коллизий. Еще сделали специальный инструмент (это самая сложная часть), который выполняет автоматический сброс кэша и генерацию совсем уникальных идентификаторов в некоторых случаях. Решив эти несколько задач, решили весь комплекс.

Реплика из зала: MySQL-репликация теперь не является надежной; она хрупкая, как хрусталь.

Сергей Рыжиков: Почему?

Реплика из зала: Стандартную MySQL-репликацию так характеризуют с точки зрения надежности...

Сергей Рыжиков: Соглашусь с комментарием, что субъективно. Во-вторых, там есть проблемные моменты. Но так как у нас клиенты разделены, у них свои базы, то мы можем решать проблемы в случае возникновения. У нас, кстати, уже есть наработки, связанные со сценариями восстановления. Но она не так неустойчива. 

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

Вопрос из зала: Небольшой вопрос-уточнение. Вы сказали, что отказались от использования RDS. Тем не менее, используете файловые хранилища. Не может ли быть таких же проблем, как с RDS? Не может ли файловое хранилище тоже отказать на несколько часов?

Сергей Рыжиков: Да может, конечно. На самом деле такое возможно. Когда утверждали, что все дата-центры отказоустойчивы, насчет RDS заявлений много было. До сих пор не дали однозначного ответа, почему RDS тогда в случае аварии стоял. Но тогда, как говорят, в случае удара этой молнии Amazon вышел из строя. И Azure вышел из строя, и, возможно, файловые хранилища какие-то провисали.

Вопрос из зала: Понятно. А машины ваши виртуальные на EC2 запущены?

Сергей Рыжиков: Да. Да, конечно.

Вопрос из зала: Какая версия MySQL используется?

Сергей Рыжиков: 5.1.


Комментарии

Нет ни одного комментария

Только пользователи могут оставлять комментарии

Возможно, вам будет интересно:

Андрей Смирнов

Андрей Смирнов

Руководитель разработки, разработчик, фанат Go, Python, DevOps и больших нагрузок. Руководил разработкой backend-сервисов в стартапе Qik, после его покупки продолжил работать в компаниях Skype и Microsoft. До этого участвовал в разработке и руководил созданием таких проектов, как damochka.

Андрей Смирнов (Qik) рассказывает, для каких задач хорош Twisted, и делится секретами работы с этим фреймворком.

Сэнди Гупта (Sandy Gupta)

Сэнди Гупта (Sandy Gupta)

Главный руководитель Open Solutions Group (OSG) в Microsoft.

Сэнди Гупта (Microsoft) расскажет о стратегии Microsoft по упрощению разработки "облаков" и по созданию возможностей для ПО с открытым исходным кодом.

Артём Гавриченков

Артём Гавриченков

Ведущий разработчик в компании "Highload Lab".

В докладе описаны типичные ошибки программирования при написании серверных приложений на основе TCP-сокетов.