мониторинг · Zabbix · observability · RED · эксплуатация
Все графики зелёные, а сервис не работает
Вторая статья о взрослении мониторинга: как перейти от состояния сервера к latency, ошибкам, очередям, пулам и проверке реальной пользовательской операции.
Это вторая статья из серии «Как построить систему мониторинга, которая действительно работает. Опыт, ошибки и взросление».
В первой части мы поставили стандартные шаблоны, начали следить за CPU, памятью, дисками и обязательными службами. Для начала этого достаточно.
Теперь наступает момент, когда мониторинг впервые серьёзно подводит администратора.
Сервер доступен. Ресурсы в норме. Процессы запущены. На дашборде всё зелёное.
А пользователи работать не могут.
Зелёный сервер
Возьмём собирательный, но вполне реальный случай.
Есть внутреннее веб-приложение. Оно работает на виртуальном сервере, использует отдельную базу данных и отправляет часть заданий в очередь фоновой обработки.
В мониторинге всё выглядит прилично: сервер отвечает на ping, агент передаёт данные, CPU загружен примерно на 35%, памяти достаточно, на диске много свободного места, процесс приложения запущен, порт 443 доступен, а главная страница возвращает HTTP 200.
Затем начинают поступать жалобы.
Сначала один пользователь говорит, что документ долго сохраняется. Потом второй не может открыть карточку клиента. Через несколько минут поддержка сообщает, что приложение «тормозит у всех».
Открываем мониторинг — всё зелёное.
Открываем главную страницу — она загружается.
Перезапускаем браузер, проверяем сеть, просим пользователя выйти и войти. Иногда операция проходит, иногда заканчивается ошибкой.
На этом этапе появляется первая опасная мысль:
Возможно, проблема не у нас.
Однако через полчаса выясняется, что проблема именно у нас. Просто мониторинг смотрел не туда.
Процесс работал. Сервис — нет
Приложение действительно было запущено. Оно принимало TCP-соединения и отдавало простую главную страницу.
Но все соединения из приложения к базе данных оказались заняты.
Допустим, приложение может одновременно держать 100 соединений. Обычно используется 20–30. После обновления один из запросов стал выполняться значительно дольше. Соединения перестали быстро возвращаться в пул.
Сначала было занято 60 соединений, потом 80, затем все 100.
Новые запросы стали ждать освобождения соединения. Появилась внутренняя очередь. Часть операций завершалась через несколько секунд, часть — по тайм-ауту. Пользователи нажимали кнопку ещё раз, создавая дополнительные запросы.
CPU при этом не обязан быть загружен на 100%. Большинство потоков ничего не вычисляет — они ждут.
Память не закончилась. Диск не заполнился. Процесс не упал. Порт не закрылся.
Сервис перестал нормально выполнять свою работу, но с точки зрения мониторинга сервер оставался совершенно здоровым.
Мы проверяли наличие, а не результат
Проверка процесса отвечает на очень узкий вопрос:
Существует ли процесс с таким именем?
Проверка порта отвечает на другой:
Принимает ли кто-нибудь TCP-соединение на этом порту?
Даже HTTP-запрос к главной странице подтверждает только то, что конкретный URL вернул какой-то ответ.
Ни одна из этих проверок не доказывает, что пользователь может войти в систему, найти документ и сохранить изменение.
Здесь и проходит граница между мониторингом компьютера и мониторингом сервиса.
Компьютер может работать безупречно, а сервис — быть бесполезным.
Сто процентов занятости бывают не только у CPU
Когда говорят о нехватке ресурсов, обычно представляют процессор, память или место на диске. Но почти у любого приложения есть менее заметные ограниченные ресурсы: соединения с базой данных, рабочие потоки, очередь запросов, HTTP-соединения, файловые дескрипторы, лицензии, процессы-обработчики, доступные сессии или лимиты внешнего API.
Их тоже можно исчерпать.
Причём для пользователя результат будет тем же: сервис работает медленно или не работает вообще.
В примере с базой нас должны интересовать не только CPU и память сервера СУБД. Нам нужны размер пула, количество занятых и ожидающих соединений, время выполнения запросов, число тайм-аутов и ошибки получения соединения.
Самое неприятное, что загрузка пула с 20 до 80% ещё не обязательно вызывает ошибку. Сервис продолжает работать, но запас прочности быстро исчезает.
Пользователь заметит проблему позже. Мониторинг мог бы заметить её раньше.
Очередь — это время, которое кто-то потеряет
Очереди встречаются повсюду, даже если в архитектуре нет RabbitMQ или Kafka.
Запросы ожидают свободного потока. Задания ожидают обработчика. Письма ждут отправки. Запросы к диску ждут выполнения. Клиенты ждут свободного соединения с базой.
Пока производительность обработчиков выше скорости поступления новых задач, очередь остаётся небольшой.
Если новые задачи появляются быстрее, чем система успевает их обрабатывать, очередь начинает расти.
Это один из самых полезных ранних сигналов. Ошибок ещё может не быть. CPU ещё может выглядеть нормально. Пользователи пока только замечают небольшую задержку.
Но очередь уже говорит, что система перестала справляться.
При этом одного размера очереди недостаточно. Сто заданий могут быть нормой, если они обрабатываются за десять секунд. Десять заданий могут быть аварией, если самое старое ждёт уже три часа.
Поэтому полезно смотреть не только на количество элементов, но и на возраст самого старого элемента, скорость поступления и скорость обработки.
Среднее значение умеет успокаивать
Допустим, за минуту приложение обработало десять запросов:
0,1 0,1 0,2 0,2 0,2 0,3 0,3 0,4 8,0 12,0 секунды
Среднее время ответа получилось около двух секунд.
На дашборде это может выглядеть терпимо. Однако два пользователя из десяти ждали 8 и 12 секунд. Для них сервис уже был неисправен.
Если таких операций тысячи, среднее значение ещё лучше скрывает медленные запросы. Большинство быстрых ответов «разбавляет» небольшое количество очень плохих.
Поэтому время ответа полезно смотреть через распределение и процентили.
p95 = 2 секунды означает, что 95% операций завершились не дольше чем за две секунды. Оставшиеся 5% выполнялись дольше.
p99 показывает хвост распределения ещё ближе к самым медленным запросам.
Процентили тоже не дают всей картины, но гораздо лучше отвечают на пользовательский вопрос: сколько людей столкнулось с плохой скоростью работы?
Среднее значение говорит о системе в среднем. Пользователь никогда не бывает средним — он выполняет одну конкретную операцию и ждёт её завершения.
Проверять нужно то, ради чего существует сервис
Следующий шаг кажется очевидным, но на практике его часто откладывают: нужно воспроизводить простую пользовательскую операцию.
Не просто открыть TCP-порт базы, а выполнить лёгкий запрос.
Не просто проверить SMTP-порт, а отправить тестовое письмо и убедиться, что оно дошло.
Не просто увидеть запущенную службу резервного копирования, а проверить возраст последней успешной копии и периодически выполнять тестовое восстановление.
Не просто получить HTTP 200 от главной страницы, а пройти короткий критичный сценарий: авторизоваться, открыть контрольный объект и выполнить безопасную операцию чтения.
Такие проверки называют синтетическими. Мониторинг сам изображает пользователя и проверяет результат.
Но синтетический сценарий должен быть коротким, безопасным и предсказуемым. Не стоит каждую минуту создавать настоящий заказ, отправлять сообщение реальному клиенту или изменять производственные документы.
Для проверки лучше использовать отдельную техническую учётную запись и тестовые данные. Права этой учётной записи должны быть минимальными. Её действия необходимо отличать в журналах от действий настоящих пользователей.
Один URL не представляет всё приложение
У веб-приложения может быть быстрый /health, который всегда отвечает OK.
Это полезно для балансировщика или оркестратора: процесс жив и способен принять запрос. Но такой endpoint нередко продолжает отвечать, когда база недоступна, очередь переполнена или внешний сервис не работает.
Если заставить health check проверять вообще все зависимости, появится другая проблема. Временная ошибка второстепенной интеграции может вывести из балансировки все экземпляры исправного приложения.
Поэтому проверки лучше разделять.
Одна сообщает, что процесс жив. Другая — что он готов принимать трафик. Третья проверяет зависимости. Четвёртая контролирует пользовательскую операцию.
Эти проверки похожи, но отвечают на разные вопросы и требуют разных действий.
Что стоило добавить после инцидента
После истории с пулом соединений легко добавить десятки метрик. Но сначала нужны те, которые объясняют проблему и позволяют заметить её раньше.
Я бы начал с нескольких сигналов: время выполнения критичных операций, количество успешных и ошибочных запросов, число тайм-аутов, занятость пула соединений, количество запросов, ожидающих соединение, размер очереди и возраст старейшего задания, время выполнения запросов к базе и одна синтетическая пользовательская операция.
После настройки нужно ответить на главный вопрос:
Что из этого показало бы проблему за 15 минут до массовых жалоб?
В нашем примере ранними сигналами могли быть рост времени запросов к базе, увеличение занятости пула и появление ожидающих соединений.
HTTP 500 и жалобы пользователей появились позже.
Теперь это можно назвать терминами
Когда практическая картина понятна, можно достать теорию.
Для сервисов часто используют подход RED:
- Rate — сколько запросов или операций выполняется;
- Errors — сколько из них заканчивается ошибкой;
- Duration — сколько времени они занимают.
Google SRE предлагает четыре золотых сигнала:
- latency;
- traffic;
- errors;
- saturation.
Три первых пункта во многом пересекаются с RED. Saturation добавляет важный вопрос: насколько система приблизилась к своему пределу?
В нашем случае saturation — это заполненный пул соединений и растущая очередь.
Названия методик полезны: с ними проще сверить покрытие разных сервисов. Но начинать проектирование мониторинга всё равно следует с назначения системы и пользовательских операций, а не с желания заполнить очередной шаблон.
Не каждая аномалия должна разбудить инженера
Рост занятости пула до 80% полезно видеть на графике. Но это не всегда повод немедленно отправлять критическое уведомление.
Возможно, каждое утро в это время запускается отчёт, а через пять минут показатель возвращается к норме. Возможно, пул увеличен с большим запасом и система продолжает выполнять пользовательские операции с нормальной скоростью.
Срочное уведомление лучше связывать с заметным воздействием: критичная операция перестала выполняться, время ответа превышает допустимое значение, доля ошибок стала значительной, очередь продолжает расти или ресурс практически исчерпан и восстановление само по себе маловероятно.
А внутренние показатели — занятость пула, число потоков, задержки базы — помогут объяснить причину.
Получается простое разделение:
О проблеме сообщает пользовательский симптом, а внутренние метрики помогают её расследовать.
Это не абсолютное правило. Иногда раннее предупреждение о насыщении спасает от аварии. Но оно должно требовать понятного действия, а не просто констатировать необычное число.
Мониторинг нужно проверить под нагрузкой
После добавления новых метрик мы всё ещё только предполагаем, что они помогут.
Проверить это можно контролируемым экспериментом на тестовой среде.
Например, временно уменьшить размер пула соединений и создать небольшую нагрузку. Затем посмотреть, в какой последовательности изменяются показатели:
- Растёт число занятых соединений.
- Появляются ожидающие запросы.
- Увеличивается время ответа.
- Начинаются тайм-ауты.
- Синтетическая операция завершается ошибкой.
Так мы получаем не просто набор графиков, а модель развития инцидента.
Одновременно можно проверить уведомление: дошло ли оно, понятно ли сформулировано, есть ли в нём ссылка на нужный дашборд и может ли дежурный инженер определить первый шаг.
Эксперимент должен быть согласованным и безопасным. На продуктивной системе случайно устроить отказ ради проверки мониторинга — плохая идея. Но совсем не проверять новые правила тоже опасно.
Как это выглядит для разных систем
Принцип не привязан к веб-приложению.
Для почтовой системы сервер может отвечать, SMTP-порт быть открытым, а письма часами лежать в очереди. Пользовательская проверка — отправить сообщение и измерить время доставки.
Для Active Directory контроллер доступен, но репликация остановилась. Проверять нужно не только службы, но и состояние репликации, DNS, время и возможность выполнить тестовую аутентификацию.
Для резервного копирования служба работает каждый день, но задания завершаются ошибкой. Результат — свежая успешная копия и периодически подтверждённое восстановление.
Для файлового сервера ping ничего не говорит о возможности открыть файл. Нужна проверка SMB, задержек хранилища и безопасная операция чтения из контрольной папки.
Для фонового обмена важно не только наличие процесса, но и время последней успешной обработки, число необработанных объектов и возраст самого старого из них.
У разных систем будут разные метрики. Вопрос остаётся одним:
Какую полезную работу должна выполнять эта система и как доказать, что она выполняется сейчас?
Что изменилось в нашем мониторинге
После первой статьи мы знали, что сервер доступен и его основные ресурсы не закончились.
Теперь мы научились отличать четыре разных состояния:
| Состояние | Что это означает |
|---|---|
| Узел доступен | До операционной системы проходит сеть |
| Процесс запущен | Программа существует и не завершилась |
| Сервис отвечает | Программа принимает и обрабатывает запросы |
| Пользовательская операция работает | Система выполняет полезную функцию с приемлемым результатом |
Все четыре уровня важны. Но только последний непосредственно описывает то, ради чего инфраструктура существует.
Следующая проблема появится там, где пользователь не ждёт ответа прямо сейчас: в фоновых заданиях, очередях, интеграциях и резервных копиях. Они могут не работать несколько дней, прежде чем кто-нибудь это заметит.
Практическая памятка
Этот раздел — краткая выжимка для проверки своей системы и анализа с помощью ИИ.
Для каждого критичного сервиса определите
- Основную пользовательскую функцию.
- Одну безопасную синтетическую операцию.
- Допустимое время её выполнения.
- Допустимую долю ошибок.
- Ограниченные внутренние ресурсы.
- Очереди и пулы, в которых может накапливаться ожидание.
- Основные зависимости.
- Владельца сервиса.
- Действие при срабатывании уведомления.
Проверьте, собираются ли сигналы
- Интенсивность запросов или операций.
- Доля успешных и ошибочных операций.
- Время выполнения и его процентили.
- Количество тайм-аутов.
- Занятость пулов и рабочих процессов.
- Размер очередей.
- Возраст старейшего элемента очереди.
- Время ответа зависимостей.
- Результат синтетической операции.
- Свежесть самих данных мониторинга.
Выжимка для ИИ
article_stage: service_observability
goal:
Detect degradation of useful service functions
before or at the same time as users notice it.
core_distinction:
host_available: network and operating system respond
process_running: application process exists
service_responding: application accepts requests
user_operation_working: useful operation completes within expectations
primary_signals:
rate: operations per unit of time
errors: failed operations and timeouts
duration: latency distribution, including p95 and p99
saturation: proximity to internal resource limits
common_saturation_points:
- database_connection_pool
- worker_pool
- request_queue
- file_descriptors
- external_api_limits
- licenses
- concurrent_sessions
alerting_principle:
Page primarily on actionable user impact.
Use internal metrics to explain the cause
and provide early warning when action is clear.
validation:
Test monitoring with a controlled failure
or controlled saturation experiment.
next_maturity_question:
What can remain broken for hours or days
because no user is waiting for the result right now?
Источники и дальнейшее чтение
Разделение monitoring symptoms и causes, а также четыре золотых сигнала описаны в главе Monitoring Distributed Systems книги Google SRE.
Подход RED широко применяется для наблюдения за сервисами: rate, errors и duration. Для инфраструктурных и внутренних ресурсов его удобно дополнять USE Method Брендана Грегга.
Особенности расчёта гистограмм и процентилей в Prometheus разобраны в официальном материале Histograms and summaries.
В следующей статье — фоновые задания, очереди, интеграции и резервные копии: как обнаружить процесс, который формально запускается каждый день, но уже неделю не приносит результата.