Cosmos представляет собой новейшее поколение медиа-обработчика на Netflix, интегрирующее архитектуру на основе микросервисов, асинхронные рабочие процессы и функционал без привязки к серверам. Эта платформа нацелена на оптимизацию медиа-процессов внутри Netflix, повышая их гибкость и производительность, а также способствуя более продуктивной работе разработчиков. За последние годы команда Encoding Technologies (ET) усердно трудилась над модернизацией видео-конвейера с использованием платформы Cosmos.

Обновлённый конвейер состоит из множества микросервисов, каждый из которых выполняет строго определённые функции. Один из таких сервисов – Video Encoding Service (VES), который играет ключевую роль в процессе кодирования видео, преобразуя исходные медиа-файлы в форматы, подходящие для стриминга на Netflix или для других студийных задач. VES сталкивается с рядом требований, связанных с обработкой контента, например:

  • Необходимость поддержки множества форматов, разрешений и качества изображения для разнообразных устройств пользователей, от мобильных телефонов до смарт-телевизоров;

  • Обязательность чанковой кодировки для соответствия требованиям задержек, с учётом различной чувствительности к ним;

  • Важность непрерывного выпуска обновлений для ускорения инноваций в стриминговых и студийных направлениях;

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

В этой статье мы поговорим о процессе создания VES, а также об уроках, извлечённых в ходе разработки. Мы опустим некоторые детали, характерные только для Netflix, чтобы сфокусироваться на главной теме нашего обсуждения.

Приятного прочтения!

Разработка VES на базе Cosmos

В рамках Cosmos микросервис структурирован на три слоя: API-слой (Optimus), который обрабатывает входящие запросы, слой управления рабочими процессами (Plato), координирующий потоки медиа-обработки, и серверно-независимый вычислительный слой (Stratum), выполняющий непосредственную обработку медиафайлов. Эти слои осуществляют асинхронное взаимодействие через разработанную внутри компании систему обмена сообщениями на основе приоритетов — Timestone. Для структурирования данных мы выбрали Protobuf, благодаря его эффективности и широкой поддержке различных платформ.

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

Optimus как API-слой

Optimus служит в качестве API-уровня в архитектуре VES, действуя как основная точка входа для пользователей сервиса. Это означает, что любое взаимодействие с VES происходит исключительно через Optimus. Установленный API интерфейс работает как надёжный контракт, связывающий VES и его пользователей, обеспечивая защиту от любых внутренних изменений в самом VES. Такая структура обеспечивает критически важную возможность для быстрого внесения изменений во внутренние процессы VES без воздействия на UX.

API VES разработан быть простым и ясным в использовании. Мы создали центральную функцию encodeVideo, которая принимает запрос на кодирование EncodeRequest и возвращает ответ EncodeResponse, обмен данными осуществляется асинхронно через систему сообщений Timestone. Запрос EncodeRequest включает в себя детали оригинального видеофайла и параметры, необходимые для кодирования. Все спецификации требуемого закодированного видео, такие как кодек, разрешение, а также управление задержками, задаются посредством модели данных encoding recipe.

message EncodeRequest { 
    VideoSource video_source = 1; // исходное видео для кодирования 
    Recipe recipe = 2; // включая формат кодека, разрешение и т. д. 
}

message EncodeResponse { 
    OutputVideo output_video = 1; // закодированное видео 
    Error error = 2; // сообщение об ошибке
}

message Recipe { 
    Codec codec = 1; // включая формат кодека, профиль, уровень и т. д. 
    Resolution resolution = 2; 
    ChunkingDirectives chunking_directives = 3; 
}

Так же, как и для других сервисов на платформе Cosmos, система автоматически создаёт RPC-клиент на основе структуры данных API VES. Этот клиент можно использовать для составления и отправки запросов к VES. При получении запроса Optimus проводит необходимые проверки и, если это требуется, конвертирует входящие данные в соответствующую внутреннюю форму перед их пересылкой на следующий уровень обработки — Plato.

Plato как управляющий рабочими процессами

Plato является слоем управления рабочими процессами в структуре VES, координируя различные этапы обработки медиа. На платформе Cosmos для работы с Plato доступны две основные парадигмы: метод прямого связывания и подход, основанный на направленном ациклическом графе (DAG). Для VES, имеющего последовательную структуру рабочего процесса, предпочтение было отдано использованию DAG, учитывая его простоту и эффективность.

