12. Отладка и проблемы производительности
При запуске HAProxy с параметром «-d» процесс остаётся на переднем плане и выводит по строке на каждое событие — входящее соединение, завершение соединения, каждую встреченную строку заголовка запроса или ответа. Этот отладочный вывод формируется до обработки содержимого, поэтому локальные изменения в нём не учитываются. Основное назначение — показывать запросы и ответы без запуска анализатора сетевого трафика. При одновременной обработке нескольких соединений вывод читается хуже, но скрипты «debug2ansi» и «debug2html» из каталога examples/ заметно помогают, раскрашивая его.
Если HAProxy отклоняет запрос или ответ HTTP/1.x как некорректный, лучше всего подключиться к CLI и выполнить «show errors». Команда показывает последние сохранённые ошибочные запрос и ответ HTTP/1.x для каждого фронтенда и бэкенда, включая все сведения для точного определения первого отклонённого символа входного потока. Иногда это необходимо, чтобы доказать заказчикам или разработчикам наличие ошибки в их коде. В таком случае часто можно ослабить проверки, сохранив захват ошибок, с помощью «option accept-unsafe-violations-in-http-request» или аналогичного параметра для ответов сервера «option accept-unsafe-violations-in-http-response». Подробнее см. руководство по конфигурации.
Пример:
Вывод команды «show info» в CLI содержит полезные сведения о максимальной достигнутой интенсивности соединений, максимальной достигнутой скорости вычисления ключей SSL и в целом информацию, помогающую объяснить временные проблемы с использованием CPU или памяти. Пример:
Если в новой версии HAProxy проблема возникает как будто случайно (например, прерывается каждый второй запрос или изредка происходит аварийное завершение), стоит попробовать включить заполнение памяти контрольным значением: тогда сразу после каждого вызова malloc() выделенная область заполняется заданным байтом. По умолчанию используется байт 0x50 (ASCII-код ‘P’), но можно выбрать любой другой, в том числе нулевой (это даёт тот же эффект, что calloc(), и может скрыть проблему). Заполнение памяти включается параметром командной строки «-dM». Оно немного снижает производительность и не рекомендуется для рабочей среды. Если с ним проблема возникает всегда либо никогда не возникает при заполнении нулями, это явно указывает на ошибку, о которой обязательно следует сообщить. Если заметных изменений нет, проблема с этим не связана.
При отладке задержек важно использовать и strace, и tcpdump на локальной машине, а также ещё один tcpdump на удалённой системе. Задержки возникают на всех этапах обработки, и нужно установить, какой именно этап их вызывает, чтобы понять, где вмешаться. На практике локальный tcpdump показывает время поступления входящих данных. Strace показывает, когда haproxy получает эти данные через recv/recvfrom. Внимание: OpenSSL использует системные вызовы read()/write() вместо recv()/send(). Strace также показывает, когда haproxy отправляет данные, а tcpdump — когда система передаёт их интерфейсу. Затем внешний tcpdump показывает, когда отправленные данные действительно получены (локальный показывает только постановку пакетов в очередь). Преимущество захвата трафика в локальной системе состоит в том, что strace и tcpdump используют одни и те же часы. Strace следует запускать с «-tts200», чтобы получать полные временные метки и достаточно большие для чтения фрагменты данных. Tcpdump следует запускать с «-nvvttSs0», чтобы видеть полные пакеты, реальные номера последовательности и полные временные метки.
На практике поступившие данные почти всегда сразу получает haproxy, если только CPU машины не перегружен либо данные некорректны и потому не доставляются. Если данные получены, но не отправлены, причина обычно в заполнении выходного буфера: получатель недостаточно быстро читает данные. Это можно подтвердить, заметив, что механизм опроса некоторое время не сообщает о возможности записи в выходной файловый дескриптор. Часто проще найти в выводе strace момент, когда данные наконец отправлены, а затем просмотреть более ранние события и установить, когда поступило уведомление о готовности к записи. Обычно оно совпадает с получением ACK от адресата, видимым в tcpdump. После отправки данные могут некоторое время бездействовать внутри системы. И здесь окно перегрузки TCP может быть ограничено и не позволять отправить данные до получения ACK, открывающего окно. Если трафик отсутствует, а отправка данных задерживается на 40 ms или 200 ms, причина иная и фактически не является неисправностью: алгоритм Нейгла не позволяет немедленно отправлять пустые пакеты в надежде объединить их с последующими данными. HAProxy автоматически отключает алгоритм Нейгла в чистом режиме TCP и в туннелях. Однако при пересылке тела HTTP он остаётся включённым, повышая производительность за счёт уменьшения числа пакетов. Некоторые приложения, не соответствующие HTTP, могут быть чувствительны к задержке доставки неполных ответов HTTP. В таком случае нужно включить «option http-no-delay», чтобы отключить алгоритм Нейгла и обойти особенности их реализации. При этом следует помнить, что любой другой прокси в цепочке может столкнуться с тем же эффектом. Если tcpdump показывает немедленную отправку данных, но другая сторона получает их с задержкой, причиной может быть перегруженный канал WAN, перегруженная локальная сеть с включённым управлением потоком, препятствующим отправке, либо, что встречается чаще, работа HAProxy в виртуальной машине, гипервизор которой по какой-то причине решил отложить отправку данных. В виртуализированных средах задержки почти всегда вызваны слоем виртуализации, поэтому для экономии времени стоит сначала сравнить tcpdump внутри виртуальной машины и на внешних компонентах. Любую разницу следует относить к гипервизору и сопутствующим драйверам.
Если в трассировке tcpdump с параметром -vv видны сегменты TCP SACK, это всегда означает, что отправившая их сторона получила подтверждение потери пакета. Отсутствие SACK не доказывает отсутствие потерь, но их наличие определённо указывает на потери в сети. Потери в сети нормальны, однако их частота должна быть такой, чтобы SACK не бросались в глаза. Если они часто встречаются в трассировке, стоит точно выяснить, что происходит и где теряются пакеты. HTTP плохо переносит потери TCP, поскольку они вызывают огромные задержки.
Команда «netstat -i» выводит статистику по интерфейсам. Рост счётчика Rx-Ovr означает, что системе не хватает ресурсов для приёма всех входящих пакетов и они теряются ещё до обработки сетевым драйвером. Rx-Drp показывает потери уже полученных пакетов в сетевом стеке из-за того, что приложение недостаточно быстро их обрабатывает. Такое возможно и при некоторых атаках. Tx-Drp означает, что выходные очереди заполнены и пакеты пришлось отбросить. При использовании TCP это должно происходить очень редко, но может указывать на перегрузку исходящего канала.