Веб-разработкаE-commerce и бизнес

Healthcheck дал 60 % запросов приложения: что показали первые сутки метрик

21 сентября 2026 года, в первые сутки учёта запросов по маршрутам, служебный /api/health получил 13 666 запросов — 60 % трафика приложения, и каждый шёл в общую базу. Рассказываем, какие две настройки это убрали, какую цену мы за них платим и как проверить свой healthcheck.

22 сентября 2026 г.
7 мин чтения

20 сентября 2026 года сайт LIONEX наконец начал считать собственные HTTP-запросы по маршрутам. На следующий день мы впервые посмотрели на полные сутки данных и увидели, что самый популярный адрес сайта — не главная и не блог. Им оказался /api/health, служебный маршрут, который ни один человек никогда не открывает. За сутки он получил 13 666 запросов, то есть 60 % всего трафика приложения.

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

Что показали первые сутки

Health-маршрут существует для платформы, а не для посетителей. Docker, настроенный через Coolify, периодически обращается к нему и по ответу решает, жив ли контейнер. Несколько плохих ответов подряд — и контейнер перезапускается.

Интервал этой проверки стоял 5 секунд. По настройкам это 17 280 вызовов в сутки; метрики за 21 сентября записали 13 666, меньше расчёта, но того же порядка. Сама частота ещё не беда: ответить «процесс жив» можно почти бесплатно. Беда сидела в коде маршрута. На каждый вызов он делал в базу SELECT 1, так что теоретически столько же, 17 280 запросов в сутки, база получала только для того, чтобы подтвердить, что она на месте.

Эта база стоит на том же сервере, что и ещё десяток наших проектов. В тот день load average сервера достиг 8,36 при пороге тревоги 8. Скажем прямо: какую долю этой нагрузки давал healthcheck, мы не измеряли и причиной его не называем. Видели мы другое — на общей машине круглосуточно крутилась лишняя работа, о которой никто не знал.

Почему никто не замечал

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

Хуже то, что по отдельности всё выглядело разумно. Пять секунд были значением в панели деплоя, их никто не выбирал сознательно. Запрос в базу когда-то дописали с добрыми намерениями, «чтобы проверка честно смотрела на базу»: health, который базы не касается, может называть здоровым контейнер, уже не способный отдать ни одной страницы. Каждое решение по отдельности защитимо. Вместе они давали запрос в общую базу каждые пять секунд, без конца.

Два рычага, и нужны оба

Первый — интервал. 21 сентября мы изменили его с 5 на 30 секунд. Количество попыток оставили 10, так что до перезапуска контейнер теперь должен проваливать проверки примерно 5 минут (10 × 30 с). Раньше хватало 50 секунд (10 × 5 с).

У этого решения есть цена, и её стоит назвать: настоящий отказ мы теперь замечаем медленнее. Для сайта агентства без очереди заказов это приемлемо. Для платёжного сервиса, где каждая минута простоя стоит денег, — возможно, нет, и там мы взвешивали бы иначе.

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

Одного интервала было бы мало: с ним каждый вызов по-прежнему шёл бы в базу, просто реже. Один кеш убрал бы нагрузку на базу, но платформа и дальше дёргала бы процесс каждые пять секунд: 17 280 вызовов в сутки, каждый с запуском curl внутри контейнера. Вместе они разводят две разные вещи: как часто платформу интересует состояние и как часто ответ действительно доходит до базы.

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

Почему ошибку не кешируем

Запоминаем только успех. Если база не ответила, маршрут возвращает 503 с текстом ошибки, сбрасывает отметку успеха, и следующий вызов снова идёт в базу.

Логика несимметрична намеренно. Успех — обычное состояние, и его кеширование снимает почти всю лишнюю работу. Неудача — ровно тот момент, когда по ответу платформа решает, перезапускать ли контейнер, поэтому каждая проверка после неё должна быть свежей. Если бы отметка успеха после сбоя оставалась, контейнер считался бы здоровым ещё минуту после падения базы — именно тогда, когда его нужно перезапускать.

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

