Время пришло!



Время пришло! Распродажа года стартует!

Друзья, вот и настал тот самый день, которого все ждали!

Стартует главная распродажа года — 11.11

А чтобы праздник был по-настоящему крутым, мы подготовили для вас специальный подарок:

Только сегодня, 11 ноября, пополняйте баланс и получайте бонус +11% к любой сумме!

Пополнили на 1000 рублей? На вашем счету уже 1110!

Нужно больше мощностей?
Сейчас — самое время!

Успейте использовать шанс:
  • Пополнить баланс с +11%

Не упустите главную выгоду осени!
Акция действует до 23:59 11 ноября.

4vps.su

UFO.Hosting запускает распродажу 11.11 — в честь Всемирного дня шоппинга



UFO.Hosting запускает распродажу 11.11 — в честь Всемирного дня шоппинга!
Здравствуйте!

С 11 по 16 ноября в UFO.Hosting действует акция 11.11 — это отличный повод обновить серверную инфраструктуру и сэкономить:
  • VPS «12 за цену 6» — арендуйте на год, платите как за 6 месяцев (–50%);
  • –10% на продление VPS — продлевайте без простоев и по выгодной цене;
  • SSL –70% — переходите на HTTPS или замените действующий сертификат;
  • FTP-хранилище –20% — удобно для бэкапов и крупных файлов вне продакшена.
  • Чтобы применить скидку, просто введите промокод UFOSALE в корзине — цена обновится автоматически.

Сроки: до 16 ноября 23:59 (MSK), 2025. Если понадобятся рекомендации по выбору — поможем.
https://ufo.hosting

Получите пожизненную скидку 11% на любой новый VPS в этот День холостяков. Только один день!



Большинство скидок в День холостяков больше похожи на маркетинговые уловки, чем на реальную экономию. Вы знаете эту закономерность: разовые скидки, которые сгорают, как только вы перестаёте обращать на них внимание.

Мы делаем это по-другому, предлагая пожизненную скидку 11% на любой новый VPS.
contabo.com/en/vps/

Скидка 11%. Навсегда.
В День холостяков мы предлагаем скидку 11% на любой новый VPS, пока вы его используете. Не только первый месяц. Не только первый год. Навсегда.

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

Почему 11%? Потому что День холостяков — 11 ноября. Некоторые вещи просто имеют смысл.

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

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

Это предложение ко Дню холостяков действует только один день (видите, что мы там сделали?) — не упустите!

Ваша команда Contabo

Снижаем цены на дедики уже 5 лет!



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

За это время наши клиенты сэкономили больше 57 000 000 рублей! И вы тоже можете приобрести сервер по приятной цене >> 1dedic.ru/auction

За 5 лет у аукциона появились свои достижения:
  • Минимальная цена сервера — всего 1975 р.
  • Самая популярная конфигурация — Intel Core i7-7700 / 16 GB RAM / SSD 240 + 240 GB;
  •  Рекордная скорость активации сервера — 6 минут

Как мы строим сеть RUTUBE

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

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

Меня зовут Олег Аралбаев, я более 20 лет в IT и телекоме, работал в операторах связи, вендоре оборудования и нескольких интеграторах. С 2022 года занимаю позицию руководителя отдела развития и эксплуатации сети в RUTUBE. Расскажу, как мы строим и эксплуатируем сеть видеохостинга. Надеюсь, что-то из этого поможет вам в вашей ежедневной работе.

Сейчас сеть RUTUBE состоит более чем из 7 тысяч серверов в 7 центрах обработки данных. У нас в эксплуатации 380 CDN-серверов, которые ежемесячно обслуживают около 80 млн пользователей. Всё это результаты работы команды за последние несколько лет — большая часть действующих сейчас кода и инфраструктуры RUTUBE была разработана и внедрена после 2020-го. В том числе и сеть, которая ещё в 2021-м году представляла из себя следующее:
  • 31 коммутатор и маршрутизатор;
  • 5 ЦОДов с каналами между ними по 20 Гбит/с и офисная сеть;
  • Border router’ы MikroTik CCR1072;
  • отдельные DMZ-сети, NAT на каждом BR и отсутствие резервирования;
  • статическая маршрутизация на всех вышестоящих провайдерах без BGP.

Схема сети RUTUBE в 2021 году


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

Перенастройка legacy-сети
Первое, что мы сделали, это взяли нашу сеть под контроль — настроили мониторинг и централизованную авторизацию на всех устройствах:
  • Подняли и настроили на всех устройствах TACACS для централизованного контроля доступа, убрав локальную авторизацию.
  • RANCID для сохранения конфигурации и ведения версионности.
  • RSYSLOG — для аудита действий на устройствах.
  • Отдельный ZABBIX 7 для мониторинга всего парка сетевых устройств.

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

Что мы предприняли, чтобы увеличить производительность имеющегося оборудования:
  • обратились за аудитом, консультацией и обучением инженеров к внешним экспертам из компании, которая более 10 лет занимается внедрением проектов на оборудовании MikroTik;
  • перенастроили все firewall-правила на MikroTik;
  • закрыли везде доступы, повесили ACL — потому что до этого внутри сети был полный доступ;
  • перевели все стыки с интернет-провайдерами со статики на BGP;
  • проапгрейдили DCI-каналы между ЦОДами: 20 Гбит/с -> 40 Гбит/с -> 100 Гбит/с -> и в 2024 перешли уже на 400 Гбит/с.

Казалось бы, сеть и роутеры перенастроили, DCI расширили. Но не тут-то было. Из-за возросшего количества фаервольных правил и из-за нескольких BGP-сессий с новыми провайдерами в часы наибольшей нагрузки роутеры стали просто дропать BGP-сессии и регулярно перезагружаться по причине 100% загрузки CPU.


Для понимания конфигурация каждого роутера Mikrotik была примерно следующая:
  • ~1500 IP-адресов и подсетей в адрес-листах;
  • ~500 фаервольных правил;
  • ~20−30 Гбит/с общего трафика на роутер.

Чтобы снизить нагрузку на CPU и увеличить скорость обработки пакетов, мы решили перевести все правила фаерволла на работу с цепочками.

По умолчанию, если мы не используем цепочки, traffic flow в MikroTik проходит последовательно: входящий пакет (chain forward) проверяется подряд по всем правилам из списка с самого начала до первого совпадения. То есть может понадобиться проверить все наши 500-700 правил, и только в конце, если пакет не подпадает ни под одно правило, он будет дропнут.

Цепочки, в отличие от последовательной проверки, позволяют на уровне входящих интерфейсов сразу распределить пакет на правила, которые предназначены именно ему, то есть соответствуют определенному признаку (в нашем случае совпадение по входящему/исходящему интерфейсу и src/dst ip). Следовательно, роутеру не нужно последовательно обрабатывать все фаервольные правила, а можно перескочить сразу на узкую цепочку правил, под которую точно подпадает наш пакет.

Рассмотрим организацию цепочек на примере. Ниже список фаервольных правил, относящихся к сети 10.80.222.0/30.


Сейчас они обрабатываются последовательно в порядке нумерации, переделаем их на цепочки.

Шаг 1. Разделить все физические и логические интерфейсы роутера на зоны. В нашем случае это DMZ, LAN и WAN:


Шаг 2. Настроить правила jump в цепочке forward: как обычно идём в IP → Firewall и создаем новое firewall-правило, выбираем Chain = forward, входящий и исходящий интерфейс, из тех, которые мы разметили на предыдущем шаге, — в примере это DMZ и LAN. На вкладке Action делаем Action = jump, вводим имя нашей новой цепочки в поле Jump target — здесь это DMZ_to_LAN (оно нам дальше понадобится для создания правил внутри данной цепочки).

Шаг 3. Подобным образом создаём jump-цепочки для трафика между всеми размеченными интерфейсами на шаге 1 с учётом необходимой связности:


Шаг 4. Так как роутеры Mikrotik обрабатывают правила начиная с первого и далее по порядку, приоритизируем их порядок и по возможности помещаем наверх те, по которым фильтруется наибольший объем трафика. Это позволит снизить нагрузку на CPU роутера.

Для примера в созданной ранее цепочке DMZ_to_LAN сделаем правило для конкретного сервиса HAProxy:
  • выбираем цепочку, созданную нами ранее — Chain = DMZ_to_LAN;
  • Src. Address — подсеть 10.8.222.0/30, в которой у нас живет HAProxy;
  • на вкладке Action — Action = jump;
  • и там же — Jump Target = HAProxy_DMZ_to_LAN — вписываем название нашей новой цепочки для этого сервиса.



Далее уже внутри созданной цепочки HAProxy_DMZ_to_LAN мы можем создать ещё одно правило для разрешения пакетов от сервиса HAProxy до конечных хостов: Chain = HAProxy_DMZ_to_LAN. В качестве адреса назначения мы привязали адрес-лист сервиса pdns с его destination IP — адресами, протокол UDP, Destination port = 53, Action = accept.


Теперь, когда HAProxy обратится к этому сервису, он пойдет не по всему списку файрвольных правил, а только по относящейся к нему цепочке из трёх правил: DMZ_to_LAN → HAProxy_DMZ_to_LAN → FW Rule — PDNS, UDP:53.


В конце цепочки — обязательно правило drop. Если совпадение не найдено, значит, этот пакет не должен никуда передаваться.

