К нам обратился заказчик из сферы развлекательного видеостриминга с интересной проблемой - у него серверы падали не одновременно, а ему очень хотелось бы добиться синхронности.
Серверы, которые смущали заказчика работали в роли бэкенда для хранения видеофайлов. По сути, это было множество узлов, содержащих десятки терабайт видеофайлов, которые предварительно были нарезаны в разном разрешении конвертерами. Затем, все эти миллионы файлов отдавались во внешний мир с помощью nginx + kaltura, что позволяло перепаковывать на лету mp4 в сегменты DASH/HLS. Это позволяло хорошо переносить даже высокие нагрузки, отдавая плеером только нужные сегменты без резких всплесков.
Проблемы появились тогда, когда встал вопрос с георезервированием и масштабированием при росте нагрузок. Серверы внутри одной группы резервирования умирали не синхронно, так как представляли из себя весьма разнообразный зоопарк с разными провайдерами, шириной канала, дисками и RAID-контроллерами. Нам предстояло провести аудит всей этой красоты и перестроить почти с нуля весь мониторинг с методологией управления ресурсами.
Особенности архитектуры
Первый же вопрос, который возник у нас при аудите, почему не использовался полноценный коммерческий CDN? Ответ оказался весьма прозаичен - так дешевле. Архитектура видеохранилища позволяла достаточно хорошее горизонтальное масштабирование, а относительно стабильный трафик позволял делал аренду железных серверов с хорошим каналом более выгодным решением, чем сторонние коробочные решения. Платный CDN был все-таки настроен, но с петабайтными масштабами отдачи контента он был весьма дорог для заказчика и подключался только в аварийных ситуациях. Такое случалось редко, так как в большинстве случаев рост числа посетителей прогнозировался заранее, что позволяло неторопливо заказать еще один сервер и добавить в перегруженную группу.
Весь видео-контент был разделен на десятки групп. Каждая группа содержала в себе 3-8 серверов с идентичным контентом. При этом, каждая группа кроме последней работала полностью в режиме read-only. Последняя группа работала в смешанном режиме - в нее одновременно писались новые видео и из нее же шла раздача контента. Когда место на группе серверов заканчивалось, запись в нее прекращалась и в дальнейшем данные из нее только читались. Схема выглядела весьма работоспособно, но было несколько очень неприятных моментов:
Серверы были разными. Нет, даже не так. Серверы были ЗООПАРКОМ. Политика диверсификации требовала иметь нескольких независимых друг от друга поставщиков с множеством локаций по всему миру. Естественно, железо арендовалось в разное время, с разными дисками, RAID-контроллерами и даже разной шириной канала.
По ряду причин, на этом этапе было невозможно обеспечить полноценную балансировку нагрузки между серверами с health-check. То есть, если один сервер из кластера падал, то пользователи все равно получали его IP адрес в DNS. До ручного снятия нагрузки, часть пользователей продолжала тыкаться в мертвый узел и получала ошибки при загрузке контента.
Из-за того, что в одной группе могли соседствовать серверы с каналом в 1 и 10 гигабит/с, было совершенно непонятно, когда уже пора докупать новые мощности.
В итоге, как потом выяснилось, в какие-то группы серверы докупались избыточно, а в какие-то наоборот.
Серверы падают по очереди
Посмотрите на график выше. Можно ли понять, насколько хорошо сбалансирована нагрузка между отдельными узлами? При этом, в этой группе есть серверы с каналом в 1, 2.5 и 10 гигабит. А еще один узел хромает на правую лапу имеет странный контроллер, который может выдавать внезапные перегрузки по IOPS на ровном месте.
При анализе мы разделили серверы на две большие группы:
Группа серверов с небольшим лимитом upload bandwidth (1G-2.5G)
Группа серверов с большим лимитом upload bandwidth (10G)
Проблема заключалась в том, что иногда случались инциденты. Один из последних выглядел как множество внезапных азиатских пользователей, которые начинали тащить десятками гигабит весь доступный контент. Все это совпало с тем, что один из мощных узлов в группе находился в нерабочем состоянии из-за вылетевшего диска и длительной пересборки массива.
Первыми сдались серверы, которые имели самый широкий канал, упершись в IOPS. При этом, узлы с небольшим каналом отключались позже, но уже по причине лимитов по пропускной способности сети. У заказчика были инструменты для перераспределению трафика между серверами, но применялись они скорее эмпирически. Сравнивать узлы между собой было очень сложно.
Таким образом, мы получили две основные группы:
WAN-limited server group
К этой группе относятся серверы с пропускной способностью менее 2.5G. Порог определен эмпирически при анализе пиковых значений.
При росте нагрузки до критической эти серверы никогда не достигают критических значений CPU iowait - DoS происходит из-за перегрузки сети. При этом внутри этой группы отказ происходит при разной нагрузке - 1G уйдет в DoS раньше, чем 2G. Использовать показатель трафика в чистом виде без нормализации невозможно.
IOPS-limited server group
К этой группе относятся серверы с пропускной способностью более 2.5G.
При росте нагрузки до критической эти серверы никогда не достигают критических значений upload traffic - DoS происходит из-за перегрузки дисковой подсистемы.
Давайте нормализуем!
В случае, если все данные представлены в разных масштабах, нам надо их нормализовать перед тем, как что-то с ними делать. Именно поэтому, мы сразу решили отказаться на основном дашборде от абсолютных значений. Вместо этого мы выделили два показателя и разметили его от 0 до 100%:
Значение cpu iowait - хорошо показывает перегрузку дисковой подсистемы. Мы эмпирически вывели по историческим данным, что системы заказчика приемлемо функционируют примерно до тех пор, пока iowait ниже 60%.
Ширина канала. Здесь тоже все прозрачно - как только нагрузка доходит до лимита, пользователи начинают страдать из-за ограничений на стороне провайдера.
За 100% мы приняли состояние, когда сервер добрался до своего лимита по IOPS или каналу и ушел в DoS.
Таким образом, шкала стала относительной, и мы стали видеть, когда сервер упирается в свои личные лимиты. При этом, совершенно неважно, 1 это гигабит или 10 - данные теперь стали нормализованы.
Возникла вторая проблема - сервер может отказать по любой из двух причин, а при финальной диагностике нужно учитывать обе. Добавили еще один слой абстракции, который показывал наихудшее значение по любому из ресурсов.
Теперь мы можем сбалансировать нагрузку так, чтобы при чрезмерной нагрузке серверы одной группы отказали одновременно. Это будет говорить о том, что ресурсы утилизируются максимально эффективно. Хотя, конечно, в норме средняя нагрузка не должна превышать рассчитанные пороги.
Что у нас получилось
Мы получили многослойный бутерброд из формул, который совершенно не показывал реальную абсолютную нагрузку на сервер, но зато точно предсказывал, когда именно он откажет.
Представьте себе упряжку, где есть сильные и слабые ездовые собаки. Нам совершенно неважно, сколько именно лошадиных сил (или собачьих?) выдает каждая из них. Нам нужно, чтобы вся повозка ехала равномерно, а каждая из собак была нагружена в равной степени в зависимости от своих возможностей.
В итоге получили вот что:
Автоматизировали весь процесс добавления новых узлов с помощью terraform/ansible с одновременной генерацией дашбордов, которые учитывают физические ограничения каждого сервера в формулах расчета при нормализации.
Подготовили качественную модель, которая позволила добавлять новые узлы в нужный момент без ситуаций “три сервера нагружены наполовину, а четвертый уже лежит”.
Серверы действительно стали падать совершенно синхронно. Реальный тест случился некоторое время спустя при мощном DDoS со стороны ботов-парсеров. Нагрузка почти одновременно уткнулась на всех узлах в свои лимиты - где-то в IOPS, где-то в ширину канала.
Заказчик смог сохранить свою модель расширения инфраструктуры с использованием разнородного по мощности оборудования.
В целом, это не было идеальным решением с точки зрения архитектуры, но оно быстро и оптимально закрывало основные проблемы, позволив добиться максимально полной утилизации уже имеющихся ресурсов. В частности, за счет нового мониторинга и нормализации мы смогли получить удельную эффективность каждого узла в группе с точки зрения финансовой эффективности. Если очень упрощенно, то стало понятно, сколько пользователей на затраченный доллар обслуживает каждый из узлов.
В дальнейшем, это позволило нам продолжить рефакторинг, внедрить кеширование с bcache, увеличив в десятки раз производительность отдельных узлов и вообще выбросить на мороз более половины серверов. Но об этом я расскажу в другой раз.
Ну а если вам нужно перебрать вашу инфраструктуру по винтику, настроить мониторинг и полностью все автоматизировать, приходите к нам в WiseOps. Мы поможем.
Комментарии (34)
aamonster
04.05.2024 22:19+7Задача вообще непонятна. Обычно ж стараются, чтобы всё разом не умирало, а не наоборот.
Meklon Автор
04.05.2024 22:19+2Тут проблема была в том, что ресурсы были несбалансированы между собой. Есть мощные и дохлые узлы. Если просто разделить трафик в равных пропорциях, часть узлов уже ляжет под нагрузкой, а часть будет нагружена наполовину от своих возможностей.
Но вот задача правильной балансировки была трудновыполнима из-за того, что не было нормальной модели и нормализованного мониторинга нагрузки.sekuzmin
04.05.2024 22:19+2@aamonster совершенно верно акцентирует внимание на несколько чудовищную формулировку задачи.
К нам обратился заказчик ... у него серверы падали не одновременно
Проблемы появились тогда, когда встал вопрос с георезервированием и масштабированием при росте нагрузок. Серверы внутри одной группы резервирования умирали не синхронно
А вот цели проекта сформулированы просто великолепно
Project Goals
To develop a methodology for predicting the dynamics of reserve capacity and the need for storage expansion.
Meklon Автор
04.05.2024 22:19+2Простите за такой кликбейт в формулировках. По сути, было необходимо обеспечить равномерность нагрузки на серверы с очень разными характеристиками, чтобы полностью утилизировать мощность.
Если часть уже откажет, а часть останется функциональными, то это не спасет в этой архитектуре. Вся группа уже будет нерабочей. Но да, в условиях идеальной балансировки они действительно синхронно падают при DDoS. Конечно, не в этом была основная цель, просто забавное наблюдение и критерий хорошей балансировки.
DaneSoul
04.05.2024 22:19+6Ну так старый добрый кликбейт же.
Я собственно из-за заголовка и зашел, стало интересно, зачем может понадобиться синхронно ронять все сервера.
aamonster
04.05.2024 22:19Я даже догадался, в чём была реальная проблема (глупо переплачивать за мощные сервера, которые работают с такой же нагрузкой, как хилые), но прозвучало крайне странно :-)
alexdora
04.05.2024 22:19+2Напрашивается вопрос, а нельзя сделать не пропорциональную нагрузку с перераспределением при падении узлов? Конечно, задача не очень простая потому что нужно проводить целое исследование для поиска оптимального алгоритма. Но это реально. Потому что…задача действительно странная. Обрубить стек серверов из-за отказа одного, но ведь если вы убиваете стек серверов в CDN трафик волшебным образом не пропадает, значит все это успешно ударит по другому стеку серверов который работает.
LoveMeOrHateMe
04.05.2024 22:19+14Очень даже молодцы. Не стали с умным видом рассказывать, мол у вас тут все не правильно, давайте все строить заново, а сначала разобрались с текущей ситуацией, помогли заказчику, а уж потом потихоньку можно будет привести все в божеский вид. Я думаю заказчик должен быть очень благодарен.
Meklon Автор
04.05.2024 22:19+2Мы всегда придерживаемся идеи, что заказчик не просто так долгое время что-то строил. Вначале быстро решаем то, что болит прямо сейчас, а потом уже итеративно движемся оптимальному варианту.
achekalin
04.05.2024 22:19Интересно, хоть каналы-то были безлимитные по объему в месяц? Или до некоторой величины оплачено, а далее погигабайтно?
Н могу еще понять: если сервер выведен/умер, а его ip все еще запрашивается клиентами, то не так это и удобно. Тут уже клиентское ПО должно тестировать серверы и выбирать живые, разве не так решать проще всего?
Meklon Автор
04.05.2024 22:19+1Там зоопарк) одновременно были варианты:
Канал безлимитный по общему трафику, но ограничен по ширине. Обычно это узлы по 1-2.5 гигабита.
Канал органичен по ширине, в стоимость входит пакет трафика, потом за дополнительные деньги.
-
Канал без формального лимита по ширине канала, пакета трафика нет, тарифицируются гигабайты.
Финансовую оптимизацию мы позднее делали, там вообще весело было. До полноценного мониторинга было сложно посчитать вообще что и сколько стоит.
Насчёт того, что мертвый сервер не должен отдаваться клиентам - я согласен. Но там были особенности, которые не давали это сделать.
rPman
04.05.2024 22:19Уже десять лет существует технология webtorrent в т.ч. для стриминга, но видеоконтент продолжают отдавать по старинке.
Или у p2p технологий есть какая то неразрешимая фатальная проблема, почему ее не внедряют везде?
php7
04.05.2024 22:19Это то что с WebRTC?
Я когда-то тестировал один сервис.
Он не особо снимал нагрузку с серверов.
unclejocker
04.05.2024 22:19Все за NATом сидят, а если у обоих пиров NAT, то нужен еще сервер, который их соединит и будет через себя гнать трафик, проще напрямую отдать.
Meklon Автор
04.05.2024 22:19Возможно, IPv6 в итоге изменит ситуацию. Мы видели большие объемы трафика через него а ЮВА.
slonopotamus
04.05.2024 22:19+1Непонятно, как? На IPv6 все вместо NAT'а сидят за фаерволом на том же самом роутере, который не разрешает входящие соединения.
Meklon Автор
04.05.2024 22:19+2Я сейчас не про torrent, а в принципе про IPv6 трафик. А так согласен, это выглядит очень перспективно
rPman
04.05.2024 22:19+1NAT-ы бывают разные, типовой nat который по умолчанию у всех у кого роутер с wifi и динамическим/статическим ip, пролезает с помощью stun сервера (когда для начала разговора нужен третий, но весь трафик пойдет напрямую между пирами, даже если они оба за nat), вот с мобильными сложнее, там провайдеры могут нагородить вложенные nat.
unclejocker
04.05.2024 22:19Про типовой нат. Я даже проверил сейчас - у меня провайдер выдает мне серый адрес, он назначается роутеру и роутер внутри квартиры раздает клиентам (опять же через nat) второй слой серых адресов. Т.е. типовая конфигурация - двойной nat. Это крупнейший провайдер в Москве. И так уже лет 10. И думаю так будет у любого более-менее крупного провайдера, т.к. с нынешними ценами на ipv4 первое что приходит в голову для оптимизации расходов - это загнать всех клиентов за nat.
blind_oracle
04.05.2024 22:19TURN и ко давно эту проблему решает достаточно надёжно.
unclejocker
04.05.2024 22:19Насколько я понимаю, через TURN весь трафик и ходит при этом, а если трафик от клиента к клиенту ходит через ваш-же сервер/канал, то зачем огород городить?
blind_oracle
04.05.2024 22:19Нет, TURN нужен только чтобы дырявить NAT и сводить клиентов друг с другом (сообщая номера портов и т.п.). Трафик ходит напрямую.
unclejocker
04.05.2024 22:19Это STUN. А про TURN вот что пишут на webrtc org: For most WebRTC applications to function a server is required for relaying the traffic between peers, since a direct socket is often not possible between the clients (unless they reside on the same local network). The common way to solve this is by using a TURN server. The term stands for Traversal Using Relays around NAT, and it is a protocol for relaying network traffic.
Ну и выше человек написал, что пробовал он, но нагрузка уменьшается незначительно, большинство клиентов не могут друг до друга достучаться. Для экономии адресов провайдеры сейчас городят nat на nat-е и nat-ом погоняет.
sshmakov
04.05.2024 22:19+1Итак, в очередной раз сферы развлекательного видеостриминга, пуст немного, но подстегнули прогресс.
Meklon Автор
04.05.2024 22:19+1У них часто интересные задачи , требующие необычных решений.
datacompboy
04.05.2024 22:19+1Интересный способ сказать, что там всегда всё через одно место...
Meklon Автор
04.05.2024 22:19Всякое бывает. Часто это довольно старые проекты, которые внезапно обнаружили, что им нужно обслуживать тяжёлые нагрузки, и сделали как смогли. Да, многие решения могут выглядеть несколько необычно. Но они работают и приносят доход.
Поэтому, приходится все аккуратно и последовательно все оптимизировать.
trublast
04.05.2024 22:19+2В похожей ситуации использовали такой подход: m3u динамический, плейлисты отдаются высокопроизводительным бэкендом. Там высокий rps, но нет io, и сами плейлисты мелкие, поэтому bandwidth не занят у серверов.
При генерации плейлиста учитывалась нагрузка на серверы, отдающие непосредственно видеочанки. Серверы выводились из балансировки при недоступности, или когда полоса была на пределе. А ещё автоматом подкидывалась раздача через cdn, когда полоса всех серверов группы была близка к пределу
Meklon Автор
04.05.2024 22:19О, спасибо. Очень пригодится, как раз сейчас оптимизируем вместе с их разработчиками.
Kilmez
04.05.2024 22:19Извините, что не совсем по теме вашего кейса
У меня давно была открыта вкладка вашего сайта (на ru) с вакансиями девопсов, а теперь сайт полностью на en и ссылка на вакансии пропала. Можно её как то вернуть? Спасибо.
Meklon Автор
04.05.2024 22:19Без проблем, напиши мне в @meklon в telegram. Прямо сейчас у нас нет найма новых сотрудников.
datacompboy
Не, пусть мрут когда хотят. Абы N+1/N+2 сохранялись... (и да, обидно, что добавлять один Самый Супер Сильный в группу не смысла, он-то и есть тот самый +1)
Meklon Автор
На том этапе развития проблема была в том, что если сервер лег, то из нагрузки он автоматически не выводится. Пользователи страдали. Мы это реализовывали на более поздних этапах рефакторинга инфраструктуры.