Проверка

Сначала локально: 13 вызовов подряд дали один запрос в базу, а вызов через 65 секунд снова пошёл в базу. 22 сентября тесты маршрута переписали: старый тест описывал версию, которой давно не существовало, и вообще не запускался, потому что проекту не хватало пакета jsdom. Новые проверяют ответ 200 и «ok», когда база на месте; отсутствие второго запроса в базу в течение 60 секунд; 503 с текстом ошибки, когда база недоступна; и то, что ошибка не кешируется.

Конфиг контейнера в тот же день: Interval 30 с, Timeout 5 с, Retries 10.

Цифры «после» и доля, которая почти не изменилась

22 сентября Prometheus за 15 минут вне окна деплоя показал 31,5 запроса к /api/health (дробь — следствие того, как Prometheus считает прирост счётчика на отрезке). Это около 126 в час, или примерно 3 000 в сутки вместо 13 666.

Кеш ограничивает число запросов в базу одним в минуту, то есть не больше 1 440 в сутки вместо 17 280. Эта цифра рассчитана из настроек, отдельно запросы к базе мы не измеряли.

Доля же почти не сдвинулась. За те же 15 минут health дал 31,5 из 55 запросов приложения, то есть по-прежнему большинство. Трафик сайта агентства невелик, и служебный адрес занимает в нём заметное место при любом интервале. Проблема никогда не заключалась в доле. Она заключалась в том, что каждый из этих вызовов шёл в общую базу.

Ловушка замера: деплой в окне

Первая попытка измерить «после» дала 690 запросов за час, будто мы ничего не меняли. Причина нашлась в самом окне: туда попал деплой нового образа. Во время развёртывания платформа сама часто опрашивает health нового контейнера, а в системе метрик какое-то время живут две серии, старого и нового контейнера. За 15 минут после деплоя вышло 31,5.

Вывод простой: измерять вне окна деплоя, иначе легко «доказать», что правка не сработала.

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

  1. Посмотрите интервал. Для Docker:

    docker inspect -f '{{json .Config.Healthcheck}}' <контейнер>
    

    Interval там в наносекундах: 5000000000 — это 5 секунд. В Kubernetes то же самое лежит в periodSeconds для liveness и readiness.

  2. Прочитайте код health-маршрута. Что он делает на каждый вызов: идёт в базу, в Redis, во внешний API? Умножьте каждое такое действие на количество вызовов в сутки:

    вызовов в сутки = 86 400 / интервал в секундах
    86 400 / 5 = 17 280
    
  3. Разведите «процесс жив» и «готов обслуживать». Kubernetes прямо разделяет liveness и readiness. В Docker проверка одна, поэтому рабочий компромисс такой: кешировать успех и не кешировать ошибку.

  4. Хотя бы раз посчитайте запросы по маршрутам — из логов прокси или из метрик приложения. Без этого такие вещи не видны в принципе.

  5. Умножьте Retries на Interval. Результат — сколько времени контейнер работает сломанным до перезапуска. Это число стоит выбрать сознательно, а не получить в наследство от значений по умолчанию.

Границы

Всё описанное касается одного сайта на одной платформе, Coolify с Docker. Цифры «до» взяты за одни сутки, «после» — за 15 минут плюс конфиг контейнера. Количество запросов к базе после правки рассчитано, а не измерено.

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

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

Для себя мы вынесли одно правило: служебный маршрут — тоже трафик, и считать его нужно так же, как страницы для людей.

Теги

PerformanceАналитика

🤔Вам понравилась статья?

Ваше мнение помогает нам создавать лучший контент

Поделитесь с друзьями

Нашли что-нибудь полезное? 🚀

Помогите другим узнать это — поделитесь статьей в социальных сетях

https://lionex.com.ua/blog/healthcheck-60-vidsotkiv-zapytiv

💚 Спасибо, что помогаете нам расти

Владислав Чистяков