Routing
С переходом на цепочки правил нагрузка на процессор действительно снизилась, но дропы BGP-сессий в часы наибольшей нагрузки не прекратились. Для снижения нагрузки на CPU роутеров MikroTik мы настроили на прероутинге правила дропать всё, что не попадает в исключения. На глубокую обработку CPU действительно стало попадать гораздо меньше пакетов, но, оказалось, что в часы наибольшей нагрузки одно ядро роутера на 100% занимается обработкой процесса ROUTING. Дело в том, что наши маршрутизаторы работали на RouterOS 6, которая просто не умеет распределять по ядрам процесс ROUTING. А как мы помним, для лучшего резервирования, мы добавили внешние стыки, работающие по BGP, и дело стало совсем плохо.

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


Мы приняли решение обновиться до RouterOS 7, несмотря на то, что на тот момент она ещё была довольно сырая. Пришлось пожертвовать протоколом BFD, потому что тогда в RouterOS 7 он не поддерживался.

После обновления MikroTik на RouterOS 7 мы взглянули на наши роутеры новыми глазами — они стали адекватно справляться с текущей нагрузкой. Это дало нам время на то, чтобы спроектировать, построить и запустить новую сеть.



Выше на графике каждое ядро загружено не более, чем на 15%, при конфигурации:
  • ~450 firewall-правил;
  • ~2100 IP в address list;
  • ~25 Гбит/с трафика и 7 млн пакетов в секунду в пиковые часы.

Бонусом апгрейда на RouterOS 7 стала дополнительная функция — мониторинг BGP-пиров по SNMP, которая ранее не поддерживалась. Теперь мы смогли вывести в Zabbix статус по всем BGP-peer’ам каждого роутера и настроить алерты.



Проектирование новой сети
Оптимизировав старую сеть, мы смогли выиграть время на проектирование и строительство новой сети под новую IT-инфраструктуру, которая значительно превосходила текущую.

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


Однако такие сети предназначены в основном для трафика «Север—юг», то есть трафика со стороны пользователя/сервера во вне или наоборот. В любом случае, предполагается, что каждый пакет проходит уровень ядра, маршрутизируется и уходит далее на какой-то внешний интерфейс. В нашем же случае требовалось построить сети для эффективной работы с трафиком внутри ЦОДов — то есть «Запад—восток».

Мы выбрали архитектуру сетей Клоза и коммутаторы Leaf и Spine.

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

Мы построили Leaf-Spine фабрику с полносвязной топологией, где коммутаторы доступа Leaf связаны со всеми коммутаторами агрегации Spine.


Эта топология имеет ряд преимуществ по сравнению с обычной трёхуровневой: минимальное количество хопов, минимальные, прогнозируемые задержки между серверами в пределах фабрики одного ЦОДа, легкое масштабирование и гибкость. Дале рассмотрим, за счёт чего это достигается.

В нашей новой сети мы выбрали технологию VxLAN для создания наложенной сети поверх L3 инфраструктуры, так как она даёт нам довольно весомые преимущества по сравнению с обычными VLAN:
  • решает проблему ограничения в 4096 VLAN — идентификатор (VNI) 24-битный;
  • позволяет растягивать L2-домены поверх опорной сети (underlay);
  • у вас появляется «наложенная» или overlay-сеть, которая позволяет развертывать и перемещать виртуальные машины в любое место дата-центра (или даже между дата-центрами) независимо от их физического расположения.

А также BGP EVPN — эта технология отвечает за распространение информации о MAC/IP-адресах и использует следующие типы маршрутов: Route Type 2, Route Type 3, Route Type 5.

Route Type 2 (MAC/IP Advertisement) — указывают, за каким сегментом подключено устройство с MAC/IP. Эти маршруты будут использоваться для передачи трафика к или от устройств, подключенных к VTEP коммутаторам (далее я расскажу, что это). Этот тип маршрута направлен на оптимальную маршрутизацию трафика внутри фабрики, основные этапы его работы:
  • MAC Learning — Leaf-коммутатор изучает MAC-адрес хоста.
  • BGP Advertisement — анонсирует RT-2 маршрут в BGP с: MAC-адресом хоста, IP-адресом (если известен), VNI, Route Target для указания VPN принадлежности.

  • Remote Learning — другие Leaf получают RT-2 и записывают к себе: MAC-запись в bridge table, VTEP-назначение для удаленного MAC, ARP/ND-запись (если IP присутствует).

Route Type 3 (Inclusive Multicast Route) — используется для распространения репликации BUM-трафика (Broadcast, Unknown unicast, Multicast) по каждому сегменту L2. В рамках работы наложенной СПД данный тип маршрутов используется для работы механизма Ingress Replication.
  • Функция: анонс принадлежности к BUM-группе.
  • Механизм: каждый Leaf анонсирует свой VTEP IP через RT-3.
  • Результат: построение multicast distribution tree для BUM-трафика.

Route Type 5 (IP Prefix Route) — предназначен для передачи информации об IP-сетях и используется коммутатором VTEP, в том числе для маршрутизации пакетов внутри наложенной сети в случае отсутствия для заданного IP-адреса назначения маршрута type 2.
  • Назначение: межсегментная L3 маршрутизация
  • Использование: анонс IP-префиксов между различными VNI/VLAN

Как же работает BGP EVPN:
  • Изучение MAC-адресов: когда хост отправляет кадр, Leaf-коммутатор изучает его MAC/IP и анонсирует эту информацию через BGP EVPN Type 2 маршрут всем остальным Leaf'ам.
  • Передача unicast-трафика: при получении кадра для известного MAC-адреса, Leaf инкапсулирует его в VXLAN и отправляет напрямую к целевому Leaf'у, используя информацию из BGP EVPN.
  • Обработка BUM-трафика: для широковещательного трафика используются Type 3 маршруты, которые создают дерево доставки или используют ingress replication.

Отказоустойчивость мы усилили, реализовав механизмы IP Anycast Gateway и Anycast VTEP для резервирования шлюзов и работы ECMP.

При работе технологии MLAG совместно с технологией ECMP образуется логический экземпляр Anycast VTEP. В качестве IP-адреса логического коммутатора Anycast VTEP используется IP-адрес интерфейса Loopback1, одинаковый для пары коммутаторов, формирующих домен MLAG (см. схему ниже).


В дополнение к этому мы используем концепцию распределенного шлюза по умолчанию (IP Anycast Gateway), это убирает необходимость поднимать трафик на уровень ядра. При такой конфигурации, как только на ближайший Leaf-коммутатор попадает трафик со стороны одного из серверов, он здесь же маршрутизируется и отправляется на Leaf-коммутатор и подключенный к нему сервер назначения (IRB маршрутизация). Это даёт понятное количество хопов до любого хоста в пределах ЦОДа и, соответственно, минимальные, прогнозируемые задержки.

Концепция IP Anycast Gateway схематично показана ниже (IP/MAC-адреса и идентификаторы сегментов VLAN ID приведены в качестве примера). Использование данной технологии опирается на конфигурацию интерфейса VBDIF на каждом из коммутаторов Leaf c одним и тем же IP-адресом и MAC-адресом.



Ключевые преимущества концепции Anycast IP Gateway:
  • Кардинальное улучшение производительности сети — устранение «тромбонного» эффекта. В традиционной архитектуре трафик между разными VLAN должен проходить через централизованный шлюз, создавая неоптимальные пути. С распределенным шлюзом каждый Leaf-коммутатор выполняет межсетевую маршрутизацию локально, направляя трафик по кратчайшему пути.
  • Максимальное использование пропускной способности. Поскольку маршрутизация происходит на каждом Leaf-коммутаторе, нагрузка равномерно распределяется по всей фабрике, а не концентрируется на одном устройстве.
  • Беспрецедентная отказоустойчивость и отсутствие единой точки отказа. Если один Leaf-коммутатор выйдет из строя, это повлияет только на устройства, непосредственно к нему подключенные. Все остальные хосты продолжают использовать свои локальные шлюзы без какого-либо влияния на производительность или доступность.
  • Мгновенное переключение при отказах. При миграции виртуальной машины с одного сервера на другой — подключенный к другому Leaf- коммутатору — IP и MAC-адрес шлюза остаются неизменными. Виртуальная машина даже не подозревает о том, что теперь использует другой физический шлюз.
  • Упрощение управления и конфигурации, единообразная настройка. Все Leaf-коммутаторы настраиваются абсолютно одинаково в части конфигурации шлюзов. Это значительно упрощает развертывание, обслуживание и устранение неисправностей в больших сетях дата-центров.
  • Автоматическая синхронизация — коммутаторы автоматически синхронизирует информацию о MAC и IP-адресах хостов между всеми коммутаторами через BGP EVPN.
  • Поддержка современных рабочих нагрузок, бесшовная мобильность виртуальных машин. При живой миграции виртуальных машин между физическими серверами в пределах ЦОДа, сетевая конфигурация остается полностью прозрачной. Виртуальная машина сохраняет свой IP-адрес и продолжает использовать тот же IP-адрес шлюза, хотя физически теперь обслуживается другим коммутатором.
  • Операционные преимущества, масштабируемость. Добавление новых Leaf-коммутаторов не создает дополнительной нагрузки на существующие шлюзы, поскольку каждый новый коммутатор становится независимым шлюзом для своих хостов.
  • Упрощенное планирование ресурсов. Не нужно беспокоиться о том, что централизованный шлюз станет узким местом при росте трафика — каждый Leaf-коммутатор обрабатывает только трафик своих локальных хостов.

