Веб-сервер сам рассказывает о своей нагрузке, но по умолчанию эта информация закрыта. Модуль состояния отдаёт несколько чисел, по которым видно, растёт ли очередь соединений, успевает ли сервер отвечать и где упирается обработка. Для большинства задач хватает периодического опроса одной страницы и простого журнала. Разбираем, как включить модуль, что означает каждое число и как по их изменению находить причину перегрузки.
Что отдаёт страница состояния и зачем она нужна
Модуль stub_status входит в стандартную поставку и не требует сторонних зависимостей. Он возвращает короткий текстовый ответ с семью значениями, которые описывают состояние сервера на момент запроса.
Active connections: 291
server accepts handled requests
16630948 16630948 31070465
Reading: 6 Writing: 179 Waiting: 106
Ответ состоит из четырёх строк. Первая показывает число активных клиентских соединений, включая ожидающие. Вторая содержит подписи счётчиков, третья три числа: сколько соединений принято, сколько из них обработано и сколько запросов выполнено за всё время работы процесса. Четвёртая строка разбивает активные соединения по состояниям.
Эти данные накопительные и не зависят от журнала запросов. Журнал пишет строку на каждый запрос и при высокой нагрузке сам становится узким местом, а страница состояния отдаётся за доли миллисекунды. Опрос раз в несколько секунды даёт картину динамики, достаточную для вывода о том, справляется сервер или нет.
Рост числа активных соединений при неизменной частоте запросов означает, что ответы замедлились. Обратная картина, когда частота растёт, а число соединений прежнее, говорит о нормальной работе с короткими запросами.
Включение модуля и ограничение доступа
Страницу состояния описывают в отдельном месте расположения. Доступ к ней стоит ограничить, поскольку счётчики раскрывают особенности нагрузки, а иногда и сам факт существования службы.
server {
listen 127.0.0.1:8081;
server_name _;
location /stub_status {
stub_status;
access_log off;
}
}
Привязка слушателя к локальному адресу простейший способ закрыть страницу от внешних обращений: снаружи такой порт недоступен, а сбор метрик выполняется с той же машины. Если опрос идёт с другого узла, доступ ограничивают фильтром по адресам.
location /stub_status {
stub_status;
allow 192.168.10.0/24;
deny all;
access_log off;
}
Отключение журнала для этого места расположения убирает из логов тысячи одинаковых строк, которые не несут сведений о посетителях. Если сборщик работает с другой машины и журнал всё же нужен, для него задают отдельный файл, чтобы служебный трафик не смешивался со статистикой посещений.
location /stub_status {
stub_status;
access_log /var/log/nginx/status-access.log;
}
Отдельный слушатель на внутреннем порту удобнее публичного пути по двум причинам. Метрики не попадают в общую статистику посещений, и их нельзя запросить снаружи даже при ошибке в правилах фильтрации на основном слушателе. На этом же порту позже размещают другие служебные пути, например проверку работоспособности, которая возвращает короткий ответ с кодом 200.
location = /healthz {
return 200 "ok\n";
add_header Content-Type text/plain;
access_log off;
}
Проверка выполняется одной командой.
curl -s http://127.0.0.1:8081/stub_status
Наличие модуля в сборке проверяют выводом параметров компиляции: среди аргументов должен присутствовать модуль состояния.
nginx -V 2>&1 | tr ' ' '\n' | grep stub_status
Как читать счётчики и считать частоту запросов
Все значения счётчиков накопительные и сбрасываются при перезапуске процесса. Смысл имеет не абсолютное число, а разница между двумя замерами, делённая на интервал.
read -r a1 h1 r1 < <(curl -s http://127.0.0.1:8081/stub_status | awk 'NR==3{print $1,$2,$3}')
sleep 10
read -r a2 h2 r2 < <(curl -s http://127.0.0.1:8081/stub_status | awk 'NR==3{print $1,$2,$3}')
echo "соединений/с: $(( (a2-a1)/10 )) запросов/с: $(( (r2-r1)/10 ))"
Отношение числа запросов к числу соединений показывает, сколько запросов выполняется в одном соединении. Значение около единицы означает, что каждое соединение обслуживает один запрос, и повторное использование соединений не работает. Значение в десятки единиц говорит о том, что посетители загружают много файлов в одном соединении, и это нормальная картина для страниц с большим числом изображений.
Разница между счётчиками принятых и обработанных соединений служит индикатором отказов на этапе установки соединения. В норме эти числа равны. Расхождение означает, что часть соединений была принята, но не обработана, и причина обычно в исчерпании предела на число одновременных подключений либо в нехватке рабочих процессов.
Предел задают директивой worker_connections в расчёте на один рабочий процесс, а общее число зависит от количества процессов.
worker_processes auto;
events {
worker_connections 2048;
multi_accept on;
}
Если число активных соединений приближается к произведению двух значений, сервер упрётся в предел, и новые посетители получат отказ.
Состояния Reading, Writing и Waiting
Три состояния активных соединений описывают разные этапы обработки и по-разному ведут себя при проблемах.
Reading означает, что сервер читает заголовки запроса от клиента. Большое число таких соединений характерно для медленных клиентов и для атак с намеренно затянутой отправкой заголовков. В нормально работающей службе это единицы.
Writing означает, что сервер формирует и отправляет ответ. Рост этого числа говорит о том, что ответы стали длиннее либо медленнее, и часто связано с медленной работой внутренней службы или с отдачей крупных файлов.
Waiting означает соединения, которые остаются открытыми в ожидании следующего запроса. Это следствие режима повторного использования соединений, и само по себе число не является признаком проблемы, хотя потребляет ресурсы.
keepalive_timeout 30s;
keepalive_requests 1000;
Сокращение времени ожидания и ограничение числа запросов в одном соединении снижают долю Waiting и освобождают слоты. Значение по умолчанию в семьдесят пять секунд часто избыточно для публичных сайтов.
Сопоставление трёх чисел даёт диагноз. Много Reading при малом Writing указывает на медленных клиентов. Много Writing при малом Reading указывает на медленные ответы внутренних служб. Много Waiting при малых остальных значениях означает, что сервер просто держит открытые соединения, и реальной нагрузки нет.
Расширенные метрики Angie и интерфейс управления
Сервер Angie включает модуль API, который отдаёт подробные сведения в формате JSON: данные по соединениям, зонам разделяемой памяти, DNS-запросам, HTTP-запросам, кэшу ответов и сессиям потокового модуля. Директивы status в Angie нет, и попытка её использовать завершится сообщением о неизвестной директиве на этапе проверки конфигурации, поэтому интерфейс включают директивой api с указанием пути.
status_zone site1;
server {
listen 127.0.0.1:8082;
location /status/ {
api /status/;
allow 127.0.0.1;
deny all;
}
}
Статистика по серверу и по группе серверов появляется в ответе только при заданной зоне: status_zone указывают в блоке server, а параметр zone в блоке upstream.
curl -s http://127.0.0.1:8082/status/ | python3 -m json.tool | head -n 40
Ответ содержит число запросов по кодам, объём переданных данных и состояние кэша, и этого достаточно для построения графиков без внешнего агента.
Тот же интерфейс позволяет менять конфигурацию запросами в разделе config, поэтому доступ к нему ограничивают по адресам обязательно.
location /api {
api /api;
allow 127.0.0.1;
deny all;
}
Отдельный модуль отдаёт метрики в формате Prometheus, что удобно при наличии системы хранения метрик. Правило выбора простое: stub_status подходит для быстрого опроса и совместимости со сборщиками, интерфейс API для подробных данных по серверу и группе серверов.
Сбор метрик по расписанию и ведение журнала
Периодический опрос удобно оформить сценарием, который пишет значения в файл с отметкой времени. Такой журнал занимает килобайты в сутки и позволяет ответить на вопрос, что происходило с сервером ночью.
#!/bin/sh
# /usr/local/bin/collect-stub-status.sh
URL="http://127.0.0.1:8081/stub_status"
LOG="/var/log/nginx/status.log"
while true; do
curl -s "$URL" | awk -v t="$(date +%s)" '
NR==1 { active=$3 }
NR==3 { acc=$1; han=$2; req=$3 }
NR==4 { print t","active","acc","han","req","$2","$4","$6 }
' >> "$LOG"
sleep 5
done
Запись в виде строки с разделителями позволяет позже загрузить файл в таблицу и построить график. Поля идут в фиксированном порядке: отметка времени, активные соединения, три счётчика и три состояния. Если в конкретной сборке числа выводятся в одной строке с подписями, номер строки уменьшают на единицу, а вместо первых полей берут третье, четвёртое и пятое.
Запуск оформляют службой с автоматическим перезапуском.
# /etc/systemd/system/status-collector.service
[Unit]
Description=Сбор метрик stub_status
After=network.target nginx.service
[Service]
ExecStart=/usr/local/bin/collect-stub-status.sh
Restart=always
User=www-data
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now status-collector.service
Сбор с нескольких машин ведут одним из двух способов. Первый, локальный сценарий на каждом узле с последующим объединением файлов. Второй, опрос по сети с машины сбора, при котором страница состояния остаётся закрытой для внешнего мира, а доступ к ней открывается только для адреса сборщика. Второй способ проще в обслуживании, но требует открытого порта во внутренней сети и правила фильтрации, разрешающего обращение только с одного адреса.
Ротацию журнала настраивают отдельно, иначе файл растёт без ограничения. При интервале в пять секунд объём составляет около двух мегабайт в сутки, и хранение за месяц остаётся незаметным для диска.
Поиск узких мест по динамике показателей
Набор признаков, по которым определяют характер проблемы, невелик. Первый признак, расхождение счётчиков принятых и обработанных соединений, указывает на исчерпание предела подключений или на отказы при установке соединения. Второй, рост Writing при стабильной частоте запросов, указывает на замедление ответов. Третий, всплески Active connections в одно и то же время, обычно связаны с задачей по расписанию или с обходом сайта сборщиком.
awk -F, '{print strftime("%H:%M",$1), $2}' /var/log/nginx/status.log | tail -n 40
Сопоставление графика активных соединений с журналом ошибок показывает, сопровождается ли рост нагрузки сообщениями об отказах. Рост без ошибок означает, что сервер справляется, и предел ещё не достигнут.
Отдельно проверяют, не упирается ли нагрузка в систему, а не в сервер. Число переключений контекста, доля времени в ожидании ввода-вывода и загрузка процессора по ядрам объясняют замедление ответов лучше, чем сами счётчики запросов.
vmstat 2 5
iostat -x 2 3
Для оценки суточной картины достаточно агрегировать журнал по часам. Такой срез показывает, в какое время сервер ближе всего к пределу, и помогает планировать изменения.
awk -F, '{h=strftime("%H",$1); a[h]+=$2; n[h]++} END{for(k in a) print k, int(a[k]/n[k])}' \
/var/log/nginx/status.log | sort
Среднее число активных соединений по часам вместе с пиковыми значениями даёт представление о запасе прочности. Если в час пик среднее значение составляет треть от предела рабочих соединений, запас есть, а если приближается к нему, предел стоит поднять до того, как начнутся отказы.
Полезная практика состоит в том, чтобы зафиксировать показатели в спокойном состоянии и считать их базой. Отклонение от базы в несколько раз это и есть сигнал, а абсолютные числа без базы почти ничего не говорят: для одного сайта сто активных соединений это пик, для другого обычная работа.
Завершает настройку проверка того, что сбор метрик не влияет на сам сервер. Опрос раз в пять секунд создаёт около семнадцати тысяч запросов в сутки, и при отключённом журнале для страницы состояния эта нагрузка незаметна. Если интервал уменьшают до долей секунды, стоит включить журнал и убедиться, что место расположения состояния не попадает в общую статистику.