Пишет о том, что делает руками: интернет-магазины на OpenCart, приложения на Next.js, интеграции и скорость сайтов. В статьях — замеры и проверки, которые читатель может повторить на своём проекте, а не общие советы. Коммерческая разработка с 2015 года.

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

Ответы на популярные вопросы по теме

Потому что платформа опрашивает его круглосуточно с фиксированным интервалом, а посетителей у небольшого сайта немного. У нас 21 сентября 2026 года, в первые сутки учёта запросов по маршрутам, /api/health получил 13 666 запросов — 60 % всего трафика приложения. Интервал стоял 5 секунд, а маршрут на каждый вызов делал в базу SELECT 1.

Выполните docker inspect -f '{{json .Config.Healthcheck}}' <контейнер>. Interval там указан в наносекундах: 5000000000 означает 5 секунд. Количество вызовов в сутки — 86 400, делённое на интервал в секундах; для 5 секунд это 17 280. В Kubernetes то же самое задаёт periodSeconds в liveness и readiness.

Начнёт, и эту цену нужно принимать сознательно. Мы изменили интервал с 5 на 30 секунд при тех же 10 попытках, так что до перезапуска контейнера теперь проходит примерно 5 минут вместо 50 секунд. Для сайта агентства без очереди заказов это приемлемо, для платёжного сервиса — возможно, нет.

Потому что по ответу health платформа решает, перезапускать ли контейнер, и после сбоя каждая проверка должна быть свежей. У нас успех запоминается на 60 секунд, а неудача возвращает 503, сбрасывает отметку успеха, и следующий вызов снова идёт в базу. Иначе контейнер считался бы здоровым ещё минуту после падения базы. Такой кеш работает в долгоживущем сервере Node, в serverless он ничего не запомнит.

Потому что в окно замера попал деплой нового образа. Во время развёртывания платформа часто опрашивает health нового контейнера, а в метриках какое-то время живут две серии — старого и нового. Первая попытка дала 690 запросов за час, а 15 минут вне окна деплоя — 31,5 запроса, около 126 в час. Измерять нужно вне деплоя.

Получайте лучшие статьи на почту

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

Мы уважаем вашу конфиденциальность. Отписаться можно в любой момент.

Похожие статьи

Все статьи
Сертификаты продлеваются сами: TLS на 116 сайтах и один, который не продлился
Веб-разработка

Сертификаты продлеваются сами: TLS на 116 сайтах и один, который не продлился

22 сентября 2026 года мы проверили HTTPS-сертификаты на 116 сайтах, которые уже измеряли для рыночных исследований, и для 104 из них сравнили состояние с августовским. Все 116 прошли проверку, 108 стоят на 89- и 90-дневных бесплатных сертификатах, и только у одного сайта сертификат до сих пор не продлился, хотя при типичных настройках уже должен был. Разбираем замеры, границы метода и показываем, как проверить свой сайт одной командой.

9 мин
22 сент.
Читать дальше
154 ошибки типов, которые выбрасывала сборка: что в них нашлось
Веб-разработка

154 ошибки типов, которые выбрасывала сборка: что в них нашлось

21 сентября 2026 года проверка типов на нашем сайте показала 154 ошибки, хотя сборка каждый раз была зелёной: результат проверки просто выбрасывался. Внутри нашлись нули в статистике ссылок и в экспорте аналитики, сортировка, которая не сортировала, и тесты, которые не запускались. Рассказываем, как разбирали, что изменили и как проверить свой проект.

9 мин
22 сент.
Читать дальше
Мы потеряли собственный магазин вместе с доменом: что об этом знает веб-архив
Техническое SEO

Мы потеряли собственный магазин вместе с доменом: что об этом знает веб-архив

17 сентября 2026 мы пересчитали по веб-архиву, как выглядела потеря собственного магазина вместе с доменом: 2 378 снимков главной в марте 2022, первый 301 первого апреля, последний снимок нашего содержимого 18 мая. Показываем, что из архива восстанавливается, а что нет.

9 мин
17 сент.
Читать дальше