В итоге использование IP Anycast gateway вместе с IRB (Integrated Routing and Bridging) позволяет создавать виртуальные L3-интерфейсы на коммутаторах, избавляет от необходимости поднимать трафик на уровень ядра сети и делает возможным маршрутизацию пакетов прямо на уровне Leaf.

Организация наложенной сети передачи данных
Кратко о логической схеме организации нашей сети:
  • Маршрутизация опорной сети (Underlay) внутри ЦОДа реализована по протоколу OSPF.
  • На основе опорной сети работает наложенная сеть (Overlay), которая маршрутизируется по протоколу iBGP (Internal BGP).
  • Каждый ЦОД — это отдельная автономная система. Между ЦОДами настроен eBGP EVPN (External BGP).



А так выглядит схема реально действующей сети в одном из ЦОДов:


Разберём роли каждого устройства сверху вниз.

BR — border router, в каждом ЦОДе их два, через них ЦОД получает доступ в интернет, а также на него приходит трафик платформы и запросы пользователей:

BGW (Border Gateway) — пограничный шлюз, на котором терминируются линки с других ЦОДов (DC Interconnect, DCI), к ним же подключаются BR, SPINE-коммутаторы и NGFW:

NGFW — Firewall — обеспечивает инспекцию и фильтрацию трафика, туда в том числе переехали все правила, которые мы ранее держали на MikroTik:

SP-SW — коммутаторы агрегации Spine, ядро фабрики:

LF-SW — коммутаторы Leaf. Каждый коммутатор Leaf подключается к каждому Spine, а все хосты подключаются уже непосредственно к Leaf:

OOB на схеме — коммутаторы управления серверными IPMI интерфейсами, которые также подключаются к Leaf по M-LAG:

В качестве протокола, отвечающего за туннелирование/инкапсуляцию трафика, мы используем протокол VxLAN. VxLAN подразумевает инкапсуляцию Ethernet-кадров при входе в наложенную сеть и их декапсуляцию при выходе в классическую сеть передачи данных. За выполнение этой процедуры отвечают коммутаторы, называемые VxLAN Tunnel Endpoint или кратко — VTEP. В нашем случае роль VTEP внутри фабрики выполняют коммутаторы уровня доступа — Leaf (LF-SW) и пограничные шлюзы Border Gateway (BGW):

Коммутаторы агрегации Spine внутри фабрики настраиваются в качестве route reflector, чтобы не создавать полносвязную топологию и сократить количество iBGP-сессий для каждого VTEP-коммутатора, которые являются их iBGP-соседями:

Мы уже построили 4 ЦОДа по такой топологии, реальная схема одного из них представлена ниже.


В каждом из четырёх ЦОДов, в которых мы уже построили сети, установлены коммутаторы:
  • 8 BGW
  • 4 Super SPINE
  • 8 SPINE
  • 170 LEAF
  • 75 IPMI
Казалось бы, теперь можно смело эксплуатировать новую сеть и просто заниматься ее расширением. Однако в момент очередного этапа расширения сети, нам подкинула сюрприз одна из моделей коммутатора известного китайского вендора.

Расширение новой сети
В августе 2024-го вследствие резкого роста трафика нам начало не хватать 100-гигабитных каналов между ЦОДами. Но для их апгрейда на существующих Border Gateway коммутаторах уже просто не было свободных 100-гигабитных портов. На замену в срочном порядке были найдены из наличия коммутаторы с 64 портами по 100 Гбит/с. Поставили — и при переключении начались проблемы.

Напомню, что Border Gateway выполняет роль VTEP’а, инкапсулирует и декапсулирует Ethernet-кадры в рамках протокола VXLAN при входе и выходе из наложенной сети передачи данных (со стороны фабрики и со стороны соседних ЦОДов):

Поменяв коммутаторы, мы обнаружили, что VXLAN-трафик полностью перестал проходить через BGW. Отвалилась связь с соседними ЦОДами, развалилось кольцо и т.д.

В ходе чтения мануалов, тестирования, обращения к вендору, выяснилось: данные коммутаторы на аппаратном уровне не поддерживает технологию VXLAN.

Тем не менее для работы с VXLAN есть обходной путь — нужно создать виртуальный интерфейс service type tunnel, привязать к нему свободные физические порты х2 пропускной способностью, с которой, вы предполагаете, должен проходить VXLAN-трафик (так как он 2 раза проходит инкапсуляцию/декапсуляцию). Например, если через устройство должно проходить 100 Гбит/с VXLAN-трафика, нужно зарезервировать 2 пустых интерфейса по 100 Гбит/с. В нашем случае получается, что мы можем использовать только 32 порта вместо 64 и это будет 100% утилизация портов этого устройства.


Мы планируем заменить эти устройства на модульные коммутаторы того же производителя с 4 картами расширения, по 32х100G, они сделаны на других чипсетах, построены на новой ОС и не имеет подобных ограничений. А также продолжим масштабировать сеть, так как нагрузка на RUTUBE продолжает стабильно возрастать.

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

Команды разработки RUTUBE, PREMIER, Yappy объединяются под брендом RUTUBE TECH

Команда — главный актив любой компании, а наша сила — в опыте и синергии. Теперь команды разработки RUTUBE, PREMIER, Yappy объединяются под брендом RUTUBE TECH. Мы долго шли к этому моменту, это важный этап нашего развития.

Объединение наших tech-команд под брендом RUTUBE TECH — это стратегическое решение, которое выведет наши продукты и сервисы на качественно новый уровень. Мы верим, что смелые идеи — это двигатель личного роста. Поэтому мы создаём среду, где талант становится сильнее, воплощаясь в реальные проекты
отметил CEO RUTUBE TECH Данила Овчаров.

Это важный шаг на пути к построению передовой big-tech компании. Вместе с объединением команд разработки в RUTUBE TECH мы делаем оргструктуру более прозрачной и эффективной, внутри единого юр. лица упростятся ротации между проектами. Мы также переходим к общей матрице должностей и performance review. Cильная и организованная команда — необходимая составляющая нашего успеха
CTO RUTUBE TECH Дмитрий Егоров.

Мы уверены, что это начало новой главы в нашей общей истории.

Собственное файловое хранилище для 400 Пбайт видеоконтента

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

В этой статье расскажем, как устроено файловое хранилище RUTUBE с точки зрения SRE, как мы пришли к именно такой конфигурации и как она работает на наших объемах — сейчас это порядка 400 Пбайт или 2 млрд объектов.

Начнём с требований к хранилищу, которые учитывают специфику видеоконтента и архитектуру RUTUBE (кстати, подробнее о ней рассказывается в отдельной статье):
  • Надёжность — базовое требование к любому хранилищу.
  • Низкая стоимость хранения контента — имеет большое значение, когда речь идёт о таких объемах контента, как у нас.
  • Высокое быстродействие — так как обращение к хранилищу может внести существенный вклад в скорость отклика сервиса, а мы хотим, чтобы пользователь не ощущал задержек, когда выбирает просмотр интересного ему видеофайла.
  • Горизонтальная масштабируемость тоже очень важна на таком объеме хранения. Так как объемы заливаемого видео постоянно растут, нам нужно регулярно вводить в эксплуатацию новые серверы. Когда вы систематически запускаете по 200-250 серверов файлового хранилища за раз, нужно делать это так, чтобы это занимало минимальное количество времени и требовалось как можно меньше ручного труда инженеров.

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

RUTUBE — видеохостинг, а не банк. Мы не закладываем требование стопроцентной сохранности данных при любых катаклизмах, которое подразумевает обязательно три реплики данных. Мы минимизируем риски потери пользовательского видео, используем две реплики, делаем резервные копии и на практике имели возможность убедиться, что этого достаточно. Когда мы на 10 часов потеряли целиком дата-центр с хранилищем видео, ни один из дисков реплик не вышел из строя и никакие пользовательские данные не были утрачены. Несмотря на очень большие количества жёстких дисков и серверов, в среднем мы теряем примерно от 3 до 10 дисков в месяц, что совсем немного. Поэтому для нас держать три реплики не целесообразно с точки зрения стоимости владения — цена вырастет кардинально.

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

HDD vs SSD
Как только появились SSD диски, аналитики начали «хоронить» HDD. Первые успехи в массовом производстве и, как следствие, удешевление SSD давали ощущение, что пройдет несколько лет и жесткие диски вымрут, останутся только SSD. Однако мы все всё ещё в той ситуации, когда стоимость хранения на SSD в 10 и более раз превышает стоимость хранения на традиционных жёстких дисках. Развиваются не только SSD, но и в HDD внедряют новые технологии.



Если нужно хранить очень большие объемы данных и при этом нет критичных требований к скорости доступа, конкурентов по стоимости у HDD нет. Типичная комплектация сервера хранилища RUTUBE выглядит следующим образом:



Мы отказались от RAID-массивов и любых СХД на серверах видеохранилища. Используем железо любого вендора, но предъявляем два важных требования:
  • возможность полной настройки, от BIOS и IPMI до установки ОС, с помощью утилит, таких как Redfish, для полностью автоматического ввода в эксплуатацию любого количества серверов;
  • плотность жестких дисков в сервере не менее 36, а по возможности больше.

