MAATRIX / Блог / Миф: графики зелёные — значит, пользователям хорошо

Миф: графики зелёные — значит, пользователям хорошо

MAATRIX

Дашборд открыт, все графики зелёные: CPU занят на 15%, память в норме, диск не забит, аптайм 100% за последний месяц. А в поддержку тем временем летят жалобы — «сайт тормозит», «корзина не открывается», «страница грузится по десять секунд». Это не парадокс и не глюк мониторинга — это разрыв между тем, что измеряет инфраструктура, и тем, что реально чувствует пользователь. Разберём, откуда берётся этот разрыв и что с ним делать.

Что на самом деле показывают инфраструктурные метрики

CPU, память, диск, сеть на уровне хоста, аптайм сервиса — все эти метрики отвечают на один вопрос: жив ли сервер и не упирается ли он в свои ресурсы. Это важные метрики, без них нельзя понять, что происходит с железом и ОС. Но у них есть фундаментальное ограничение: они измеряют состояние машины, а не результат, который получает конкретный человек в браузере.

Сервер может быть загружен на 10% и при этом отдавать страницу за 8 секунд — потому что один SQL-запрос без индекса блокирует остальные, потому что внешний API платёжной системы отвечает медленно, потому что фронтенд грузит три мегабайта неоптимизированных картинок. Ни CPU, ни RAM, ни аптайм этого не покажут: с точки зрения хоста всё в порядке, процесс жив, порт слушает, HTTP 200 приходит.

Классический пример из этой же категории мифов разобран в статье «Сайт открывается — значит, всё в порядке»: HTTP 200 на главной странице ничего не говорит о том, что происходит на остальных 90% сайта. Та же логика применима и к «зелёным» инфраструктурным метрикам — они отвечают за узкий срез реальности, а не за весь пользовательский путь.

Отдельная ловушка — путать «сервер не перегружен» с «сервер быстрый». Задержка ответа складывается не только из загрузки CPU, но и из сетевого RTT, TLS-хендшейка, времени на диске, времени ожидания в очереди воркеров, времени выполнения бизнес-логики. Можно иметь свободные ядра и всё равно медленно отвечать — просто потому что запросы выполняются последовательно там, где могли бы параллельно, или упираются в лок на уровне базы.

Где теряется разрыв: приложение и сторонние интеграции

Первая и самая частая причина расхождения — код самого приложения. Неоптимальный SQL-запрос без индекса, N+1-запросы к базе из ORM, синхронный вызов внешнего API прямо в обработчике HTTP-запроса, отсутствие кеширования там, где оно давно нужно — всё это создаёт задержку, которая никак не видна в top или htop. Процесс потребляет мало CPU именно потому, что большую часть времени он не считает, а ждёт — ответа от базы, от диска, от внешнего сервиса.

Проверить это на сервере можно быстро, не разворачивая тяжёлый профилировщик. Для базы — включить логирование медленных запросов:

-- PostgreSQL, postgresql.conf
log_min_duration_statement = 200   -- логировать всё, что выполняется дольше 200 мс
-- MySQL / MariaDB
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 0.2;

Для самого HTTP-ответа полезно смотреть не только код ответа, но и время генерации в логах nginx — по умолчанию access_log его не пишет, формат нужно расширить:

log_format timing '$remote_addr - $status "$request" '
                   'rt=$request_time uct=$upstream_connect_time '
                   'uht=$upstream_header_time urt=$upstream_response_time';

access_log /var/log/nginx/timing.log timing;

Поле $upstream_response_time показывает, сколько реально думал бэкенд — если оно систематически растёт, а CPU при этом спокоен, дело почти наверняка в коде или в базе, а не в железе.

Вторая частая причина — сторонние интеграции: платёжный шлюз, SMS-провайдер, геокодер, антифрод-сервис, внешний API рекомендаций. Ваш сервер может быть быстрым, но если оформление заказа синхронно ждёт ответ от платёжного API, который в моменте отвечает не за 200 мс, а за 4 секунды, пользователь увидит зависшую кнопку «Оплатить» — а инфраструктурные метрики вашего сервера останутся зелёными, потому что проблема не у вас, а на стороне партнёра. Похожий кейс с разрывом между «мониторинг проверял» и «пользователь получил ошибку» разобран в статье «Мониторинг проверял главную, а лежала оплата» — там речь именно про слепую зону между технической доступностью и реальным пользовательским сценарием.

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

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Клиентская сторона: то, что происходит уже не на вашем сервере

Даже если сервер отдал HTML за 50 мс, страница может «грузиться» для пользователя ещё несколько секунд — и это время целиком проходит мимо серверного мониторинга, потому что происходит в браузере пользователя.

