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

Этот пост публикуется для того чтобы мудрые дяди пробили фейспалм и сказали как делать правильно. Для остальных, как мне кажется это не имеет никакого смысла.
Когда речь заходит о работе с медиа на сайте, кажется, что все просто: загрузил файл и он отобразился. Но на деле за этим стоит целая цепочка сервисов, оптимизаций и решений, которые мы принимали не один вечер. Расскажу, как мы выстраивали систему медиаконвертации на хабе: от первых проблем до текущей архитектуры.
На первый взгляд немного. В момент нашего неожиданного роста весной прошлого года, когда за день пользователи загрузили 25 ГБ медиа (без всякой оптимизации), цифры не выглядели катастрофическими, но пугали. После внедрения конвертации и оптимизации медиаконтент стал занимать куда меньше места: на текущий момент мы съедаем всего около 220 ГБ.

Главным врагом оказались тяжелые файлы.
- Картинки: мы начали конвертировать все в WebP. Этот формат при одинаковом разрешении дает меньший размер файла. В итоге большинство изображений на хабе весят буквально десятки килобайт.
- Видео: используем FFmpeg для конвертации в WebM. Это прогрессивный формат, поддерживаемый всеми браузерами и идеально подходящий нам по соотношению качества и размера.
На бумаге решение простое, но на практике начались новые трудности.
Когда весь хаб и медиаконвертер работали на большом сервере с последним AMD EPYC(48c/96t), проблем не было заметно. Но стоило затянуть пояса и переехать на сервера в РФ - стало заметно как хаб убивает сам себя, сразу не было понятно почему, ведь нагрузка на веб-сервер небольшая а процессор все равно улетает в 100%, немного покопав оказалось что конвертация видео слишком прожорлива. Каждый ролик отнимал 4-5 ядер процессора на минуту, и если в этот момент совпадала нагрузка от самого хаба, CPU улетал в 100% на 5-7 минут. За это время сервисы веб-сервера пытались как-то реанимировать сами себя, и в итоге падали из-за недостатка процессора.
Решение оказалось очевидным: разнести сервисы по разным серверам.
- Хаб - на одной машине (6 CPU / 16 GB RAM).
- Конвертер - на другой (4 CPU / 8 GB RAM).
Теперь они общаются по API, и хабу стало намного легче.
Все медиа у нас хранятся в S3, и хаб тянет их напрямую. В результате:
- Ежемесячный расход трафика доходил до 7-8 ТБ.
- Загрузка страниц заметно тормозила из-за тяжелых файлов.
Выходом стало внедрение кэширующего сервера. Мы построили цепочку на основе Varnish + Nginx, добавили Python-скрипт для автоподписи SSL (так как open-source версия Varnish не поддерживает HTTPS), и в итоге ускорили отдачу медиа в разы.

Все устроено примерно так:
- Пользователь прикрепляет медиафайл.
- Файл уходит в конвертер.
- Отконвертированный файл сохраняется в хранилище, оригинал временно лежит в темпах.
- При показе файла на хабе он кэшируется.
- После публикации уже оптимизированный файл отдается из кэша, а не напрямую из S3.
Результат: страница грузится быстрее, трафик уменьшается, серверу проще.
Помимо основной схемы, есть еще несколько фишек:
- Очереди и автоскейлинг: если загружается сразу несколько видео или десятки картинок, конвертеры автоматически масштабируются и потом схлопываются.
- *Проверка на уникальность: когда одна и та же картинка загружается повторно Мы ее сохраним, но повторно конвертировать не будем.
- Гибкость в очередях: система балансирует нагрузку так, чтобы пользователи не ждали слишком долго.
*Вопрос о повторном хранении файла очень сложный ибо если удалится оригинал - то он удалится отовсюду, поэтому проще хранить оптимизированные копии. На текущем этапе по крайней мере.
На словах все кажется простым, на деле - мы(я, лол, зачем я о себе в третьем лице говорю всегда) потратили немало времени на отладку этой архитектуры. Сейчас она выглядит так:
- Docker - для всего окружения.
- Медиа-прокси: Varnish + Nginx + Python (подпись SSL).
- Медиаконвертер: Celery + FastAPI + Redis + Nginx.
- Две машины: одна для хаба (6/16), вторая для конвертера (4/8).
Скорость работы уже неплохая. Конечно, есть куда расти, но текущая схема решает основные задачи: минимизирует расходы, ускоряет загрузку страниц и упрощает жизнь пользователям.
По деньгам нам это обходится в что-то типа 70 баксов в месяц(два сервера + хранилка). Что вполне себе дорого особенно после копеечных серверов в ЕС. Но закон есть закон.