Объясню на примере, какое значение на наших объемах имеет плотность дисков в сервере. Рассмотрим современный хороший сервер Supermicro, ёмкость которого 90 жестких дисков на 4 юнита.


Если использовать диски по 18 Тбайт, то ёмкость одного сервера будет 1,62 Пбайт.

Соответственно, в обычную стойку на 10 серверов войдет 16 Пбайт. Это почти в 2,5 раза большая плотность по сравнению с более старой конфигураций на 36 дисков, где максимально в одну стойку можно впихнуть 6,5 Пбайт.


Мы сейчас используем в основном диски на 18 Тбайт, но Seagate уже выпустил в продажу 36-терабайтные серверные диски. С ними в одну стойку уже можно уместить 32 Пбайта — то есть всё видеохранилище, накопленное RUTUBE к лету 2025-го, без учёта репликации можно уместить всего в 15 стоек! А раньше это было бы несколько машинных залов.

Почему мы здесь вообще считаем стойки? Во-первых, их аренда тоже стоит денег: средняя цена по рынку где-то 140 тысяч рублей в месяц. В нашем примере при хранении 16 Пбайт на серверах по 90 дисков вместо 36 экономия получится 210 тысяч рублей в месяц. Не столь заметно для большой компании, но это ещё не всё.

Во-вторых, давайте рассчитаем стоимость железа на примере тех же серверов Supermicro 12-го поколения. С одной стороны, сама платформа на 90 дисков почти в два раза дороже, с другой — требуется 10 серверов, а не 25 (для ориентира используем цены европейского поставщика, потому что в России логистические факторы вносят существенные коррективы).


Даже при более дорогой платформе разница на эту ёмкость составит 34% или 36755 €.

Ниже расчёт для полной комплектации серверов со всем железом на платформе одного поколения — здесь разница составляет 21% и больше 128870 €.


Серверное ПО
На серверах видеохранилища RUTUBE стоит:
  • Rocky Linux — дистрибутив, который появился как форк Red Hat Enterprise Linux и должен быть на 100% с ним совместимым.
  • Angie — nginx-совместимый форк, который имеет дополнительные плюшки, например, в виде возможности получения конфигурации сервера и статистики по API и большого набора уже собранных сторонних модулей.
  • Модуль Kaltura для адаптивного видеостриминга.
  • FileHeap — самописное ПО, о котором поговорим дальше.

Указанные характеристики позволяют нам не испытывать каких-либо проблем с производительностью на серверах видеохранилища. Пиковый трафик отдачи RUTUBE составляет примерно 7 Тбит/с, однако он распределяется по CDN (подробнее о CDN RUTUBE читайте в отдельной статье), а нагрузка по сети на один сервер весьма разумная.

Ниже пример графика скорости отдачи непосредственно из файлового хранилища, для наглядности с достаточно слабого старого сервера.


Нагрузка на процессор (старого сервера с однопроцессорной конфигурацией) в пике составляет 40%, обычно меньше.



FileHeap
RUTUBE создавался в 2006 году. Тогда ещё не существовало хороших общепринятых готовых решений для файлового хранилища, Amazon S3 и Ceph появились позже. Поэтому видеохранилище создавалось логичным и доступным тогда способом — с нуля самостоятельно, ориентируясь на основную задачу, то есть хранение видео для видеохостинга.


Наше ПО управления хранилищем называется FileHeap. Оно написано на Python, для хранения всех объектов используется RabbitMQ и PostgreSQL, для кеша — Redis. Это хранилище разработано специально для хранения видео, оптимизировано под него и ни для чего другого не используется.

Рассмотрим основные задачи FileHeap.
  • Управление хранилищем контента — стандартный необходимый набор админских функций: добавлять и удалять серверы, регулировать количество репликаций, добавлять и удалять пользователей и так далее.
  • Загрузка/удаление контента в хранилище. Основная функция для любого файлового хранилища — загрузка файлов и их удаление.
  • Получение сведений о контенте в хранилище — его хэш-суммы, даты и всей информации о файле, которая нам необходима.
  • Раздача контента клиентам. FileHeap на самом деле ничего не раздаёт пользователям, он лишь сообщает сервису балансировки, где находится видео, на каком сервере его необходимо найти: в основном видеохранилище или в реплике на CDN-серверах. На основании чего уже балансер генерирует манифест для потока и отдаёт его на клиент.
  • Актуализация storage — постоянный процесс поддержания необходимого количества активных актуальных реплик (напомним, у нас их две).
  • Валидация storage — постоянный процесс проверки на ошибки доступности жестких дисков непосредственно на сервере. Если что-то выходит из строя, FileHeap сразу же узнаёт об этом через RabbitMQ, автоматически запускается система создания активных реплик на других дисках или серверах.
  • Балансировка storage — процесс перемещения реплик по серверам хранения для равномерного распределения нагрузки на них. Обычно мы добавляем сервера большими пачками, например, по 200 новых за раз. Чтобы не влиять на производительность хранилища, переносим данные в фоновом режиме, когда нагрузка на существующие серверы минимальна. Так постепенно контент размазывается по серверам, что позволяет равномерно балансировать нагрузку между ними.
  • Check online storage — процесс проверки реплики на доступность. Сервер хранилища может даже быть жив, но потерять сетевую связность с CDN — мы это тут же увидим на мониторинге, а балансировщик будет создавать манифест с живыми серверами, чтобы пользователи ничего не заметили.

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



Как видео попадает в видеохранилище
Пользователь может залить видео практически в любом формате и любом качестве. Мы поддерживаем upload для: MP4, AVI, WMV, MOV, FLV, MPEG-1, MPEG-2, MPEG-4, MPG, MPEGPS, 3GPP, WebM, DNxHR, ProRes, CineForm, HEVC (H.265). Загруженное видео нам нужно преобразовать в формат, который мы потом сможем показать на всём многообразии пользовательских устройств. Соответственно, перед тем как положить видео в основное видеохранилище, нужно его перекодировать (у нас основной кодек H.264) и нарезать в разные качества для адаптивного стриминга (поддерживаем от 144p до 8К).

Разберём этот процесс по шагам.


  • Данные от пользователя загружаются на upload-серверы, которые представляют из себя, по сути дела, просто серверы временного хранения, но с очень быстрыми NVMe-дисками. Их не очень много и их основная цель — максимально быстро получить контент от пользователя и начать обработку.
  • Как только видео заливается на upload, оно отдаётся на сервер транскодирования, который у нас называется WatchDuck.
  • Каждое видео нарезается во все поддерживаемые качества (ниже исходного) и в нужном кодеке отправляется в FileHeap.
  • FileHeap раскладывает все экземпляры видео во всех качествах по двум серверам видеохранения обязательно в двух разных дата-центрах.

Особенности хранения очень большого количества файлов
Как мы видим, видео хранится во многих качествах, то есть 400 млн единиц контента превращается примерно в 2 миллиарда объектов хранилища. Это может стать проблемой, если не учесть одну особенность устройства файловых систем, про которую нередко забывают в современной разработке. Возможно, потому что люди всё реже работают на низком уровне, а всё чаще оперируют абстракциями типа контейнеров.

Представьте: проект тестировали на dev, всё было хорошо, всё работало быстро. Отдали на прод, и через какое-то время производительность очень деградировала или и вовсе проект перестал работать. Что случилось?

Дело в том, что для хранения всех, скажем, пользовательских картинок разработчикам выдали один бакет. Однако в большинстве случаев, в частности в случае популярной реализации S3 MinIO, бакет — это просто отдельная папка в файловой системе (больше о работе с MinIO читайте в другой статье из нашего блога).


На 10-20 тысяч файлов — а dev за эти рамки обычно не выходит — всё замечательно работает. Далее проект выходит в продакшен, несколько недель нормально функционирует, бакет заполняется, и начинается резкая деградация скорости ответа MinIO. По достижении примерно 100 тысяч файлов (зависит от файловой системы на серверах) система полностью выходит из строя и уже не отдает ничего. Причем быстро это исправить проблематично.

Чтобы предотвратить подобное поведение, давно придумали хеш-структурированное хранение. Работает следующим образом: берётся, например, хеш-сумма файлов (мы вместо хеш-суммы используем UUID), создаются папки и подпапки по значениям последних символов хеш-суммы, файлы раскладываются в соответствующие подпапки — ни одна директория не переполняется.


Глубина вложенности может быть 2 или 3, при этом используется 4 или 6 последних символов хеш-сумм (или UUID, как в нашем случае). При вложенности 2 получается, что будет создано (16*16) ² = 65536 поддиректорий. При вложенности 3 получится уже ≈16 млн поддиректорий.

Для наглядности рассчитаем подходящую глубину вложенности в случае видеохранилища RUTUBE. На диски по 18/36 Тбайт, которые мы используем, помещается порядка 100 000 файлов — видео тяжелые и в среднем занимают несколько сотен мегабайт. При глубине вложенности 2 получится по 5-10 файлов в директории — это нам подходит. Если сделать глубину вложенности 3, то можно разложить 1 млрд файлов (примерно по 60 файлов в каждой директории). Таким образом можно в тот же самый бакет заливать сколько угодно файлов, просто регулируя глубину вложенности по папкам.

