1. 1
  2. 2
  3. 3
  4. 4

# теория · шаг 1 из 4

stub_status и три метрики, которые нужны

Коротко

stub_status включается одной директивой и даёт семь чисел. Два из них отвечают на вопросы, которые иначе стоят часов: расхождение accepts и handled означает, что соединения отбрасывались, а отношение requests к accepts показывает, работает ли keepalive. Считать надо и соединения к бэкендам: один проксируемый запрос занимает два слота worker_connections, и при нехватке клиент получает 500. Качество ответов снимается не отсюда, а из лога: доля 5xx, 95-й процентиль $upstream_response_time и число 499.

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

worker_connections 3. Статика отдаётся нормально, а тот же сайт через proxy_pass отвечает 500. Опечатки в конфиге нет, бэкенд жив. Что происходит?

↳  выводстенд, три прогона подряд
worker_connections 2: свой ответ=000, через прокси=000
worker_connections 3: свой ответ=200, через прокси=500
worker_connections 4: свой ответ=200, через прокси=200

На что это похоже

worker_connections привычно читают как «сколько клиентов поместится». На самом деле это число слотов на всё сразу: слушающий сокет занимает слот, клиент занимает слот, и соединение к бэкенду занимает ещё один - как будто в приёмной считают не посетителей, а стулья, и один стул всегда занят вешалкой, а переговорщик с поставщиком садится на второй.

Отсюда правило, которое экономит вечер: делить настроенное число пополам, если сайт проксирующий. И отсюда же понятно, почему nginx с worker_connections 2 вообще не стартует.

Механизм: что считает stub_status

accepts handled requests разница = соединения отброшены больше при keepalive Reading Writing Waiting Waiting - это keepalive без активности, слот они тоже занимают
Три счётчика с момента старта и три состояния прямо сейчас

Правило

/etc/nginx/conf.d/status.confhttp server
location = /nginx_status {
    stub_status;

    allow 127.0.0.1;
    allow 10.0.0.0/8;
    deny  all;

    access_log off;
}
↳  выводcurl -s http://127.0.0.1/nginx_status
Active connections: 291
server accepts handled requests
 16630948 16630948 31070465
Reading: 6 Writing: 179 Waiting: 106
Число Что означает
Active connections обслуживается прямо сейчас, включая keepalive
accepts принято соединений с момента старта
handled обработано; меньше accepts - соединения отбрасывались
requests всего запросов; больше соединений из-за keepalive
Reading читаем запрос клиента
Writing пишем ответ
Waiting keepalive-соединения без активности

Закрывать обязательно: секретов тут нет, но профиль нагрузки виден, а сама страница удобна как цель для опроса чужими руками. access_log off - чтобы опросы раз в секунду не забивали лог.

Разбор: работает ли keepalive

Вопрос «а keepalive-то у нас включён?» решается двумя замерами. Двадцать запросов одним соединением и двадцать - каждый своим:

прогон прирост accepts прирост requests
20 запросов в одном соединении 2 21
20 запросов, каждый со своим 21 21

Отношение requests / accepts в первом случае около десяти, во втором - около единицы. Единица означает, что каждый запрос платит за установку соединения, а на HTTPS ещё и за рукопожатие; на графике это видно как ровная линия, которая не должна быть ровной.

Замерить это на живом сервере - минута, а вывод серьёзный: если отношение близко к единице, ищи, кто обрывает keepalive - клиент, промежуточный прокси или твой же keepalive_timeout 0.

Разбор: сколько соединений съедает один запрос

Тот же стенд, что в крючке, только теперь с логом:

worker_connections свой ответ через прокси строк «not enough»
2 nginx не стартует - 2
3 200 500 1
4 200 200 0

Читается так. Слушающий сокет забирает слот ещё до первого клиента - поэтому при двойке nginx честно отказывается стартовать:

↳  выводnginx -c ... с worker_connections 1
nginx: [emerg] 1 worker_connections are not enough for 1 listening sockets