В рабочем процессе DAG каждый этап представлен узлом, а зависимости между этапами — рёбрами. Узел переходит в активное состояние только после завершения всех этапов, от которых он зависит. В VES задействована параллельная обработка видеофрагментов для обеспечения необходимой производительности и надёжности. Такой параллелизм на уровне рабочего процесса удобно реализуется в DAG с использованием принципа MapReduce. Узлы могут быть помечены аннотациями для обозначения зависимостей, и узел типа Reduce запускается только после обработки всех соответствующих ему узлов типа Map.

В рабочем процессе VES мы определили пять ключевых узлов и их взаимные связи, которые отражены на схеме процесса:

  • Splitter Node: этот узел отвечает за разбиение исходного видео на фрагменты в соответствии с параметрами фрагментации, указанными в encoding recipe;

  • Encoder Node: он принимает на вход видеофрагменты и выполняет их кодирование. Это узел типа Map;

  • Assembler Node: после кодирования этот узел объединяет фрагменты в единый видеофайл. Это узел типа Reduce;

  • Validator Node: занимается проверкой качества закодированного видео;

  • Notifier Node: информирует API-слой о завершении всех этапов рабочего процесса.

Пример "упаковки контейнеров". Круги представляют ядра ЦП, а область представляет ОЗУ. Этот экземпляр EC2 с 16 ядрами заполнен 5 контейнерами кодирования (прямоугольниками) трех различных форм (обозначенных разными цветами).

В рамках описанного рабочего процесса некоторые узлы выполняют функции не требующие значительных вычислительных мощностей, например, Notifier Node, и могут быть обработаны напрямую в рамках Plato. В то же время задачи, которые требуют большего объема ресурсов, должны быть делегированы вычислительному слою — Stratum или соответствующим сервисам. Plato координирует работу Stratum, направляя туда задачи, такие как кодирование и сборка видео, где узлы Encoder и Assembler помещают задачи в специализированные очереди сообщений. Для проверки качества окончательно собранного видео Validator Node взаимодействует с другим сервисом на платформе Cosmos — Video Validation Service.

Stratum как вычислительный уровень

Stratum представляет собой вычислительный слой в архитектуре сервисов Cosmos, где доступны ресурсы для обработки медиа. Разработчики сервисов Cosmos создают специализированные функции в Stratum для обработки медиафайлов, используя при этом собственные или сторонние инструменты, которые упаковываются в Docker-образы. Эти Docker-образы затем размещаются во внутреннем реестре Docker, в том числе на платформе Titus. В процессе эксплуатации Titus обеспечивает автоматическое масштабирование экземпляров функций в зависимости от загрузки очередей задач.

Для VES критически важно обеспечить возможность кодировать исходное видео в множество форматов кодеков, таких как AVC, AV1, VP9 и другие. Разные бинарные кодировщики, или просто "кодировщики", используются для различных кодеков. Например, кодировщик AVC, который используется уже более 20 лет, считается стабильным. В то же время AV1, относительно новый кодек для Netflix, требует частых обновлений из-за постоянных улучшений и экспериментов. Чтобы эффективно управлять этими изменениями, мы создали отдельные функции Stratum для каждого кодека, которые могут обновляться независимо друг от друга. Это говорит о том, что обновление одного кодировщика не повлияет на работу VES для других кодеков, поддерживая стабильность и высокую производительность системы в целом.

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

ffmpeg -i input/source%08d.j2k -vf ... -c:v libx264 ... output/encoding.264

Кодирование является процессом, который требует значительного количества вычислительных ресурсов, и объем необходимых ресурсов варьируется в зависимости от выбранного кодека и параметров кодирования. Мы осуществили ряд тестов производительности, чтобы оценить потребление ресурсов, преимущественно процессорного времени и оперативной памяти, для разнообразных сценариев кодирования. Используя полученные данные, мы воспользовались функцией "container shaping" на платформе Cosmos.

Для этого было разработано несколько "container shapes", каждый из которых имеет индивидуальные настройки распределения ресурсов, включая центральный процессор и оперативную память.

// пример container shape
group: containerShapeExample1
resources:
  numCpus: 2
  memoryInMB: 4000
  networkInMbp: 750
  diskSizeInMB: 12000

Для эффективного распределения задач кодирования между разными форматами, исходя из уникального сочетания используемого кодека и параметров разрешения, мы разработали специализированные правила маршрутизации. Это дает возможность платформе оптимизировать "bin packing" для максимально эффективного использования доступных ресурсов.