Основные источники клиентских тормозов:

  • Тяжёлый и неоптимизированный JavaScript. Большие бандлы без code-splitting, синхронная загрузка сторонних скриптов (аналитика, чаты, рекламные пиксели) блокируют рендеринг страницы до полной их загрузки и выполнения.
  • Неоптимизированные изображения. Картинка на 3-5 МБ вместо WebP/AVIF на 200-300 КБ — на быстром канале разница не так заметна, но на мобильном интернете это секунды ожидания.
  • Отсутствие ленивой загрузки (lazy loading). Браузер загружает всё содержимое страницы сразу, включая то, что пользователь никогда не проскроллит.
  • Web-шрифты и layout shift. Пока шрифт не загрузился, текст может «прыгать» — это не скорость в чистом виде, но напрямую портит воспринимаемое качество.
  • Слабое или перегруженное устройство пользователя. Старый телефон с медленным процессором будет тормозить даже на идеально быстром сервере — JS-код на нём просто выполняется дольше.

Сервер здесь физически не может ничего показать в своих метриках — вся эта цепочка происходит после того, как байты покинули хост. Единственный способ увидеть это — измерять на стороне клиента, тем самым методом, который называется Real User Monitoring (RUM), о нём — в следующем разделе.

Сеть и география: путь до конкретного пользователя

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

На практике задержка до пользователя зависит от:

  • физического расстояния и маршрута до его провайдера;
  • качества пиринга между вашим хостером и конкретным ISP пользователя;
  • локальных сетевых проблем на стороне провайдера пользователя (перегруженный узел, авария, DDoS у соседа по каналу);
  • наличия или отсутствия CDN перед статикой.

Хороший ping до сервера ещё не гарантирует хороший пользовательский опыт — разница между задержкой (latency) и пропускной способностью (bandwidth) разобрана в статье «Пинг хороший, а сайт медленный: задержка и полоса». Тот же принцип работает и здесь в масштабе: если вы проверяете сайт мониторингом из одного региона, а аудитория раскидана по нескольким, у части пользователей может быть объективно хуже — и вы об этом не узнаете, пока не начнёте измерять с их стороны, а не только со своей.

Для распределённой аудитории обычно помогает комбинация: CDN для статики (снимает нагрузку с origin-сервера и приближает контент к пользователю физически), плюс синтетические проверки из нескольких географических точек, а не одной. Но даже несколько точек синтетики — это по-прежнему выборка, а не полная картина: она не покроет специфику конкретного провайдера конкретного пользователя.

RUM и метрики уровня пользователя: что это и зачем

Real User Monitoring (RUM) — это сбор метрик производительности прямо в браузере реального посетителя, а не в синтетическом тесте с сервера или из лаборатории. Идея простая: вместо того чтобы гадать, что чувствует пользователь, по косвенным серверным признакам, измеряем это напрямую, у него в браузере, и отправляем данные вам.

Базовый минимальный вариант — снять время до первого байта и полное время загрузки прямо через Navigation Timing API браузера и отправить в свою систему аналитики или в лог:

<script>
window.addEventListener('load', () => {
  const nav = performance.getEntriesByType('navigation')[0];
  const payload = {
    ttfb: Math.round(nav.responseStart - nav.requestStart),
    domLoad: Math.round(nav.domContentLoadedEventEnd - nav.startTime),
    fullLoad: Math.round(nav.loadEventEnd - nav.startTime),
    url: location.pathname
  };
  navigator.sendBeacon('/rum-collect', JSON.stringify(payload));
});
</script>

Более полный подход — метрики в духе Core Web Vitals: показатели загрузки (насколько быстро отрисовался основной контент), отклика на взаимодействие (насколько быстро страница реагирует на клик или тап) и визуальной стабильности (насколько сильно элементы «прыгают» при загрузке). Их удобно собирать готовой библиотекой web-vitals от Google и отправлять на свой бэкенд тем же способом через sendBeacon, не привязываясь к конкретному внешнему сервису аналитики.

Важный нюанс: RUM-метрики нужно смотреть не как одно среднее число, а в разрезе процентилей — p50 (медиана), p90, p95. Среднее легко «спрячет» то, что происходит с самыми медленными 10% посетителей — а это часто как раз пользователи на слабой сети или старых устройствах, то есть те, кому реально хуже всего. Если у вас p50 хороший, а p95 в разы хуже — это сигнал не игнорировать проблему только потому, что «в среднем нормально».