Хеш-структурированное хранение даёт не столько ускорение, сколько просто возможность создать рабочую масштабируемую конфигурацию и хранить миллиарды файлов без потери производительности. Потому что иначе, если в одном каталоге будет слишком много объектов, файловая система в Unix-системах будет отвечать очень медленно вплоть до полной невозможности с ней работать.

Раздача видеоконтента
Контент, который мы складываем в видеохранилище, мы храним не просто так, а чтобы наши зрители в любой момент могли посмотреть что-то интересное для себя — сделать это без задержки и с любого устройства. За минимизацию задержки отвечает двухуровневый CDN, где на первом уровне кешируется холодный контент, а на втором — расположенном как можно ближе к зрителям — горячий, то есть наиболее популярный в данном регионе.


А для того, чтобы видео воспроизводилось на самых разных устройствах — от компьютеров до бюджетных телефонов и Smart TV — мы как раз и перекодируем исходное видео и используем алгоритмы адаптивного стриминга.

Существует два основных протокола адаптивного видеостриминга: HLS (HTTP Live Streaming, разработан Apple в 2009) и DASH (Dynamic Adaptive Streaming over HTTP, разработан рабочей группой MPEG в 2011 году). Они отличаются способами организации манифеста, но базово устроены похоже и вы точно сталкивались с их работой. Именно они отвечают за то, чтобы, если у пользователя ухудшилось качество связи, воспроизведение не прервалось, а просто уменьшить разрешение видео.

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

Чтобы на лету нарезать нужные фрагменты, мы используем nginx-модуль vod от компании Kaltura. Он читает исходный mp4-файл и динамически его сегментирует в нужный протокол: HLS (.m3u8 + .ts) или DASH (.mpd + .mp4).

Ниже короткие примеры конфигурации:
location /hls-vod/ {
    alias /media/;
    vod hls;
    vod_bootstrap_segment_durations 2000;
    vod_bootstrap_segment_durations 2000;
    vod_segment_duration 4000;
    vod_base_url "$video_id.mp4/";
    vod_mode local;
    hls_metadata_cache 16m;
    vod_align_segments_to_key_frames   on;
    vod_hls_segment_file_name_prefix   "segment";
    vod_manifest_segment_durations_mode accurate;
}
location /dash-vod/ {
    alias /media/;
    vod dash;
    vod_bootstrap_segment_durations 2000;
    vod_bootstrap_segment_durations 2000;
    vod_segment_duration 4000;
    vod_align_segments_to_key_frames on;
    vod_manifest_duration_policy min;
    vod_dash_manifest_format segmenttemplate;
    vod_dash_profiles urn:mpeg:dash:profile isoff-live:2011;
    vod_base_url "$video_id.mp4/";
}

Здесь созданы две локации, где в качестве root мы указываем одну и ту же папку с одними и теми же видеофайлами. При обращении к одной пользователь будет получать манифест DASH, к другой — HLS. Из основных параметров — длина чанков, и для разных форматов стриминга она может быть задана по-разному.

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

Пример запроса от плеера за HLS:

/hls-vod/0x5000c500c32ab248/77/ec/a8ae0fb363484dc180705b8a5dbc77ec.mp4/index.m3u8

Пример запроса от плеера за DASH:

/dash-vod/0x5000c500c32ab248/77/ec/a8ae0fb363484dc180705b8a5dbc77ec.mp4/manifest.mpd

А ещё vod-модуль позволяет на лету генерировать превью (thumbnails) — то есть картинку из видеофайла как стоп-кадр по временной метке в миллисекундах. Зачем это нужно? Во-первых, для перемотки по таймлайну, чтобы пользователь на миниатюрах видел, куда перетаскивает курсор. Во-вторых, для создания обложек. Авторы, которые заливают видео на RUTUBE, могут загрузить свою обложку, а могут использовать любой стоп-кадр, который и будет показываться на главной странице.

Надо заметить, что подход переупаковки на лету, кроме того что помогает экономить место в хранилище и обеспечивает скорость доступа к видео, еще и не вычислительно затратный. Единственную нагрузку создаёт генерация превью, нарезка чанков фактически не использует CPU, потому что это просто сегментирование MP4, никакой сложной обработки видео для этого не требуется.

Выше на графике скорость раздачи со всех вместе взятых серверов файлового хранилища. Пик нагрузки составляет ≈525 Гбит/с при общем трафике нашей платформы около 7Тбит/с. Это значит, что большая часть раздачи происходит с CDN-серверов, из хранилища забирается только незакешированое по CDN видео.

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


Также для экспериментов и резервного копирования мы используем S3 у разных партнеров. Но, как показала практика, производительность S3 в холодной конфигурации оставляет желать лучшего. Мало у кого из партнеров её в принципе достаточно для того, чтобы раздавать видео. В некоторых случаях, максимум, на что можно рассчитывать, это положить бэкап и забыть. Всё остальное — непозволительная роскошь, использовать что-либо кроме холодной конфигурации слишком дорого. Поэтому мы и остановились на использование собственных обычных серверов без СХД и RAID-массивов.

Итоги
Спроектированная еще в 2006 году система хранения видео RUTUBE, конечно, многократно дорабатывалась, в неё добавлялись новые функциональные элементы, но на уровне базовой архитектуры она сохранила свою простоту, которая сейчас позволяет нам легко масштабироваться под любые нагрузки.

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

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

Как устроен CDN RUTUBE: железо, сеть, ПО

Меня зовут Дмитрий Иванов, я начальник отдела эксплуатации IT-инфраструктуры RUTUBE, что на наши деньги переводится как SRE-тимлид. В этой статье разберу задачу доставки контента и расскажу о решениях, которые помогают нам в RUTUBE.

Дано: с одной стороны у нас 17,7 млн ежедневных пользователей, а с другой — 400 млн единиц контента. Оба эти показателя постоянно увеличиваются, а география присутствия пользователей расширяется.

Требуется: показывать всем нашим пользователям видео из библиотеки быстро, надежно и эффективно.

Как взаимосвязаны эти приоритеты и в чём проблема:
  • Раздавать видео по всей нашей большой стране и за её пределы из одного хранилища, очевидно, не быстро — пинг от Москвы до Владивостока около 100 мс.
  • Некоторые видео популярнее других, то есть одновременно их хотят посмотреть очень много пользователей. Если показывать такие видео напрямую из видеохранилища, это не будет надежно — потому что вся нагрузка пойдет на один сервер, серверу может стать плохо и «кина не будет».
  • Чтобы раздавать контент быстро, надо его разместить как можно ближе к зрителю. Но с учётом размера нашей библиотеки мы не можем её всю разложить по всем точкам присутствия пользователей — это будет неэффективно.

Решением этой задачи, как понятно из названия, является CDN — Content Delivery Network. Далее в статье сначала определим особенности нашего контента и то, какие требования к CDN в связи с этим возникают, а потом будем искать способы им соответствовать.

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


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



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

Два уровня кэширования
Для эффективного и быстрого доступа к горячему и холодному контенту соответственно выделим в CDN два вида кэша: L1 для холодного и L2 для горячего.


Исходный контент находится в видеохранилище. Далее разместим L1-кэш — уровень, который предназначен для холодного контента, ближе к хранилищу и с прямым доступом к нему. А L2, то есть горячий кэш, расположим как можно ближе к зрителям, в том числе в регионах. При этом, как показано на схеме, у региональных CDN-серверов с горячим контентом нет доступа в основное видеохранилище, вместо этого используется L1 для подкачки на уровень L2.

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

Ниже в таблице характеристики серверов для раздачи горячего и холодного контента. Для горячего L2-кэша в среднем не требуются очень производительные серверы и скорости отдачи в 20-40 ГБит/с обычно достаточно, зато их нужно много. L1 же прокачивает через себя больше контента, так как является промежуточным звеном и обеспечивает доступ к тому самому длинному хвосту из миллионов видео. Поэтому холодные серверы обычно мощнее и оснащены сетевыми картами на 100 или 200 Гбит/с.

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



Как раздать 200 Гбит/с с одного сервера
Если просто взять сервер, вставить в него сетевую карту на 200 Гбит/с, то, к сожалению, без дополнительных усилий скорость отдачи будет намного ниже. Чтобы утилизировать всю пропускную способность сетевой карты, нужно знать, что такое NUMA — Non-Unified Memory Access.

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


Схема работы с памятью на многопроцессорных серверах
Для того, чтобы раздавать с одного сервера все 200 Гбит/с, нужно привязывать прерывание сетевых карт к ядрам того процессора, который их обрабатывает, чтобы не обращаться к памяти через шину.

К счастью, для сетевых карт Mellanox, которые мы используем, уже есть специальные скрипты, которые позволяют настроить NUMA (см. github.com/Mellanox/mlnx-tools). Скрипты имеют возможность выбора профиля: minimum latency предназначен для оптимизации задержек, а high throughput — для максимальной пропускной способности. Нас в данном случае интересует максимальная пропускная способность.

Кэширование
Разберём, как разложить контент по кэширующим серверам. Теоретически можно, например, с помощью rsync копировать видео из хранилища. Это даже будет работать, но придется городить целую схему, чтобы обеспечить эффективность и надёжность. Поэтому у нас кэширование организовано в обратную сторону: вместо того чтобы нам закачивать на CDN-серверы какие-то файлы, CDN сам скачивает нужные данные из хранилища по мере надобности при помощи обратного проксирования.

Мы используем nginx proxy_pass для скачивания и, что логично, делаем и proxy_cache, чтобы сервер складывал то, что скачал, в свой кэш. Также мы разделяем контент на чанки по 4 секунды, то есть скачиваются не сразу гигабайтные видеоролики, а чанки в несколько мегабайт.

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

В зависимости от этого параметра контент будет попадать на диск только после нескольких обращений за ним.

Распределенное кэширование
На CDN-серверы под кэш мы ставим обычно 8-32 диска, но не объединяем их в общий RAID. Потому что, если вдруг RAID развалится, то потребуется долгая процедура rebuild. Вместо этого можно использовать такой трюк:
proxy_cache_path /media/d0 keys_zone=d0:512m levels=2:2:1 use_temp_path=off;
proxy_cache_path /media/d1 keys_zone=d1:512m levels=2:2:1 use_temp_path=off;
proxy_cache_path /media/d2 keys_zone=d2:512m levels=2:2:1 use_temp_path=off;
proxy_cache_path /media/d3 keys_zone=d3:512m levels=2:2:1 use_temp_path=off;
proxy_cache_path /media/d4 keys_zone=d4:512m levels=2:2:1 use_temp_path=off;
proxy_cache_path /media/d5 keys_zone=d5:512m levels=2:2:1 use_temp_path=off;

split_clients $request_uri $disk_cache {
  16.66%  d0;
  16.66%  d1;
  16.66%  d2;
  16.66%  d3;
  16.66%  d4;
  16.66%  d5;
  *   d0;
}


Здесь на каждый диск, который есть в сервере, определяется своя зона кэширования и через директиву split_clients разделяются запросы, которые приходят за контентом в кэш (подробнее см. документацию). Таким образом мы равномерно размазываем весь контент по всем дискам сервера.

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

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



Распределенное кэширование устроено так, что каждый запрос, попадающий в nginx, проходит две стадии:
  • балансировка с consistent-hash,
  • непосредственно кэширование.

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

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

Поиск ближайшего сервера
После того, как мы разложили видео по горячим и холодным кэшам, нужно адресовать запрос от пользователя на правильный сервер так, чтобы:
  • это был ближайший сервер, то есть чтобы было быстро;
  • сбалансировать нагрузку, чтобы это было надёжно;
  • и при этом эффективно расходовать сетевые ресурсы.

Для выбора ближайшего сервера существует BGP Anycast.


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

Сделать Anycast достаточно несложно, нужно:
  • купить сеть, которую мы будем анонсировать;
  • поставить на сервер демон маршрутизации, мы используем bird;
  • всё, profit.


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

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

Чтобы определять ближайший к пользователю сервер с учётом физического расстояния, существует другая технология — GeoIP. GeoIP — это база данных производства компании MaxMind — по сути бинарный файл, который загружается в nginx-модуль. С его помощью в ответ на запрос с IP-адресом nginx возвращает: регион местоположения пользователя, город, а также, возможно, информацию об операторе.

Однако в GeoIP-базе MaxMind есть неточности, причём количество этих неточностей растет, поэтому нам приходится накладывать на неё правки.

Ниже представлен пример корректировки GeoIP-базы:
  • заводим список, где пишем IP-адреса и то, к какому региону их нужно относить;
  • через директиву geo формируем переменную, которая в ответ на IP-запрос возвращает расположение;
  • делаем map на приоритетное использование региона из нашего списка.
# geoip_corrections.conf example
198.51.100.0/24 Moscow; # RFC 5737 TEST-NET-2

# nginx.conf
geo $region_name_from_geo {
	# The variable will be empty if IP address does not match.
	include geoip_corrections.conf;
}

# Now we combine region names.
map $region_name_from_geo $region_name {
	# If there is no correction.
	''  	$geoip2_region_name;
	# And if there is.
	default $region_name_from_geo;
}


Это было бы не такой уж большой проблемой, если бы не влияло на пользователей. Но, к сожалению, о необходимости поправок мы обычно узнаём из обращений зрителей. Как это происходит? Неправильный GeoIP приводит к двум нежелательным ситуациям:
  • Если определить неправильный регион, то можно направить зрителя не на ближайший сервер. Зритель скорее всего этого не поймёт и не будет жаловаться, но мы же хотели так не делать.
  • Если неверно определить страну, то из-за локальных лицензионных ограничений контент может вовсе стать недоступен для пользователя. Тогда, возможно, он обратится в поддержку, мы всё исправим, но зритель на какое-то время останется без любимого шоу и будет расстроен.

Есть другой вариант GeoIP-базы, но с ними тоже есть свои особенности. Во-первых, в базе MaxMind 32 млн записей, а в geoip.noc.gov.ru — 4,5 млн записей. Допустим, это не так страшно, потому что это записи о сетях и они могут быть разного размера. Во-вторых, в базах различаются названия регионов, например, Chuvashia вместо Chuvash Republic. И третье, что нам действительно мешает перейти на эту базу: порядка 5 млн IP-адресов со всей страны в ней приписаны к Москве. То есть она опять не позволит всегда корректно определять ближайшие кэш-серверы, некоторым зрителям из регионов по-прежнему придется ждать лишнее время, чтобы посмотреть видео на RUTUBE.

Балансировка
Все запросы зрителей за видео идут не напрямую к одному из CDN-серверов, с сначала попадают в наше самописное ПО, которое называется видеобалансер.

Видеобалансер:
  • определяет, где находится зритель, по GeoIP-базе;
  • учитывает оператора, чтобы раздавать контент по возможности минуя меж-операторские стыки;
  • считает температуру видео, то есть горячий это контент или холодный;
  • мониторит нагрузку на серверы CDN;
  • распределяет зрителей на ближайший свободный сервер, где есть нужный контент.

Ниже скриншот из видеобалансера — у него утилитарный инженерный интерфейс, не самый красивый, но рабочий.


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

Итоговая схема раздачи приведена на схеме ниже.


Когда пользователь открывает плеер RUTUBE происходит следующее:
  • клиент отправляет запрос за видео в видеобалансер;
  • балансер по GeoIP определяет ближайший к пользователю CDN;
  • балансер знает температуру запрашиваемого видео, он мониторит CDN-серверы и в соответствие с этими данными возвращает мастер-манифест на клиент со ссылками на плейлисты с чанками запрашиваемого видео;
  • по манифесту зритель уже направляется на конкретные CDN-серверы (с учётом всех уровней кэширования, которые мы рассмотрели ранее).

Edgestat на схеме выше — это наш сервис мониторинга, с помощью которого балансер узнаёт о состоянии серверов. Edgestat написан на Go и напрямую из псевдо-файловой системы /proc собирает параметры производительности процессора, сети и пр. Ниже пример файла, который модуль мониторинга возвращает в балансер:
{
  "net": {
	"tx_bytes": 3070389798640361
  },
  
  "cpu": {
	"user": 821038789,
	"nice": 7206904,
	"system": 1068090496,
	"idle": 8891017069,
	"iowait": 140402173,
	"irq": 183963736,
	"softirq": 1899188205,
	"steal": 0,
	"guest": 0,
	"guest_nice": 0
  },
  
  "tcp": {
	"out": 4009339463073,
	"retrans": 107337952956
  },
  
  "pressure": {
	"cpu": 0.42,
	"io": 1.77,
	"memory": 0.77,
	"irq": 1.7
  }
}


Разберем подробнее блок «pressure». Pressure Stall Information — подсистема внутри Linux-ядра, которая отслеживает задержки в работе системы, связанные с нехваткой железных ресурсов, таких как CPU, устройств ввода-выводы, память.

По меркам ядра PSI появилась относительно недавно, около 7 лет назад. А также буквально пару лет назад — совсем новинка — в неё добавили отслеживание задержек по обработке прерываний — IRQ (значения в микросекундах).

Мониторить irq особенно полезно на серверах, обрабатывающих много трафика, так как в таких условиях обработка прерываний от сетевой карты уже может вносить существенный вклад в общую производительность.
$ ls /proc/pressure
cpu  io  irq  memory
$ cat /proc/pressure/io
some avg10=2.50 avg60=2.38 avg300=2.34 total=13968383315
full avg10=2.35 avg60=2.22 avg300=2.15 total=12888830571
$ cat /proc/pressure/irq
full avg10=0.64 avg60=0.36 avg300=0.24 total=7294258624
$


На серверах, прокачивающих большой трафик, например, как у нас в Тбит/с, показатель irq pressure часто скачет и сообщает нам, что сервер перегружен, даже если этого не видно по другим характеристикам. Внедрение PSI помогло нам окончательно избавить CDN от тормозов — повысить скорость.

Для обеспечения дополнительной надёжности в схеме раздачи в мастер-манифесте содержится не один, а два CDN-сервера: основной и резервный. Мастер-манифест — это по сути плейлист из плейлистов, выглядит примерно так:
#EXTM3U
#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=478000, FRAME-RATE=25, CODECS="avc1.42c01f, mp4a.40.2", RESOLUTION=256x144
https://cdn-server
#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=478000, FRAME-RATE=25, CODECS="avc1.42c01f, mp4a.40.2", RESOLUTION=256x144
https://backup-cdn


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

Сеть
Работа с сетью — также неотъемлемая часть эффективной реализации CDN, ведь на таких расстояниях и объемах трафика сеть легко может стать узким местом.

Ниже неполный вывод команда ip, который показывает список сетевых интерфейсов одного из наших CDN-серверов.
$ ip -c a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
	link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
	inet 127.0.0.1/8 scope host lo
   	valid_lft forever preferred_lft forever
2: aggr0: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 1500 qdisc mq master bond0 state UP group default qlen 1000
	link/ether xx:xx:xx:xx:xx:x1 brd ff:ff:ff:ff:ff:ff
3: aggr1: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 1500 qdisc mq master bond0 state UP group default qlen 1000
	link/ether xx:xx:xx:xx:xx:x2 brd ff:ff:ff:ff:ff:ff
4: aggr2: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 1500 qdisc mq master bond1 state UP group default qlen 1000
	link/ether xx:xx:xx:xx:xx:x3 brd ff:ff:ff:ff:ff:ff
5: aggr3: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 1500 qdisc mq master bond1 state UP group default qlen 1000
	link/ether xx:xx:xx:xx:xx:x4 brd ff:ff:ff:ff:ff:ff
6: anycast2: <BROADCAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
	link/ether xx:xx:xx:xx:xx:xa brd ff:ff:ff:ff:ff:ff
	inet 198.51.100.79/24 brd 89.248.230.255 scope global anycast2
   	valid_lft forever preferred_lft forever
7: bond0: <BROADCAST,MULTICAST,MASTER,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
	link/ether xx:xx:xx:xx:xx:xb brd ff:ff:ff:ff:ff:ff
	inet 192.0.2.1/24 brd 192.0.2.0 scope global bond0
   	valid_lft forever preferred_lft forever
8: bond1: <BROADCAST,MULTICAST,MASTER,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
	link/ether xx:xx:xx:xx:xx:xc brd ff:ff:ff:ff:ff:ff
	inet 203.0.113.2/24 brd 203.0.113.0 scope global bond1
   	valid_lft forever preferred_lft forever
$


То есть на каждом из десятков CDN стоят серверы с десятками же сетевых интерфейсов — всем этим нужно эффективно и надёжно управлять. Например, везде должны быть правильные согласованные с операторами настройки. Использовать для этого штатный NetworkManager не очень удобно, поэтому мы перешли на systemd-networkd.

systemd-networkd — это демон для настройки сети. Как и всё в systemd, все настройки в нём хранятся в текстовых файлах, что удобно для автоматизации, например, Ansible.

Ниже пример файла .link, который настраивает физические сетевые устройства. В нём задаётся максимальный размер буферов для эффективной раздачи больших объемов трафика.
# /etc/systemd/network/10-aggr0.link

[Match]
MACAddress=xx:xx:xx:xx:xx:xx

[Link]
Name=aggr0
RxBufferSize=max
TxBufferSize=max

В файле .netdev описываются виртуальные сетевые устройства, например, bond — объединение двух физических сетевых устройств в одно логическое для большей отказоустойчивости или скорости отдачи.
# /etc/systemd/networkd/bond0.netdev
[NetDev]
Name=bond0
Kind=bond

[Bond]
Mode=802.3ad
MIIMonitorSec=100ms
TransmitHashPolicy=layer3+4

Другой пример полезной настройки в .netdev — dummy-интерфейс. По сути это loopback, который мы используем для того, чтобы на него назначать Anycast-адрес. Можно назначить этот Anycast-адрес как второй ip-адрес на сетевой интерфейс, но тогда, если сетевой интерфейс перестанет работать, то и Anycast-адрес перестанет быть доступным.

# /etc/systemd/network/anycast2.netdev

[NetDev]
Name=anycast2
Kind=dummy


Третий вид файлов systemd-networkd это .network. Они описывают стандартные настройки: IP-адреса, gateway, маршруты и т.д.

# /etc/systemd/network/aggr0.network
[Match]
Name=aggr0
[Network]
Bond=bond0

# /etc/systemd/network/bond0.network
[Match]
Name=bond0

[Network]
Address=12.34.56.78/28
Gateway=12.34.56.79


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


Проблема в том, что при подключении двух операторов к одному серверу «в лоб» получается асимметричная маршрутизация. Её эффект показан на иллюстрации слева:
  • на anycast-адрес второго оператора приходит запрос,
  • ответ на этот запрос направляется default gateway, который скорее всего будет указывать в первого оператора.

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

Как сделать, чтобы ответ на запрос уходил тому же оператору, от которого он пришел? При помощи fwmark — меток сетевых пакетов. Это виртуальные метки, которые ставятся на сетевые пакеты и при этом существуют в рамках одного Linux-хоста. Они позволяют сделать такой трюк:
chain prerouting {
	type filter hook prerouting priority mangle; policy accept;
	iifname "bond0" ip daddr 198.51.100.0/24 meta mark set 0x00000010 accept comment "isp1 fwmark"
	iifname "bond1" ip daddr 198.51.100.0/24 meta mark set 0x00000020 accept comment "isp2 fwmark"
}


Выше представлены правила nftables: на запросы, приходящие через сетевой интерфейс «bond0», ставим на него метку 0x00000010 (неважно какую, важно, что не ноль), а на «bond1» ставим другую метку.

В соответствие с этим прописываем и исходящие правила маршрутизации:
$ ip rule
0:  	from all lookup local
110:	from 198.51.100.0/24 fwmark 0x10 lookup 100 proto static
120:	from 198.51.100.0/24 fwmark 0x20 lookup 101 proto static
32766:  from all lookup main
32767:  from all lookup default
$


Здесь в приоритете 110 написано: если запрос с Anycast-адреса и есть метка 0x10, то нужно посмотреть в таблицу номер 100, где будет default gateway для bond0; а если метка другая — то надо смотреть в другую таблицу, где уже будет default gateway для bond1.

Воедино входящие правила фаервола и исходящие правила маршрутизации связывают несколько sysctl:
net.ipv4.tcp_fwmark_accept=1
net.ipv4.fwmark_reflect=1
net.ipv4.conf.all.src_valid_mark=1


Здесь написано следующее: если на сетевой пакет, который пришел на данный сетевой интерфейс, есть метка, то её нужно скопировать на весь сокет и выставлять на все исходящие пакеты в рамках соединения.

Player events
Мы мониторим состояние серверов, но кроме того, для надёжности, плеер пользователя в случае каких-либо проблем научен отправлять специальные сообщения, которые называются player events. Если на клиенте, в вебе, в мобильном или Smart TV приложении RUTUBE возникнут какие-то проблемы с доступом к CDN, то мы узнаем об это по резервному маршруту. Например, сбои в каком-то сегменте сети, дадут всплеску player events и наш мониторинг тут же отреагирует.

И последняя в этой статье оптимизация: простая, но важная. Мы уже говорили, что используем демон маршрутизации BIRD, чтобы анонсировать Anycast. Логично, что тогда должен быть запущен и nginx, чтобы он что-то раздавал. Если запустить bird, но не запустить nginx (а у нас такое бывало), то зрители придут и попадут в черную дыру. Чтобы такого избежать, можно использовать такой трюк:
# /etc/systemd/system/bird.service.d/requisite_nginx.conf
[Unit]
# If nginx is stopped, bird will be stopped as well.
# If nginx is not running, bird will refuse to start.
# If bird is stopped, nginx is not affected.
# If nginx is restarted, bird is not affected.
Requisite=nginx.service


Главное тут в последней строчке: если nginx запущен, то bird работает; если nginx выключить, то bird погаснет; если nginx не запущен, то и bird не запустится.

Итоги
В этой статье мы рассмотрели архитектуру CDN RUTUBE: как кэшировать, балансировать нагрузку, обеспечивать отказоустойчивость или, в крайнем случае, деградировать незаметно для зрителей. Как сделать CDN, чтобы показывать нашим пользователям видео быстро, надежно и эффективно.

Вот три основных момента, на которые нужно обратить внимание, если вы строите свой CDN:
  • Если вы только начинаете строить CDN, то начните с BGP Anycast как наиболее простого варианта. Но не ограничивайтесь им и подключайте узконаправленные средства балансировки, подходящие для вашего профиля трафика и нагрузки.
  • Используйте разные источники мониторинга: и изнутри системы, и снаружи.
  • Pressure Stall Information — это ваш союзник в борьбе с тормозами.

Архитектура национального видеохостинга: путь RUTUBE к 10 Тбит/с с использованием своей CDN

За полгода с июля 2024 года большинство аудиторных и технических показателей RUTUBE выросло в разы: количество ежедневных пользователей выросло почти в 4 раза; количество видео, ежедневно загружаемых на видеохостинг — в 3 раза, с 330 тыс. до 1 млн единиц контента; CDN-трафик — в 4 раза и в пиковые часы превышает 7 Тбит/с. Как архитектура сервиса показала себя в условиях продолжительного «нагрузочного тестирования» и как команда переживала такой рост нагрузки, читайте в этой статье.



