В этот раз к нам пришел клиент, желавший запустить собственную SSP (Supply‑Side Platform). Это система, которая позволяет владельцам отдельных сайтов или целых сетей продавать рекламные места и получать доход от размещения объявлений. У клиента уже был реализован MVP системы, но оставалось еще много работы. Меня зовут Сергей Дербуш, я архитектор в компании «СмартАп Технолоджи». Расскажу о том, что мы доделывали, чтобы система заработала на полную, и как это бустануло навыки всей команды. Клиент занимается установкой рекламы на сайты. Для установки рекламы используется JavaScript с подключенной библиотекой Prebid.js. Процесс устроен следующим образом: к нашему клиенту приходит владелец сайта (также он зовется паблишером); наш клиент подключает партнеров, через которых можно запрашивать рекламу. Для этого он регистрируется в системах партнеров, заключает договор и получает идентификатор; клиент подготавливает скрипт для запроса и отображения рекламы от партнеров, для этого в библиотеке Prebid.js необходимо подключить адаптеры партнеров; реклама, предоставляемая партнерами, отображается на сайте владельца и считается стоимость ее показа; партнеры предоставляют отчеты о показах и их стоимости; партнеры выплачивают деньги нашему клиенту; наш клиент берет процент за услуги предоставления рекламы; наш клиент выплачивает деньги владельцу сайта; Партнеры которые предоставляют рекламу являются SSP системами.  SSP тесно связанно с DSP. DSP (Demand Side Platform) — это система, которая облегчает процесс покупки и продажи рекламных мест. Рекламодатель подключается к DSP, чтобы покупать и размещать рекламу в интернете, отслеживать результаты, оптимизировать все запущенные кампании — все это на едином сервисе и в одном интерфейсе. DSP анализирует поступающие от SSP запросы и предоставляет рекламу со ставкой на покупку рекламных мест. Взаимодействие между Prebid.js, SSP и DSP происходит по открытому протоколу OpenRTB.  Клиент пришел к нам, чтобы запустить свою SSP, в нее будут интегрированы DSP, с которыми есть контракт. Так как DSP отдает рекламу со ставкой, а SSP уменьшает эту ставку на некоторый процент за предоставление своих услуг. Идея заключалась в том, чтобы использовать свою SSP для показа рекламы и зарабатывать на SSP самим. Prebid.js это библиотека, которая работает по технологии header bidding. Во время загрузки страницы сайта Prebid.js отправляет запросы на получение рекламы через Prebid.js адаптеры подключенных партнеров (SSPs). В запросе отправляются все места и размеры под баннеры (высота и ширина). В ответ SSPs присылают рекламу и ставки, по которым реклама будет показана. Prebid.js собирает все ставки от партнеров на показ баннера. Так как на одно рекламное место может приходить несколько предложений, то Prebid.js проводит, так называемый, аукцион первой цены. Это когда выбирается баннер с максимальной ставкой (стоимостью показа). Выигравшая реклама отображается на сайте. SSP работает по открытому протоколу OpenRTB. Она получает запрос с местами под баннеры и их размеры. SSP собирает информацию о пользователе (геолокацию, браузер, устройство и т. д.), чтобы иметь возможность идентифицировать пользователя в будущем. Далее SSP отправляет запросы с местами под баннеры для получения рекламы от подключенных DSP. Она получает и собирает ставки от всех DSP, от которых пришел ответ. SSP может взимать процент за использование с каждой ставки и возвращать уменьшенную ставку. В конце SSP отправляет собранные ставки и возвращает результат запроса. SSP хранит идентификатор пользователя, который ему предоставляет DSP. SSP отправляет идентификатор пользователя при каждом запросе, чтобы DSP могла идентифицировать одного и того же пользователя. SSP добавляет трекер для отслеживания показа рекламы на клиенте. Также она готовит ежедневные отчеты о показах и их стоимости и сравнивает отчеты с отчетами DSP. Несоответствие между ними не должно превышать 5%.  В начале работы с клиентской системой мы выяснили, что у него есть база паблишеров (владельцев сайтов), на сайтах которых установлены скрипты с библиотекой Prebid.js и отображается реклама от нескольких SSP, подключенных через Prebid.js адаптеры.  Клиент хотел опубликовать свой Prebid.js адаптер, который запрашивал бы рекламу через свою же SSP. В наличии уже был неопубликованный Prebid.js адаптер с возможностью отправки запроса на получения баннерной рекламы от SSP. У клиента также был UI для добавления новых паблишеров, сайтов и рекламных блоков для сайта. В UI можно было настраивать параметры для интегрированных DSP (вкл/выкл, время отклика и т. д.) SSP могла определять устройство, с которого пришел пользователь, откуда (геолокацию), а также умела подготавливать OpenRTB запрос для DSP и получать ответ. Однако, изучая возможности системы, мы не смогли получить реальную ставку от DSP — интеграции не работали и возвращали пустой ответ. Были предоставлены скрипты Terraform для развертывания инфраструктуры в AWS (Amazon Web Services). В скриптах написан модуль для развертывания Kubernetes кластера через сервис AWS EKS. Развернуто стейджинг окружение. Запуск Terraform скриптов производился с локальной машины. Предоставлены YAML файлы для запуска SSP сервисов в Kubernetes. А еще построен CI/CD pipeline для доставки изменений в сервисе SSP. В качестве CI использовался Circle CI. Отсутствовали мониторинг и логирование. Зато был PySpark Glue Job для сбора и агрегации данных о показах рекламы, которые складываются на S3 через сервис Kinesis Firehose У клиента уже был готов UI для генерации отчетов о количестве полученной/показанной рекламы, общей стоимости по: Паблишеру. Дате. DSP партнеру. Веб‑сайту. Из технологий использовались: Github — репозиторий с кодом. CircleCI для сборки докер образа и применения Kubernetes шаблонов. AWS сервисы: EKS — сервис для запуска управляемого кластера Kubernetes; EC2 — сервис, предоставляющий виртуальные машины, в том числе и машины для EKS; ECR — это хранилище для Docker образов; Cognito — сервис, используемый для авторизации в SSP; Kinesis Firehose — сервис для получения потоков данных в реальном времени. Нужен для сбора информации о ставках и показах, чтобы на их основе формировать отчеты; Cервис баз данных RDS; OpenSearch — этот сервис используется для поиска и для хранения логов от SSP; Glue — ETL cервис, позволяющий строить процессы для обработки данных; S3 — хранилище данных, в том числе тех, что сохранены через Kinesis Firehose; Athena — это сервис интерактивной аналитики, который облегчает анализ данных, хранящихся в S3; NATS — брокер сообщений для взаимодействия SSP с сервисами. Terraform — скрипт для управления инфраструктурой. Готовясь начать работу над системой, мы наметили такой список задач: интегрировать несколько DSP, с которыми есть договор; проверить Prebid.js адаптер на соответствие требованиям для публикации в официальном репозитории. Обновить адаптер для совместимости с новой версией Prebid.js; добавить мониторинг. Так как система работает с множеством сервисов и выход из строя одного из них может привести к неработоспособности всей системы, необходимо отслеживать состояние каждого сервиса; настроить алерты. Необходимо определить для каждого сервиса метрики на основании которых следует уведомить команду о неполадках на сервисе или о необходимости что‑то сделать (например, если заканчивается место в базе данных); настроить сбор логов от приложения. В случае неполадок в работе системы необходимо сохранить информацию о том, где произошла ошибка. Также необходимо проинформировать команду о резком увеличении ошибок в системе; одно из требований клиента к системе, чтобы SSP могла справляться с нагрузкой 5000 запросов в секунду (QPS). В ходе работы над проектом мы: изучили скрипты Terraform и пересоздали компоненты, которые были созданы руками, чтобы вся инфраструктура поднималась централизованно и применялась практика инфраструктура как код (IaC — Infrastructure as Code). Структурировали репозиторий со скриптами и выделили отдельные модули и вынесли параметры, необходимые для повторного развертывания кластера в производство. Запустили pipeline для развертывания в производство; обновили все компоненты системы (Kubernetes кластер, helm chart‑ы, RDS DB); изучили компоненты SSP и выделили из них те, на работу которых уходило много времени, что в случае высокой нагрузки могло блокировать цикл событий. Для анализа использовали профилирование и инструмент clinic.js. Выяснили, что компонент определения устройств, с которых пришел запрос на рекламу в SSP, занимает основное время жизни запроса и решили вынести компонент в отдельный пул воркеров, чтобы уменьшить время блокировки цикла событий.После увеличения трафика начался рост задержек на обработку запроса (Latency). Для исследований задержек в цикле событий воспользовались (perf_hooks.monitorEventLoopDelay). Чтобы не обрабатывать запросы, когда задержки в цикле событий превышают заданное значение, реализовали Circuit Breaker паттерн. Также при большом объеме трафика замечали рост latency вместе с увеличением времени работы сборщика мусора (GC). Поэкспериментировали с параметрами ‑max‑semi‑space‑size=(32|64|128), с параметрами CPU, Memory для одного экземпляра приложения (pod‑а). В итоге, после нескольких итераций подбора значений, удалось добиться приемлемого уровня latency. SSP должна хранить соответствие пользователей SSP и DSP, для этого использовалась PostgreSQL. После увеличения трафика PostgreSQL долго обрабатывала запрос, а так как у нас был опыт работы с Aerospike в AdTech проектах, то мы заменили PostgreSQL на Aerospike. После этого время поиска пользователя уменьшилось; получили документацию от DSP, с которыми было необходимо интегрироваться. Подготовили модули для каждой из них.  Чтобы считать, что интеграция с DSP завершена успешно, необходимо: убедиться что запрос содержит все обязательные поля для этой DSP; убедиться что механизм синхронизации информации о пользователе работает; убедиться в получении ставок. Для этого нужно отправить реальный трафик. У нашего клиента была возможность отправлять 1% трафика с нескольких подконтрольных сайтов; сформировать отчеты о показе рекламы и ее стоимости за несколько дней (по дням) и сравнить с отчетами DSP. Несоответствие количества показов и их стоимости между отчетами не должны превышать 5%;  в зависимости от DSP, включить сжатие (gzip) трафика между системами, чтобы снизить его стоимость; если DSP не поддерживает в одном запросе нескольких рекламных блоков, отправить несколько параллельных запросов к DSP и объединить результат; в качестве системы мониторинга в кластер Kubernetes установили Prometheus, а для отображения данных в графическом виде установили Grafana. Добавили дашборды для мониторинга Kubernetes кластера (количество node, pod, CPU, Memory), Node JS приложения. Создали информационные панели для бизнес метрик заказчика: общее количество показов и доход за выбранный интервал, количество показов и доход по DSP, CPM (стоимость 1000 показов рекламы) по DSP, процент ошибок от общего количества запросов по DSP, общее количество запросов на рекламу к SSP; проанализировали AWS Glue ETL скрипты для сбора данных. Скрипты написаны на Python (PySpark) и занимаются агрегацией данных за предыдущий час работы SSP. Данные хранятся в S3 и записываются туда при помощи Kinesis Firehose. При увеличении трафика выяснилось, что скрипты работают долго и тратят много ресурсов. Перевод ETL скриптов на новую версию Glue 3 позволил сократить время на запуск ETL скрипта. Информация о ставках и информация о показах хранились в разной схеме данных. Это приводило к объединению больших объемов агрегированных данных и увеличивало время выполнения скриптов. Когда мы создали общую структуру показов ставок и переработали ETL скрипты, то сократили время работы, это привело к снижению затрат на использование сервиса;  так как SSP‑система должна обрабатывать большое количество запросов на рекламу, которое зависит от времени суток, нам предстояло внедрить автомасштабирование (горизонтальное масштабирование) виртуальных машин (Node) и SSP приложения (Pod). Для этого в кластере Kubernetes были созданы Node Group‑ы которые позволили задавать тип виртуальных машин. Для сервисов, которые требуют большого объема ресурсов, были выбраны типы Node помощнее и настроено горизонтальное масштабирование, на основе загрузки CPU. Для оптимизации затрат привлекли Spot‑инстансы (это привлечение свободных в данный момент ресурсов на AWS со скидкой). Для мониторинга масштабирования и количества привлеченных Spot‑инстансов добавили информационные панели в Grafana; для сбора и хранения логов воспользовались сервисом AWS OpenSearch. Клиента устроила стоимость и простота его запуска; так как в проекте Node JS библиотеки периодически обновляются и добавляются, то необходимо проверять установленные пакеты на предмет безопасности. Для сканирования зависимостей на уязвимости использовали Owasp плагин для CircleCI; составили документ о всех параметрах системы, которые нужно контролировать и оповещать команду в случае их выхода из заданных пределов. Настроили оповещение в PagerDuty (это платформа для обработки инцидентов, помогает настраивать порядок дежурств и оповещения дежурных инженеров); для тестирования системы и анализа метрик под нагрузкой внедрили практики нагрузочного тестирования. Для этого воспользовались инструментом jMeter. При помощи Ansible скриптов создается заданное количество виртуальных машин на сервисе (EC2), с них генерируется нагрузка на SSP. Проект принес опыт использования Node JS в условиях высоких нагрузок. Дал опыт работы с Kubernetes кластером. Информационные панели в Grafana + Kibana логи позволили быстро отслеживать аномалии в работе системы. В проекте наладили процесс автоматизированной поставки от разработки до эксплуатации. Это написание кода, автоматические тесты, развертывание в промежуточной среде, развертывание в рабочей среде  После того как мы вышли в производство, сосредоточились на добавление новых функций: поддержка видео‑рекламы (до этого система работала только с баннерной рекламой); возможность интегрироваться с SSP через JSTag (для клиентов, которые не используют Prebid.js, подготавливается специальный javascript код); возможность интегрироваться с SSP как с партнером по OpenRTB протоколу (дать возможность другим SSP использовать SSP в качестве DSP); поддержка PMP deals (это необходимо для поддержки аукционов закрытого типа); добавить механизм ограничение количества входящих запросов (так как некоторые DSP имеют ограничения на количество принимаемых запросов, то следует применить throttling запросов); Дальнейшие планы подключить клиентов и продолжать интегрировать больше DSP. А также вывести систему на стабильный доход.  А сейчас пройдемте в комментарии обсуждать наш кейс и делиться опытом, если вы тоже работали с SSP‑системами.