Привет, Хабр! Вещает Иван Щукин, системный администратор с 1999 года. Последние 2,5 года я работаю в Купере. Сейчас моя роль — тимлид SRE фронтенда.

Эта статья написана по мотивам моего выступления на DevOpsConf 2025. О чем поговорим?
Как мы мониторим фронтенд в Купере
Что выбираем в качестве SLI
Какие клиентские метрики используем в качестве SLI
Купер входит в топ-3 на рынке e-grocery. У нас больше 100 тыс. уникальных пользователей в сутки. До 10 тыс. RPS в обычный день, в несколько раз больше — в дни высокой нагрузки. Из 10 тыс. порядка 1,5 тыс. приходится на сервисы фронта.
Из чего состоит фронтенд в Купере

В первую очередь это веб-приложение, которое запускается на пользовательских устройствах. Оно через Web Application Firewall (WAF) и Content Delivery Network (CDN) обращается к серверным компонентам фронта и бэкенда.
У нас есть самописный Server Side Rendering (SSR) на Next.js, ответственный за создание каркаса страницы и частично за наполнение. Также есть поды статики — по сути, Nginx-поды, которые раздают ассеты веб-приложения. Есть Static Proxy — это поды nginx, которые берут на себя проксирование и кэширование запросов к так называемой долгоиграющей статике, которая у нас располагается в S3-бакетах. А еще есть Imgproxy — чисто серверный компонент, который по запросу от веб- или мобильного приложения берет из S3-бакета картинку товара и отдает ее в нужном разрешении непосредственно в клиент.
Как мы встали на путь истинный (кажется)
На конец лета 2022 года, когда я пришел в компанию, фронтенд только выводили из большого монолита. Тогда мы собирали немного базовых метрик SSR: потребление ресурсов, Garbage Collection (GC), количество активных хендлеров, количество рендеров по ручкам и время рендера по ручкам, время отклика от API, работа некста с кэшем. Плюс немного метрик с Redis. Алертинг был на начальной стадии развития. Казалось бы, уже неплохо, но фронтенд — это не только SSR, но и клиентское приложение. И вот тут нас поджидали проблемы…
В открытых (да и закрытых) источниках крайне мало информации по мониторингу фронтовых проектов и еще меньше — по построению SLA для фронта.
Мы не понимали, какой именно компонент сбоит, а иногда не понимали, сбоит фронт или бэкенд.
Было трудно оценивать степень удовлетворенности клиента.
Однако глаза боятся, а руки делают!
Совместно с фронтенд-разработчиками мы начали развивать мониторинг SSR. Постепенно мы стали мониторить и Circuit Breaker, который защищает конечного пользователя от ошибок API и сдерживает API от парсинга запросов. Мы более широко исследуем кэширование, потребление памяти, объем получаемых данных и корреляцию между этими значениями, но не буду подробно останавливаться.
Некоторые компоненты давались нам со скрипом. Например, поды с ассетами — Static Proxy, ответственные за раздачу статики. Каждый из вас, кто хотя бы раз пытался настроить мониторинг nginx, знает, как много метрик он отдает в бесплатной версии.

Что мы сделали? Внедрили OpenResty как Drop-in Replacement для Nginx. Модуль Nginx-Lua-Prometheus позволил собирать гораздо больше метрик. Здесь и общее количество запросов, и распределение по кодам/времени ответов, и запросы к проксируемым бэкендам, будь то это S3, SSR или API Gateway.
Вдобавок снимаем обобщенные данные по фронту с Ingress (в Купере его роль выполняет Istio), и в итоге набираем достаточно много разнообразных метрик, которыми уже можно оперировать.
А что с клиентскими метриками?
Мы с фронтенд-разработчиками давно измеряем клиентские метрики с помощью Lighthouse. Это инструмент для автоматического мониторинга качества, производительности и корректности работы веб-приложений. Вы можете посмотреть на его метрики, открыв в Chrome консоль разработчика на любом сайте.

После каждого деплоя на продовое окружение у нас запускается специальная джоба, которая поднимает браузер; браузер прогоняет тесты Lighthouse и сохраняет их на сервер Lighthouse в нашем инфраструктурном окружении.
Набор измеряемых метрик постоянно эволюционирует: что-то добавляется, что-то уходит. На текущий момент мы опираемся на измерения, которые использует Lighthouse восьмой версии.
Основных метрик шесть. Их можно разбить на две группы. Первая группа измеряет скорость получения контента, вторая — качество взаимодействия с пользователями.
First Contentful Paint — время, которое проходит от клика по ссылке до момента, когда на странице появляется первый элемент.
Speed Index — скорость наполнения страниц контентом: как быстро подгружаются ассеты и как быстро эти ассеты преобразуются во что-то, что может увидеть пользователь.
Largest Contentful Paint — время полной загрузки основного содержимого страницы, то есть загрузки самого тяжелого элемента.
Что такое Time to Interactive? Рассмотрим простой пример. У вас форма с одной-единственной кнопкой, обработку нажатия кнопки производит JavaScript. Когда JavaScript с обработчиком загрузился и кнопка начала отрабатывать нажатие, это и есть TTI.
Total Blocking Time, если смотреть на общий таймлайн работы приложения, складывается из кусочков, когда основной поток приложения был заблокирован загрузкой дополнительных ассетов или другой работой и не мог отработать действия пользователя.
Cumulative Layout Shift, если просто, измеряет, не сильно ли дергается страничка, подставляя вместо одной кнопки другую.