Меня зовут Эльдар Ниязов, я директор департамента развития и эксплуатации ИТ-инфраструктуры RUTUBE. В статье разберу верхнеуровневое устройство основных компонентов видеохостинга: раздачу видео и CDN, загрузку на платформу, хранение, воспроизведение и прямые трансляции. А в следующих статьях этого блога мы с коллегами рассмотрим каждый элемент подробнее и копнём вглубь реализации.

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

Видеохостинг работает на вебе, Android, iOS, Smart TV. Поддерживает просмотр горизонтальных и вертикальных видео, прямые трансляции и вещание ТВ-каналов.

По данным Mediascope за декабрь 2024 г. сервис посетило 78,3 млн человек, а среднее количество ежедневных пользователей составляет 18 млн. RUTUBE смотрят по всей России: из городов миллионников и населённых пунктов поменьше (почти половина зрителей живут в городах с населением менее 100 тыс. чел.).


Объём видеохранилища на сегодня составляет более 250 Пбайт, в нём хранится около 400 млн видео и каждый день их количество увеличивается в среднем на 1 млн — столько новых видео пользователи в среднем загружают на платформу.

Чтобы обеспечить быстрый и надёжный доступ к контенту, наша собственная геораспределенная сеть доставки контента (CDN) состоит из 6 ЦОДов в Москве и одного в Санкт-Петербурге, 4000 серверов (из них 700 серверов для обработки видео), более 200 CDN-серверов, более 25 городов присутствия, страны СНГ и даже США. Ёмкость сети составляет более 10 Тбит/с.


Видеохостинг разрабатывает собственная, полностью in-house команда, которая так же как и ИТ-инфраструктура смогла гибко перестроиться и масштабироваться.

RUTUBE — это более 100 микросервисов. Кодовая база бэкенда написана преимущественно на одном из трёх языков: Python3 — 600 тыс. строк, Node.js — 600 тыс. строк, Golang — 300 тыс. строк. В инфраструктуре у нас высокий уровень автоматизации: IaC (Terraform, Ansible) составляет примерно 200 тыс. строк.


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


Такое разделение позволило:
  • повысить скорость доставки контента до конечного пользователя;
  • сэкономить на хранилище и оптимизировать использование серверных ресурсов;
  • снизить затраты на передачу данных по сети.

Подходящий узел CDN мы можем определять по GeoIP (т.е. ближайший) или по ISP (Internet Service Provider). Наши CDN-серверы, естественно, тоже подключены к сети через провайдеров, и в некоторых случаях с точки зрения задержки выгоднее избежать лишних стыков между провайдерами, чем выбрать ближайший, но находящийся в другой сети CDN. В предельном случае грамотное использование CDN позволило нам снизить время доставки с 300 мс до 10 мс.

Стандартный сетап сервера CDN с холодным контентом: 512 Гбайт оперативной памяти, 100 Тбайт дисков под хранение и 2 диска под ОС, 2 сетевые карты — 100 Гбит/с на отдачу, 25 Гбит/с на подгрузку контента из основного хранилища.

Горячий CDN-сервер мощнее и в нём стоят одинаково быстрые сетевые карты на 100 Гбит/с на download и на upload.

График раздачи в течение типичной недели выглядит примерно так.


Зеленая линия — трафик с CDN первого уровня, раздача холодного контента, красная линия — трафик с CDN второго уровня, синяя линия — суммарный трафик

Воспроизведение видео
Рассмотрим, что происходит, когда пользователь запускает видео в плеере RUTUBE.
  • GET-запрос отправляется в балансировщик, который по GeoIP и ISP рассчитывает «расстояние» до CDN-серверов.
  • Балансировщик видео получает из нашего хранилища FileHeap адрес CDN, на котором есть видео, и адрес самого видео.
  • Отдает плееру М3U8-манифест c указанием основного и запасного CDN-узла и адрес исходного видео.
  • Плеер идёт за видеочанками сначала на CDN второго уровня. Если контент не популярный и его нет в горячем кеше, то плеер запрашивает видео с CDN первого уровня, если и там не оказалось — то идет в основное хранилище.


Загрузка видео
  • В среднем каждый день пользователи RUTUBE загружают на видеохостинг 1 млн новых видео. Загрузка видео — важный и нагруженный сервис и один из самых сложно масштабируемых. Расскажу, что происходит, когда пользователь в личном кабинете нажимает «+», «Загрузить видео».
  • Балансировщик загрузки отдаёт адрес сервера, на который отправляется загрузка (под загрузку у нас выделено более 800 серверов).
  • Наш собственный сервис Watchduck, который по нашей традиции назван в честь животного, нарезает видео на разные качества. Watchduck написан на Python3, умеет транскодировать видео и на GPU, и CPU, что нам очень пригодилось в момент бурного роста нагрузки и невозможности быстро увеличить парк видеокарт. Нарезкой разных качеств и обложек занимается ≈ 700 серверов.
  • После нарезки Watchduck идёт в FileHeap, под который отведено по 10 серверов в основных ЦОДах и который обеспечивает балансировку нагрузки на хранилище. FileHeap получает адрес, куда и в какое именно хранилище положить загрузку.
  • Естественно вся загрузка и нарезка происходит чанками — по 3–6 Мбайт.


Устройство видеохранилища. Альтернатива S3
Для хранения видео мы используем не стандартный S3, а собственное приложение. Наше решение существует давно, хорошо себя зарекомендовало и в своё время было быстрее, чем альтернативы, например, Ceph.

Главный минус S3 для нас — это необходимость в кеширующих серверах, причем в определённом соотношении с бакетами, чтобы не тормозило. А большое количество дополнительных серверов — это большие затраты на покупку и обслуживание оборудования. ​​


Кроме экономии на оборудовании собственная реализация хранилища даёт следующие плюсы:
  • возможность глубокого мониторинга на каждом этапе;
  • гибкая адаптация под свои нужды;
  • не надо платить за лицензии и поддержку;
  • скорость и производительность.

Мы используем партнёрские S3 в качестве резерва, арендуем мощности у крупных известных поставщиков. Однако максимальная скорость отдачи, которую нам могут обеспечить сторонние S3, составляет всего 60 Гбит/с. Иначе их сервисы деградируют и страдают другие клиенты.

Видеохранилище RUTUBE состоит по сути из трёх частей:
  • DS-origin — само хранилище, разнесенное по разным ЦОДам;
  • FileHeap — балансировщик;
  • CDN-серверы.

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

Что в результате? Ниже графики нагрузки в обычную неделю без происшествий и пиковой посещаемости, это скорость отдачи контента напрямую с основного хранилища DS-origin (т.е., когда контента не оказалось на CDN первого и второго уровня) и с использованием партнёрских S3.


Красная линия — график отдачи трафика с основного собственного хранилища RUTUBE, пиковая скорость в среднюю неделю составляет порядка 500 Гбит/с. Синия линия — отдача с партнерских S3, скорость составляет 35–60 Гбит/с
Прямые трансляции
Когда пользователь хочет провести прямой эфир через RUTUBE и создает трансляцию, под капотом сервиса происходит следующее:
  • видеопоток от пользователя поступает на RTMP-балансировщик;
  • RTMP-балансировщик распределяет поток в разные ЦОДы (основной и запасной);
  • далее поток попадает в сервис live-транскодирования на FFmpeg, где нарезается в разные разрешения — от 144 до 4к;
  • транскодирование также проходит через балансировщик, выполняется в разных ЦОДах и резервируется по двум ссылкам;
  • из балансировщика поток попадает на CDN, ближайший к зрителю трансляции.

Наши трансляции минимально отстают от реального времени — на 3–5 секунд.


Схема работы прямых трансляций RUTUBE. Genetta — собственный сервис, агент по управлению ffmpeg на live-транскодерах

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

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

Где потребовались дополнительные ресурсы при росте нагрузки:
  • Загрузка и обработка — первое, где требовалось масштабирование. Обработка видео, его нарезка во все качества — ресурсоемкий процесс, а когда авторы резко начинают заливать в видеохостинг все свои ролики, накопленные за годы творчества, это может стать потенциальной проблемой. Мы арендовали 1000 дополнительных серверов у партнёров, а сам процесс не потребовал изменений, просто у балансировщика появился больший пул серверов.
  • Хранение — новые видео надо где-то хранить. На всякий случай подстраховались арендными S3 и увеличили количество кеш-серверов.
  • Раздача и трансляции имеют больший запас прочности из-за особенностей архитектуры, поэтому тут удалось подстраховаться тем, что быстро купили несколько серверов в частном порядке и выиграли время на полноценную закупку.

Специфика видеохостинга в том, что нагрузки на запись гораздо меньше, чем нагрузки на чтение. Поэтому мы держим по одному мастеру в каждом ЦОДе и много реплик на чтение.


Итого
Основные элементы архитектуры RUTUBE, заложенные разработчиками сервиса ранее, позволили нам горизонтально масштабироваться при кратном росте нагрузки. Конечно, когда в течение августа 2024 г. трафик увеличился примерно в два раза, нам с командой пришлось попотеть: перераспределять имеющиеся ресурсы, сетапить новые, подключать арендные мощности, готовиться к сценариям плавной деградации и т.д. Для нас главное — команда быстро и чётко прошла критическую фазу масштабирования. Теперь мы можем в чуть более равномерном, но по-прежнему высоком темпе наращивать мощности: и железные, и человеческие. Потому что планы у нас амбициозные ;)