Как стать автором
Обновить

Архитектура облачного волейбольного сервиса

Время на прочтение4 мин
Количество просмотров2K
Не так давно я писал про волейбольный сервис, теперь пришло время описать его с технической точки зрения.

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

Краткое описание функциональности:

  • пользователь загружает видео с записью волейбольной игры
  • один хитрый алгоритм распознает мяч на кадрах
  • другой хитрый алгоритм выделяет розыгрыши
  • розыгрыши компонуются в отдельные видеофайлы
  • файлы с розыгрышами собираются в дайджест всей игры
  • все видео заливается в облако
  • пользователи смотрят/качают/шарят клипы с самыми классными розыгрышами

Например, такими:



Теперь, как это все работает.

Технологии


Все написано на python, веб-сервис — Django/Gunicorn.
Интенсивно используются OpenCV и FFMpeg.
База данных — Postgres.
Кэш и очередь — Redis.

Альфа


В самой первой версии было 3 компонента:

  • Front — Веб сервис (Django), с которым взаимодействуют конечные пользователи
  • Videoproc (Vproc) — Ядро алгоритма, python + opencv, которое содержит все алгоритмы треккинга мяча и логику нарезки на розыгрыши
  • Clipper — Сервис генерации видео на основе выхлопа Vproc, используя ffmpeg



Разработка велась локально, я поставил на домашний десктоп Ubuntu, а на нее microk8s и получил маленький Кубернетес-кластер.

Здесь я столкнулся с тем, что кадры, которые видит opencv не очень совпадают с временем, которое видит ffmpeg из-за особенностей кодеков, поэтому высчитать время в оригинальном файле из номера кадра оказалаось невозможно.

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

Параллельная обработка


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

Логично распараллелить эти две работы.

По начальной задумке пока vproc разбирает кадры через opencv, ffmpeg параллельно записывает все на диск, а clipper собирает из них видео.

Но с ffmpeg нашлись две проблемы:

  • Кадры из ffmpeg не идентичны кадрам из opencv (это не всегда так, зависит от кодека видеофайла)
  • Количество кадров в записи может быть слишком большим — например час видео при хорошем fps — это порядка 200K файлов, что многовато для одного каталога, даже если это ext4. Городить разбиение на поддиректории и потом склеивать при компоновке видео — не хотелось усложнять

В итоге вместо ffmpeg появился пятый элемент компонент — Framer. Он запускается из vproc, и листает кадры в том же видеофайле, ожидая пока vproc найдет розыгрыши. Как только они появились — framer выкладывает нужные кадры в отдельную директорию.

Из дополнительных плюсов — ни одного лишнего кадра не эспортируется.

Мелочь, но все таки.

По производительности (на 10-минутном тестовом видео):
Было:
Completed file id=73, for game=test, frames=36718, fps=50, duration=600 in 1677 sec

Стало:
Completed file id=83, for game=test, frames=36718, fps=50, duration=600 in 523 sec + framer time 303

Выигрыш в два раза, очень хорошо. Если писать в несколько потоков, то наверное можно выжать еще чуть. Если хранить кадры в памяти и генерить из них видео через API, то скорее всего можно ускориться значительно.

Digital Ocean


Дальше я стал выбирать хостинг. Понятно, что основные варианты — GKE, AWS, Azure, но многие авторы мелких проектов жалуются на непрозрачное ценообразование и, как следствие, немаленькие счета.

Основная засада здесь — цена за исходящий трафик, она составляет порядка $100/Tb, а поскольку речь идет о раздаче видео, вероятность серьезно попасть очень неиллюзорна.

Тогда я решил глянуть второй эшелон — Digital Ocean, Linode, Heroku. На самом деле Kubernetes-as-service уже не такая редкая вещь, но многие варианты не выглядят user-friendly.

Больше всего понравился Digital Ocean, потому что:

  • Managed Kubernetes
  • Managed Postgres
  • S3 хранилище с бесплатным(!) CDN + 1 TB/месяц
  • Закрытый docker registry
  • Все операции можно делать через API
  • Датацентры по всему миру

При наличии CDN веб-серверу уже не было надобности раздавать видео, однако кто-то должен был это видео опубликовать.

Так в архитектуре появился четвертый компонент — Pusher.



Однако серьезным недостатком оказалась невозможность смонтировать один и тот же диск на несколько машин одновременно.

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

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

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

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

Логи и метрики


Запустив кластер в облаке, я стал искать решение для сбора логов и метрик. Самостоятельно возиться с хостингом этого добра не хотелось, поэтому целью был free-tier в каком-нибудь облаке.

Такое есть не у всех: Модная Grafana хочет $50 в месяц, выходящий из моды Elastic — $16, Splunk даже прямо не говорит.

Зато внезапно оказалось что New Relic, также известный своими негуманными ценами, теперь предоставляет первые 100G в месяц бесплатно.

Видео


Вполне естественным решением виделось разделить кластер на два node-pool'а:

  1. front — на котором крутятся веб-сервера
  2. vproc — где обрабатывается видео

Фронтовой пул, понятно, всегда онлайн, а вот процессинговый не так прост.

Во-первых, обработка видео требует серьезных ресурсов (= денег), а во-вторых, обрабатывать приходится нечасто (особенно в стадии зарождения проекта).

Поэтому хочется включать процессинг только, чтобы обработать видео (время обработки — 3x от продолжительности действа).

Кубернетес формально поддерживает autoscale 0, но как именно это реализуется — я не нашел, зато нашлась такая дискуссия на Stack Overflow.

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

У DigitalOcean есть неофициальный клиент для питона, но он уже давно не обновлялся, а Kubernetes API там не присутствует в принципе.

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

В итоге диаграмм разрослась вот так:



DevOps


Несмотря на великое множество CI/CD инструментов, в разработке не нашлось ничего удобнее Jenkins.

А для управления DigitalOcean'ом идеально подошли Github Actions.

Ссылки


Теги:
Хабы:
Всего голосов 8: ↑8 и ↓0+8
Комментарии2

Публикации

Истории

Работа

Python разработчик
137 вакансий
Data Scientist
71 вакансия
DevOps инженер
51 вакансия

Ближайшие события

19 сентября
CDI Conf 2024
Москва
24 сентября
Конференция Fin.Bot 2024
МоскваОнлайн
30 сентября – 1 октября
Конференция фронтенд-разработчиков FrontendConf 2024
МоскваОнлайн