Иллюстрация принципа "bin packing" можно представить следующим образом: круги символизируют ядра процессора, тогда как пространство вокруг них отражает объем оперативной памяти. На примере экземпляра EC2 с 16 ядрами, мы видим, что он заполнен пятью контейнерами для кодирования (изображены в виде прямоугольников), которые представлены в трех различных размерах и цветах, соответствующих их типам
Иллюстрация принципа "bin packing" можно представить следующим образом: круги символизируют ядра процессора, тогда как пространство вокруг них отражает объем оперативной памяти. На примере экземпляра EC2 с 16 ядрами, мы видим, что он заполнен пятью контейнерами для кодирования (изображены в виде прямоугольников), которые представлены в трех различных размерах и цветах, соответствующих их типам

Непрерывное развёртывание

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

Когда фокусируешься на одной функции, изменения кода для добавления новых возможностей в VES обычно невелики и изолированы, что упрощает процесс их проверки. Поскольку взаимодействие с VES осуществляется исключительно через API, внутренние изменения кода могут рассматриваться как детали реализации, которые можно без риска модифицировать. Чёткое определение API сокращает объём необходимых тестов для VES. К тому же, платформа Cosmos предлагает многоуровневую структуру тестирования, поддерживающую разработчиков в создании тестов на различных этапах.

После тестирования и одобрения, изменения кода объединяются и подготавливаются к релизу. Процесс релиза полностью автоматизирован: начиная с объединения изменений, он включает проверку кода, компиляцию, сборку, проведение модульных, интеграционных и приёмочных тестов в соответствии с установленными критериями, и переходит к полному развёртыванию, если не встречает препятствий. Обычно весь процесс занимает около 30 минут с момента слияния кода до окончания развёртывания (в предыдущей системе на это уходило от 2 до 4 недель!). Такая скорость релиза позволяет разработчикам получать обратную связь быстрее и вносить необходимые корректировки, пока детали проекта ещё свежи в памяти.

Изображение процесса работы конвейера релиза в рамках нашей производственной среды.
Изображение процесса работы конвейера релиза в рамках нашей производственной среды.

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

Извлеченные уроки

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

Нахождение оптимальной границы для сервиса

Одним из ключевых принципов микросервисной архитектуры является идея, что каждый сервис должен обслуживать строго определённую функциональность. Это понятие звучит довольно ясно, однако возникает вопрос: как точно определить, что считать "определенной функциональностью"? Например, "кодирование видео" может показаться подходящей задачей, но не более точным ли будет "кодирование видео в формате AVC"?

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

Сервис, описываемый в этой статье, является усовершенствованной версией VES. В этом обновлении мы интегрировали кодирование различных кодеков в один сервис. Хотя все кодеки используют общий API и рабочий процесс, для каждого из них предусмотрены уникальные функции Stratum. По нашему мнению, это является эффективным балансом: единый API и рабочий процесс снижают дублирование кода, в то время как индивидуальные функции Stratum позволяют сохранить независимость развития для каждого кодека.

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

Практический подход к управлению моделями данных

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

Этот подход привёл к множественным вариациям моделей данных для одних и тех же характеристик, таких как глубина цвета и разрешение. Например, наша система кодирования обрабатывает различные глубины цвета для кодеков AVC (8-бит) и AV1 (10-бит). Разработав отдельные структуры данных AVC.BitDepth и AV1.BitDepth, мы смогли интегрировать ограничения по глубине цвета непосредственно в данные модели. Однако вопрос о том, перевешивают ли преимущества такого разделения возможные сложности, связанные с многократным преобразованием данных, остаётся открытым.

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

Управление изменениями API сервиса иногда может казаться парадоксальным

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

Хотя существует множество научных работ по разработке API, наш случай имеет свою уникальность: VES - это внутренний сервис для технологий кодирования Netflix (ET), и его основными пользователями являются Оркестратор потоковой передачи и Оркестратор студии, также принадлежащие команде рабочих процессов ET. Мы разделяем с нашими пользователями общий контекст и цели. Если мы приходим к пониманию, что обновление API будет служить общим интересам Netflix, мы согласовываем эти изменения с заинтересованными сторонами. Как только получено одобрение на обновление API, команды тесно сотрудничают для обеспечения плавного перехода к новой версии.


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

Комментарии (1)


  1. nelrenop1983
    28.04.2024 08:59

    спасибо, полезная информация