Это метрики Lighthouse. Помимо них, мы снимаем еще Crash-Free Session Rate — метрику Sentry. Это процент сессий, не завершившихся сбоем приложения, в указанном диапазоне времени. Проще говоря, отношение нормальной отрисовки приложения к общему количеству запросов. Метрики Sentry собираются у нас еще с момента, когда фронтенд был в монолите.
В какой-то момент бизнес-часть Купера попросила нас вывести клиентские метрики на дашборды Grafana. Сначала мы узнали, что в открытом доступе нет никаких экспортеров из Sentry в Grafana с нужным нам функционалом. Потом мы подумали, что надо залезть в ClickHouse, но команда Observability сказала: «Нет, ребят, наша задача — чтобы все сервисы Observability работали 24x7, давайте вы не будете приносить нам хаос своими походами».
Мы договорились, что будем ходить в Sentry исключительно через API, но тут выяснилась еще одна проблема: оказывается, в API Sentry получение клиентских метрик нигде не задокументировано.
Тогда мы отреверсили запрос с веб-интерфейса и написали свой экспортер — вот пруф. Обкатали, подумали, добавили еще функционала и раскатали его на все проекты фронтенда в рамках нашего платформенного решения. Мы можем видеть Crash-Free Session Rate, скоринг по пользовательским метрикам и распределение по путям приложения. Красота!
Как получить из собранных метрик индикаторы уровня обслуживания
В итоге мы собираем огромное количество данных: больше 50 метрик с SSR, больше 15 метрик статических приложений, порядка 20 метрик с Imgproxy и около 10 клиентских метрик. Как отобрать те, которые отражают реальный уровень сервиса?
Все мы знаем, что уровень сервиса на фронте часто отображает уровень сервиса всей системы. Но перед нами нет задачи измерять общую производительность приложения. Прежде всего нам важно понимать, насколько здоров фронт. Соответственно, нам не совсем подходят метрики приложения: GC и те параметры, которые зависят от внешних сервисов (отклик от S3, отклик от Redis, отклик от бэкенда). Где-то они становятся основой для индикаторов уровня обслуживания, но полагаться на них полностью нельзя.
И — внезапно! — бо́льшая часть клиентских метрик нам тоже не подходит в качестве индикаторов уровня обслуживания. Просто потому, что существует значительная погрешность со стороны производительности как клиентского устройства, так и канала передачи данных. Если от браузера клиента до нашего дата-центра большой пинг, клиентские метрики просто рухнут.
Что остается? Со стороны SSR:
доступность
ошибки SSR
общая Latency
доступность и Latency с участием критичных API со стороны бэкенда (в качестве компромисса)
время рендера каркаса или страниц, которые SSR сразу отдает пользователю
Со статикой все просто:
доступность
количество успешных запросов
75-й перцентиль времени ответа
Почему 75-й, а не 99-й? 99-й грешен, скажем так, выбросами: там могут учитываться, например, ошибки по неверным запросам.
Из клиентских метрик берем:
Crash-Free Session Rate
Scoring как кумулятивная оценка производительности
Time to Interactive
First Contentful Paint как метрику Lighthouse, которая наименее привязана к производительности клиентского устройства
Метрик много не бывает
Вы спросите: «Куда девать все остальные метрики? Вот ты собрал цéлую сотню, а для оценки уровня обслуживания не используешь и 20%».
Благодаря оставшимся параметрам мы формируем мониторинг и алертинг, регулярно проводим анализ производительности приложения, оцениваем влияние релизов на пользовательский опыт, даем фидбэк команде разработки (причем не только разработки фронта). Короче, много работаем.
Например, благодарям введенным метрикам мы заметили, когда бэкенд начал лавинообразно увеличивать количество данных на однотипные запросы. Это влияло как на время загрузки страниц, так и на потребление памяти SSR.
Также однажды мы смогли увидеть проблемы на одном из используемых нами CDN и оперативно сообщили о них в техподдержку, предотвратив инцидент и ухудшение пользовательского опыта.
Как повторить трюк?
В каждой компании — разный ландшафт в разрезе фронтенд-сервиса. Однако я могу дать несколько универсальных советов:
Расширяйте мониторинг веб-серверов. Надеюсь, моя статья вам с этим поможет. На OpenResty свет клином не сошелся: много метрик можно снимать и в других веб-сервисах.
Обязательно сравнивайте метрики статики и приложения с метриками Ingress. Если есть расхождения, нужно алертить. Так можно более четко локализовать возникающие проблемы.
Вводите мониторинг клиентских метрик.
Собирайте метрики в одном месте (Victoria Metrics, Prometheus и т. д.). Это поможет вам проводить сквозной мониторинг не только среди сервисов фронта, но и среди сервисов, которые касаются бэка.
loly_girl
А почему приложение Купера работает быстрее самоката? Недавно тортик и конфеты девочкам в офис заказывала, вся испереживалась.