Модуль ustats: статистика запросов к бэкендам

    Приветствую!

    В этой статье речь пойдет о новом модуле для nginx'а, цель которого — сбор и предоставление пользователю статистики обращений сервера к бэкендам. Под катом — подробности, примеры использования, скриншоты, ссылки, а также история создания.


    История


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

    При помощи JMeter'а мы погоняли на стенде nginx, HAProxy, Brocade Server Iron ADX 1000 и ряд других балансировщиков. Основным критерием отбора была возможность терминации порядка 50 тысяч одновременных ssl-сессий в пиковые периоды. После длительного тестирования по разным причинам отпали все варианты кроме nginx'а и его железного конкурента Brocade Server, а в итоге остался лишь первый из них. При прочих равных, наверное решающими факторами в пользу nginx'а стали гибкость его настройки и легковесность.

    Проблема


    Ранее в качестве балансировщика на некоторых фронтах у нас использовался HAProxy. После перехода на nginx стало ясно, что нам не хватает в нем сколько-нибудь информативной статистики работы с бэкендами. Дело в том, что у того же HAProxy была такая статистика, и с ее помощью мы отслеживали возникающие на бэкендах проблемы и оперативно на них реагировали. С новым балансировщиком мы оказались без этой статистики, как без рук. stub_status и подобные ему модули нам не подошли, т.к. их функция — показывать статистику не в разрезе отдельного апстрима, а сервера в целом. Нам же хотелось по каждому апстриму/бэкенду иметь данные по таким параметрам как количество обращений к каждому бэкенду и число ошибок HTTP 499/500/503 и TCP, причем позднее этот список расширился.

    Решение


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

    Какая статистика?

    С помощью ustats можно вести статистику по таким показателям бэкендов как
    • Количество запросов.
    • Число ошибок 499/500/503.
    • Количество HTTP таймаутов чтения и записи.
    • Число ошибок TCP соединений.
    • Таймер отказа (fail_timeout). В nginx'е этот параметр настраивается одноименной директивой и определяет период времени, в течение которого должно произойти подряд несколько неудачных обращений к бэкенду (точное количество указывается директивой max_fails), после которых бэкенд помещается в черный список, и обращений к нему не происходит еще в течение времени fail_timeout. Обычно администратор и сам в курсе, какие таймауты прописаны у него в конфиге сервера, но все же иметь их перед глазами показалось нам неплохой идеей.
    • Число неудачных попыток работы с бэкендом (fails count). Внутри nginx'а для каждого бэкенда ведется счетчик неудачных попыток работы. Это число показывает, сколько раз в течение времени fail timeout nginx попытался постучаться на бэкенд и потерпел неудачу (что считать неудачей, см. описание директивы proxy_pass). Принцип работы счетчика достаточно прост. Когда nginx собирается перенаправить запрос, он сначала смотрит, какой бэкенд следующий на очереди (если речь о балансировке round robin), проверяет его статус (в черном списке или нет), и если бэкенд «в игноре», сервер смотрит на время его последнего отказа. Если с того момента уже прошло время fail_timeout, то счетчик неудачных попыток для бэкенда сбрасывается, и запрос отправляется. Если же бэкенд не был в черном списке, то запрос направляется сразу, и счетчик может сброситься в зависимости от времени, которое прошло с момента первого неудачного обращения.
    • Максимальное количество неудачных обращений (max_fails). Определяет порог количества неудачных попыток работы с бэкендом, при достижении которого тот помещается в черный список на период времени fail_timeout. Этот параметр тоже прописывается в конфиге nginx'а, и его отображение в статистику мы добавили для наглядности.
    • Время последнего неудачного обращения к бэкенду. Его назначение должно быть понятно из предыдущих пунктов :)

    Дополнительные функции

    Еще ustats умеет показывать, какие бэкенды в данный момент находятся в черном списке. Отмечу, что под бэкендом понимается не то, что указывается в конфиге nginx'а директивой server, а непосредственно адрес, в который ресолвится указанное в директиве имя. Если за одним именем в DNS значится несколько адресов, то модуль показывает их как отдельные бэкенды (не забывая при этом указывать, из какого имени они получились).

    Вдобавок к подсветке бэкендов из черного списка, ustats подсвечивает выключенные сервера, т.е. описанные в конфиге nginx'а как
    ...
    server some.server.name down;
    ...
    

    И наконец, с помощью модуля можно включать и выключать сервера из топологии nginx'а прямо во время его работы, через веб-интерфейс. Изменения не сохраняются в конфиге и призваны облегчить проведение тех. работ, предполагающих временное отключение бэкендов. Хочу предостеречь: никакой защиты от несанкционированного выполнения данного действия ustats не предоставляет, поэтому придется самостоятельно следить за тем, чтобы случайный человек не исключил из работы половину бэкендов на вашей площадке :)

    Сценарии использования

    Их два. Во-первых, модуль предоставляет всю статистику в виде веб-странички с таблицей, показ которой можно повесить на любую локацию, наподобие stub_status:
    location /ustats {
    	ustats on;
    	...
    }
    

    Страничка автоматически обновляется, а интервал обновления (в миллисекундах) можно настроить в конфиге:
    ...
    ustats_refresh_interval 7000
    ...
    

    Второй сценарий предполагает использование модуля в качестве источника данных для других утилит мониторинга. В этом случае к нему делаются соответствующие запросы, в ответ на которые он возвращает обыкновенный xml с нужной информацией. Запросить можно данные по одному апстриму или по одному бэкенду. Например, запрос

    /ustats?u=offline
    

    вернет данные по всем бэкендам в апстриме offline, а по запросу

    /ustats?u=break&b=the_mold
    

    вернутся данные по бэкенду the_mold в апстриме break. Если апстрим или бэкенд не найден, в ответном xml об этом будет сказано.

    Немного картинок


    Чтобы не быть голословным, приведу несколько скриншотов странички-результата работы модуля. В nginx'е с первого снимка настроено 2 апстрима, в которых все сервера — локальные, поднятые на том же самом nginx'е, за исключением сервера www — он ресолвится в адреса Яндекса:

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

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

    Вкупе с тем, что еще три бэкенда были выключены, всю нагрузку принимал на себя единственный оставшийся.

    Наконец, последний снимок сделан на рабочем фронтэнде с нашей площадки:

    На картинке видно еще одну особенность, о которой я пока не упоминал. Апстримы снизу подсвечены чуть светлее — это признак того, что они определены в конфиге неявно, т.е. не так
    upstream give_me_a_name {
      ...
    }
    

    а так
    location /whereami {
    ...
    proxy_pass http://192.168.0.75:8080
    ...
    }
    


    Итог


    Мы выложили исходники модуля на страничке Google Code. В репозитории лежит файл патча для nginx'а, файл с исходником + конфигурационный файл. На страничке модуля есть инструкции по установке, там же описаны дополнительные директивы настройки. Текущая версия модуля была протестирована с nginx'ом версии 0.8.53 в браузерах Chrome, Firefox и Opera. Напоследок надо сказать, что ustats — лишь попытка добавить в nginx самый базовый механизм отображения данных по работе с бэкендами. В будущем хотелось бы видеть в основной ветке сервера такие небесполезные модули, как например, опережающий health check бэкендов.
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 20

      +2
      >Если точнее, надо было решать проблемы с распределением возросшей нагрузки — наши фронты стали с трудом справляться со своей задачей.
      >Ранее в качестве балансировщика на некоторых фронтах у нас использовался HAProxy.

      На самом деле мы использовали собственный балансировщик — это было обусловлено необходимостью двухстороннего шифрования канала по ГОСТ. Когда такая необходимость отпала мы стали думать о более вменяемом балансировщике, HAProxy отпал по причине неумения терминировать ssl, а Stunnel не давал нужной производительности.
        +2
        Молодцы, что не «зажали» разработку и на google code выложили :)
          +2
          Особого смысла скрывать разработку нет:) Так она, возможно, заживет своей жизнью независимо от нас
          0
          Nginx и ГОСТ вполне дружат, при желании.
          +2
          Я сначала прочитал название как ustas, по моему больше подходит :)
            +3
            То что надо, спасибо!
            Еще бы плагин к munin
              +5
              Отпостили в рассылке nginx'а. Вопрос в том, решится ли Сысоев принять модуль в основную ветку
                0
                нет, не решиться. Максим тебе ответил почему твой модуль плох.
                  0
                  99.9%, что нет — он нужен крайне редко и показывает статистику только для одного воркера.
                    +3
                    Претензии из рассылки я понял. Модуль мы продолжим развивать, поскольку нам он удобен, и по крайней мере позволяет отслеживать количественные соотношения между бэкендами по запросам и ошибкам. Если по-вашему юзкейсы для модуля возникают не у многих (о чем я бы не стал рассуждать столь категорично), значит развивать придется без поддержки сообщества.
                  +2
                  зачем имплементировать эти счетчики модулем, если всю ту же информацию можно получить через логи nginx-а?
                    +3
                    Дак тут не только счётчики, ещё же видно какой сервер попал в грей лист, т.е. это своего рода мониторинг апстрима. Это можно увидеть в логах?
                    Если да, то может и проще, но не так интересно.

                    P.S. Логи мы тоже считаем, но в несколько другом разрезе.
                      +2
                      Да, в логах это увидеть можно, т.к. запросов на сбитый апстрим в текущий момент (или в последнюю минуту) нет. Логичнее было бы сделать подсветку — чем ярче цвет, тем больше запросов идет на апстрим, если цвет серый их или нет или очень мало.
                      В мониторинге можно поставить алерт на величину производной счетчика (алерт срабатывает если минуту назад было например 100 запросов, а в последнюю 5, а не +-10. я так обычно делаю)
                      +3
                      Вы правы, данные можно выдирать и из логов. Повесить еще один процесс, занимающийся парсингом логов (которые у нас пишутся с дикой скоростью), приделать к нему веб-интерфейс, не забыть про ротацию и архивирование. Не будет веб-интерфейса для выключения бэкендов. Так ли уж это менее трудозатратно?
                        0
                        Дело в том, что код nginx довольно стабилен сам по себе, и когда вы подключаете к нему свой код в рамках основного — это может как-то влиять на основною функциональность. Мало ли где вы воткнете синхронный вызов например, и упадет производительность, или будет ошибка по управлению памяти. А узнать о том что работает что-то не так можно только пересобрав весь сервер и сравнив, т.е. проблему может быть сложно даже увидеть.

                        В то же время снаружи код в виде анализатора логов будет выглядеть практически точно так же. Можно же сделать его даже демоном — и получать данные онлайн.
                      +1
                      Модуль весьма полезен для решения наших задач.
                      Избавляет от дополнительных клиентов-парсеров.
                        0
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c: In function 'ngx_http_ustats_create_response_full_html':
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:646: error: 'struct ' has no member named 'server'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:706: error: 'struct ' has no member named 'server'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:706: error: 'struct ' has no member named 'server'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:710: error: 'struct ' has no member named 'server'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:712: error: 'struct ' has no member named 'server'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:717: error: 'struct ' has no member named 'server'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:787: error: 'struct ' has no member named 'server'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:819: error: 'struct ' has no member named 'server'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:819: error: 'struct ' has no member named 'server'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:825: error: 'struct ' has no member named 'server'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:827: error: 'struct ' has no member named 'server'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:830: error: 'struct ' has no member named 'server'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:837: error: 'struct ' has no member named 'num_reqs'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:842: error: 'struct ' has no member named 'num_http_499'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:847: error: 'struct ' has no member named 'num_http_500'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:852: error: 'struct ' has no member named 'num_http_503'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:857: error: 'struct ' has no member named 'num_tcp_error'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:862: error: 'struct ' has no member named 'num_http_read_timeout'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:867: error: 'struct ' has no member named 'num_http_write_timeout'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c: In function 'ngx_http_ustats_toggle':
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:956: error: 'struct ' has no member named 'server'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:966: error: 'struct ' has no member named 'server'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:981: error: 'struct ' has no member named 'server'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:994: error: 'struct ' has no member named 'server'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c: In function 'ngx_http_ustats_create_response_upstream_xml':
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:1066: error: 'struct ' has no member named 'server'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:1070: error: 'struct ' has no member named 'server'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:1073: error: 'struct ' has no member named 'server'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:1077: error: 'struct ' has no member named 'server'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:1094: error: 'struct ' has no member named 'server'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:1098: error: 'struct ' has no member named 'server'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:1105: error: 'struct ' has no member named 'server'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:1111: error: 'struct ' has no member named 'server'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:1119: error: 'struct ' has no member named 'num_reqs'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:1120: error: 'struct ' has no member named 'num_http_499'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:1121: error: 'struct ' has no member named 'num_http_500'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:1122: error: 'struct ' has no member named 'num_http_503'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:1123: error: 'struct ' has no member named 'num_tcp_error'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:1124: error: 'struct ' has no member named 'num_http_read_timeout'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:1125: error: 'struct ' has no member named 'num_http_write_timeout'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c: In function 'ngx_http_ustats_create_response_backend_xml':
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:1176: error: 'ngx_http_upstream_server_t' has no member named 'name'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:1220: error: 'struct ' has no member named 'server'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:1249: error: 'ngx_http_upstream_server_t' has no member named 'name'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:1254: error: 'struct ' has no member named 'server'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:1271: error: 'struct ' has no member named 'num_reqs'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:1272: error: 'struct ' has no member named 'num_http_499'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:1273: error: 'struct ' has no member named 'num_http_500'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:1274: error: 'struct ' has no member named 'num_http_503'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:1275: error: 'struct ' has no member named 'num_tcp_error'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:1276: error: 'struct ' has no member named 'num_http_read_timeout'
                        /usr/ports/www/nginx/ustats/ngx_http_ustats_module.c:1277: error: 'struct ' has no member named 'num_http_write_timeout'
                        *** Error code 1

                        Stop in /usr/ports/www/nginx/work/nginx-0.8.53.
                          0
                          сначала пач накати
                          0
                          собираю под Linux

                          Linux ***** 2.6.32-27-generic-pae #49-Ubuntu SMP Thu Dec 2 00:07:52 UTC 2010 i686 GNU/Linux

                          выкачал последнюю версию.

                          /nginx-0.8.53
                          ./configure \
                          --with-http_stub_status_module \
                          --with-http_realip_module --prefix=/usr/local/nginx \
                          --add-module=src/http/modules/ustats

                          1. если не сделать в конфигурации

                          location /ustats {
                          ustats on;
                          }

                          в логах имеем:

                          2011/02/09 14:15:35 [notice] 28816#0: signal 17 (SIGCHLD) received
                          2011/02/09 14:15:35 [alert] 28816#0: worker process 28854 exited on signal 11

                          2. добавлялем этот локейшен

                          все заводится, но если запросить /ustats

                          так же валится с 11 сигналом

                            0
                            /usr/local/nginx/sbin/nginx -V
                            nginx version: nginx/0.8.53
                            built by gcc 4.4.3 (Ubuntu 4.4.3-4ubuntu5)
                            configure arguments: --with-http_stub_status_module --with-http_realip_module --prefix=/usr/local/nginx --add-module=src/http/modules/ustats

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

                          Самое читаемое