При тройке хватает на сокет и клиента, но не на соединение к бэкенду - и клиент получает 500, а не 502 и не 503. Это важное различие: 500 в такой ситуации означает не ошибку приложения, а нехватку ресурсов у самого nginx. В логе рядом лежит объяснение, и уровень у него alert, а не warn:

↳  вывод/var/log/nginx/error.log
[alert] 10#10: *10 4 worker_connections are not enough while connecting to upstream,
  client: 127.0.0.1, server: , request: "GET /apislow/x HTTP/1.1"

Хвост строки называет и место: слот кончился при подключении к бэкенду, а не при приёме клиента.

Что ломается без этого

Наплыв, которого не видно в кодах ответа. Стенд с worker_connections 4 и тридцатью параллельными запросами: accepts 38, handled 31. Семь соединений были приняты ядром и отброшены nginx - клиент увидел обрыв, а в access_log для них нет ни строки, потому что запроса не случилось. Единственные следы - расхождение двух чисел в stub_status и [alert] в error_log. Если снимать только «долю 5xx по логу», такой инцидент не виден вовсе.

Телеметрия, которую никто не проверял. Метрика, собираемая не с той машины или не в том формате, выглядит как здоровый график. Проверяется нарочной поломкой:

$  команда
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1/этого-точно-нет

В графике должен появиться 404. Не появился - разбираться сейчас, а не в три часа ночи. Так же проверяются и алерты: подними порог на минуту и убедись, что письмо дошло. Ненастроенный алерт хуже отсутствующего - он создаёт ложное чувство безопасности.

Зачем это в работе

Три показателя, которых stub_status не даёт и которые снимаются из лога: доля 5xx ловит поломки, 95-й процентиль $upstream_response_time ловит деградацию до того, как она станет поломкой, число 499 ловит терпение пользователей - оно кончается раньше любых твоих таймаутов.

/etc/nginx/nginx.confhttp
log_format metrics '$time_iso8601 $status $request_time $upstream_response_time '
                   '$upstream_cache_status $upstream_addr';

Разбирать этот формат можно чем угодно - от awk до экспортера в Prometheus. Важно не чем, а что рядом с ним лежит опрос stub_status: коды ответа и счётчики соединений отвечают на разные вопросы, и инцидент, который не видно в одном, обычно виден в другом. В открытой версии активных проверок здоровья бэкендов нет, их роль играют пассивные max_fails и fail_timeout из восьмого раздела.

Теперь сам

В мониторинге ровная линия: requests растёт примерно так же, как accepts, их отношение около единицы. Жалоб нет, ошибок нет. Стоит ли что-то делать?

Ответ: стоит. Отношение около единицы означает, что keepalive не работает и каждый запрос платит за установку TCP-соединения, а на HTTPS - ещё и за рукопожатие. Это не поломка, а постоянный налог на скорость и на процессор. Смотреть надо keepalive_timeout у себя, заголовок Connection от клиентов и промежуточные прокси; если сайт проксирующий, заодно проверить keepalive в блоке upstream - к бэкендам соединения тоже могут открываться на каждый запрос.

Главное

stub_status - одна директива и семь чисел. Расхождение accepts и handled означает отброшенные соединения (замер: 38 против 31 при нехватке слотов), и в access_log их не видно вовсе - только [alert] worker_connections are not enough. Отношение requests к accepts показывает, работает ли keepalive: двадцать запросов одним соединением дали прирост accepts на 2, двадцатью соединениями - на 21. В worker_connections входит и слушающий сокет, и соединение к бэкенду: при значении 3 статика отдаётся, а проксируемый запрос даёт 500. Закрывать статус обязательно (allow/deny плюс access_log off), качество ответов снимать из лога (5xx, процентиль $upstream_response_time, 499), а телеметрию проверять нарочной поломкой.

Комментарии

Пока нет комментариев. Будь первым!

Оставить комментарий

Комментарий появится после проверки. Email не публикуется. Войти, чтобы не вводить имя каждый раз.