RUM дополняет, а не заменяет синтетический и инфраструктурный мониторинг. Синтетика (Uptime Kuma, blackbox_exporter и подобные — можно посмотреть обзор инструментов в статье «Мониторинг доступности сайта: инструменты») хороша тем, что стабильна, воспроизводима и не зависит от того, есть ли сейчас живой трафик — она может разбудить вас ночью, когда пользователей ещё нет. RUM хорош тем, что показывает реальность настоящих людей на реальных устройствах и сетях, но требует, чтобы у сайта уже был трафик, и по своей природе шумнее — на неё влияет состав аудитории, который вы не контролируете.

Как собрать оба уровня метрик в одну картину на практике

На практике разумная схема мониторинга состоит из трёх слоёв, а не одного:

  1. Инфраструктурный слой — CPU, память, диск, сеть, аптайм процессов. Node Exporter + Prometheus + Grafana на самостоятельном сервере — рабочая и предсказуемая связка, установка описана в статье «Как установить и настроить Grafana и Prometheus на VPS».
  2. Синтетический слой — регулярные проверки ключевых сценариев (не только «главная открывается», а «корзина работает», «оплата проходит», «поиск отвечает») из одной или нескольких точек снаружи.
  3. RUM-слой — метрики прямо из браузеров реальных посетителей: TTFB, время до интерактивности, ошибки JS, процентили по географии и типу устройства.

Пример упрощённого пайплайна для самостоятельного хостинга RUM-данных: браузер отправляет метрику через sendBeacon на лёгкий эндпоинт, эндпоинт складывает событие в очередь или сразу пишет метрику в формате Prometheus, а Grafana строит дашборд поверх тех же данных, где уже живут инфраструктурные графики — так разрыв между «сервер здоров» и «пользователю хорошо» виден на одном экране, а не в двух разных системах, которые никто не сопоставляет.

[Браузер] --sendBeacon--> [Nginx / lightweight collector]
                                   |
                                   v
                          [Prometheus pushgateway
                           или прямая запись в TSDB]
                                   |
                                   v
                              [Grafana: один
                           дашборд, оба слоя метрик]

Полезная привычка — завести алерт не только на «CPU выше 90%», но и на «p95 TTFB по RUM выше N мс два периода подряд» или «доля ошибок JS в браузере выросла». Такой алерт срабатывает именно тогда, когда страдают пользователи, а не только тогда, когда страдает железо — а это, как мы разобрали выше, далеко не всегда один и тот же момент времени. Общий список того, что стоит проверять ежедневно на обоих уровнях, есть в статье «Какие метрики смотреть каждый день».

Важно не пытаться сразу построить идеальную систему. Минимально рабочий вариант — это уже: инфраструктурный дашборд (который у вас, скорее всего, и так есть) плюс один скрипт на 15 строк, который отправляет sendBeacon с TTFB и временем полной загрузки в отдельную таблицу или лог. Даже такой грубый RUM почти сразу покажет, есть ли разрыв между тем, что видите вы, и тем, что видит пользователь.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Если CPU и память в норме, а жалобы на тормоза есть — с чего начать разбор?

Сначала посмотрите время ответа бэкенда в логах ($upstream_response_time в nginx или аналог), затем медленные запросы к базе. Если бэкенд отвечает быстро — проблема почти наверняка на клиенте (JS, изображения) или в сети до конкретного пользователя, и без RUM это будет сложно локализовать.

RUM обязательно покупать как SaaS-сервис?

Нет, базовый вариант можно собрать самому: navigator.sendBeacon в браузере плюс лёгкий эндпоинт на сервере, который пишет метрики в ту же систему, где у вас уже живут Prometheus и Grafana. Это менее удобно, чем готовый коммерческий сервис с красивым UI из коробки, но полностью рабочее и бесплатное решение для старта.

Нужно ли RUM маленькому сайту с небольшим трафиком?

Если трафика мало, RUM-метрики будут шумными и статистически ненадёжными — на выборке в 20 визитов в день доверять процентилям не стоит. Для небольших проектов чаще достаточно синтетического мониторинга ключевых сценариев и периодической ручной проверки скорости через инструменты вроде Lighthouse.

Может ли синтетический мониторинг вообще заменить RUM?

Нет — синтетика проверяет из фиксированной точки на фиксированном «идеальном» окружении и не видит разброса реальных условий: разные устройства, сети, географию, состав браузеров. Она отлично ловит полные отказы сервиса, но плохо ловит деградацию, которая касается не всех пользователей одинаково.

Как понять, что проблема именно на стороне стороннего API, а не у себя?

Логируйте время каждого внешнего вызова отдельно от общего времени ответа — большинство HTTP-клиентов позволяют замерить это оборачиванием запроса таймером. Если общее время ответа растёт синхронно с временем внешнего вызова, а не с загрузкой вашего CPU — это внешняя зависимость, и стоит добавить таймаут и деградацию (например, показывать страницу без блока рекомендаций, если внешний сервис не ответил за разумное время).

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →