суббота, 16 мая 2009 г.

HAProxy vs. Pound

Некоторое время назад столкнулся с неприятным "притормаживанием" при выполнении запросов. Как выяснилось, проблема в реверс-прокси Pound - в режиме HTTPS его эффективность оказалась не слишком высокой. Трафик проекта, на котором это произошло, составляет около 200 Gb в месяц, или миллион "с гаком" запросов в сутки (весь трафик по протоколу HTTPS). Притом в режиме HTTP (по отзывам разработчиков в рассылке AOLServer) Pound выдерживает до 800 запросов в секунду (ну, правда, неизвестно, на каком железе), после чего приходится ставить другой прокси.

В качестве альтернативы выбрал HAProxy - и код понравился, и автор помог перенести конфиг Pound. Также оказалось очень полезным, что автор HAProxy написал патч к Stunnel4 для поддержки хидера X-forwarded-For (этот патч понадобилось немного адаптировать к версии stunnel4 в debian lenny, результат можно взять на сайте HAProxy или уже собранный пакет Stunnel4 в моем репозитории). Что приятно, после замены реверс-прокси в top-е его практически не видно, иногда заметен stunnel, но в общем и целом производительность связки HAProxy+Stunnel4 как минимум на порядок выше. Для поддержки условных редиректов следует обновить HAProxy, да и автор советовал, так что собрал пакет с последней версией, которая меня и устроила (брать все в том же репозитории).

12 комментариев:

citrin комментирует...

рекомендую в качестве реверсного proxy попробовать nginx

Печников Алексей комментирует...

Не пойдет. nginx - это огромный комбайн, в который понапихано разное барахло - от встроенного перла до поддержки мускуля. Лично у меня даже нет желания смотреть код nginx - чтобы разобраться, насколько его код безопасный, целая команда нужна.

Что касается nginx на продакшене... видел я сервера, которые при DDOS дохнут, ибо nginx забивает логи ошибок со скоростью много мегабайт в секунду - во-первых, создавая LA>100, во-вторых, занимая весь /var/log. В то время как нормальный реверс-прокси, в отличие от, вообще во время работы не имеет доступа к жесткому диску. Так что nginx по своей идеологии уже явно не секьюрное и не надежное решение. Его применение может быть оправдано, только если бэкендом стоит монстр вроде апач, который и статику нормально отдать не умеет, и динамику обрабатывает, потребляя жуткое количество ресурсов.

По тестам производительности nginx серьезно уступает специализированным решениям, таким, как HAProxy.

citrin комментирует...

1. mysql там нет, а perl по умолчанию не собирается. Да и кучу других модулей можно отключить через configure.
2. Уровень логгирования ошибок можно менять. Если ставить error или crit то сообщений в error_log будет мало. Если поставить info то разумеется сервак от DOS умрет, хотя при небольшой нагрузке будет работать нормально. Можно вообще error_log отключить.
3. Тесты бывают очень разные и сильно зависит от того кто и как эти тесты проводил. К тому же nginx позволяет часть функциональности вынести с бэкенда на фронтенд, что часто бывает удобно.

Печников Алексей комментирует...

1. Зачем в реверс-прокси какие-то модули? А универсальный комбайн, который включает в себя и реверс-прокси, это тот же самый apache, разве что с лучшей производительностью. Больше кода - больше ошибок, больше параметров настройки и т.п. Pound я использовал из соображений надежности и он себя оправдывал до определенной нагрузки.

2. Логирование должно выполняться стандартными средствами syslog. Доступ реверс-прокси к диску - уже потенциальная дыра в безопасности. Ориентация на виндоус-платформы, не имеющие syslog, отнюдь не аргумент - нет смысла запускать производительное серверное решение на "игрушечной" ОС.

3. Разделение на фронтенд и бэкенд для того и придумано, чтобы _разделить_ полномочия. А если часть функций бэкенда выполняет фронтэнд, то никакого выигрыша в уровне защиты, контроле доступа и эффективности не будет. Да, в качестве "костыля" к apache нередко используют nginx, но это проблемы apache. Полагаю более оправданным использовать сервер приложений, к которому не требуются "костыли".

Что касается функционала - HAProxy умеет

- балансировать запросы между серверами на основе application-defined cookie (вроде бы nginx так тоже может, но требует сборки с perl),

- управлять очередью "ожидающих" запросов (насколько мне известно, nginx просто не принимает "лишние" соединения),

- использовать различные алгоритмы балансировки,

- предоставляет веб-интерфейс для простмотра и сбора (в формате CSV) статистики,

- не обращается к жесткому диску (nginx создает временные файлы для загружаемых файлов, пишет логи и т.п.)

Тесты смотрите здесь. Замечу лишь, что уже при 30-ти одновременных сессиях HAProxy в разы превосходит nginx. Меня же интересуют как минимум сотни одновременных сессий (на бэкендах стоит AOLServer, каждый экземпляр которого способен обрабатывать сотни одновременных сессий, т.е. тысячи запросов в секунду).

citrin комментирует...

1. Принципиальная разница в модели обработки соединений (см. http://groups.google.com/group/fido7.ru.unix.prog/msg/04eac3d871ebfaf1). В nginx это FSM со всеми преимуществами и недостатками. И модули которые могут работать в рамках такой архитектуры модель эту не меняют и заметно работу не замедляют.
2. логгирование через syslog возможно только при маленькой нагрузки. Попробуйте писать 1000 строчек в секунду (для access_log если его писать будет минимум одна строчка на запрос) в файл и через syslog и в top легко увидите разницу.

Печников Алексей комментирует...

1. Вопрос в реализации, а не в модели. Чем больше кода, тем больше ошибок, поскольку, увы, человек не идеален. Учитывая, что мне дополнительная функциональность от reverse-proxy не нужна в принципе (инструмент должен быть удобен, эффективен и надежен, а не "фичаст"), смысла в использовании навороченного nginx - нет. Что касается архитектуры nginx, то, насколько мне известно, спроектирован он для BSD-систем, а для линукса неоптимален (как я понимаю, на архитектурах с kernel-threads наилучший вариант заключается в использовании пула процессов или нитей, причем каждый из них обслуживает несколько сокетов).

2. У меня access.log порядка гигабайта в сутки, "в пике" нагрузки это около полтысячи запросов в секунду. В выводе top я ни разу не видел rsyslog в верхних строчках. Диски - 2 SATA в зеркале. Да, логирование настроено асинхронное, но это как бы само собой подразумевается. Что я делаю не так?

citrin комментирует...

1. И под FreBSD и под Linux каждый userspace thread мапится в kernel thread то что в потоках принципиально разницы нет. А для обработки большого количества соединений лучше подходит FSM.

2. Как минимум при использовании syslog больше переключений контекста (при записи в файл - один syscal на запись строчки в лог, при записи через syslog - минимум три - sendto, recvfrom, write, реально их еще больше, и еще данные два раза копируются из userspace в ядро и обратно). Все это конечно недорогие операции, но не бесплатные. Не знаю как в Linux, но под FreeBSD можно заметить небольшую разницу в %idle при использовании syslog и прямой записью в файл (сам syslog при этом в верхних строчках top-а не появляется).

Печников Алексей комментирует...

1. Если не ошибаюсь, на FreeBSD используются user-space threads и эффективность ниже. Впрочем, на практике с BSD-системами я не сталкивался, так что не могу сказать, к чему это приводит "в железе".
Что касается применения конечного автомата в nginx, то это, конечно, техническое преимущество nginx перед apache, но от перегруженности функционалом не спасает. А поскольку речь идет о выборе между HAProxy и nginx, то более отлаженный код и обеспечивает более эффективную реализацию (оба они event-driven, только HAProxy запускает лишь один процесс, а nginx - несколько).

2. "Небольшая разница" в количестве тактов процессора заведомо окупается меньшей нагрузкой на дисковую подсистему (напомню, nginx временные файлы создает для пересылаемых данных), большей безопасностью и надежностью, а также удобством настройки через стандартный syslog. Причем последний может писать как в файлы, так и в БД или на удаленную машину (и не только), причем предоставляет различные виды буфферизации вывода. Думаю, не стоит и говорить, что оптимизация дисковых операций для нагруженной системы - важнейшая из задач.

citrin комментирует...

Userscape-треды были во FreeBSD 4, а сейчас уже 7.1 есть, где треды работают хорошо.

что касается нескольких процессов - они нужны для того, чтобы:
- соединения могли обрабатывать не привилигированные процессы (workers), но при этом был рутовый процесс (master) для того чтобы делать bind на порт < 1024 и создавать log-файлы.
- несколько воркеров нужно чтобы использовать несколько CPU. Просто одного процесса недостаточно, либо он должен быть тредовый (что усложняет отладку поскольку чревато разными race).

nginx пишет проксируемые данные во временные файлы только для ответов, больше заданного в конфиге размера, и их создание можно отключить (тогда бэкенду придется ждать пока клиент заберет ответ).

Печников Алексей комментирует...

Насчет тредов во FreeBSD - не знал.

В nginx многое настроить можно, но его модель безопасности - это идеология веб-сервера, а не защищенного прокси. Не говоря о том, что мало смысла заниматься настройкой лишь для отключения абсолютно всех "наворотов" эдакого комбайна, когда есть компактные решения без "лишних деталей".

А кроме ответов, есть еще и сами запросы, тело которых nginx тоже пишет в файл. Согласен, это может облегчить жизнь "нерадивому" бэкенду, но ведь костыль же. Если бэкендом стоит AOLServer, или эрланговский веб-сервер или другое полнофункциональное решение, все эти "довески" nginx можно убрать и забыть о них навсегда. В таком случае реверс-прокси - всего лишь балансировщик, не более того, а вот как раз балансировку nginx и не умеет (ну, за исключением одного-единственного алгоритма).

С другой стороны, nginx может оказаться истинным спасением для системного администратора, у которого целый зоопарк веб-проектов (возможно, на разных веб-серверах) со всяческими php-форумами и проч., сжирающими все ресурсы сервера при нескольких одновременных подключениях, и надо как-то все это хозяйство поддерживать. Сам видел, что в таких ситуациях nginx бывает незаменим. Но с точки зрения архитектуры получается полный бардак. При использовании же узкоспециализированного решения, например, Pound или HAProxy, бэкенды должны уметь качественно работать самостоятельно.

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

citrin комментирует...

Сложную логику веб-приложения (то что обычно пишут на php/perl/python/ruby/java и т. п.) очень сложно реализовать в рамках FSM, поэтому это делается в рамках процессов или тредов. Можно называть это "кривыми серверами", но я так не считаю. Ну а тред или процесс это объект гораздо более тяжелый, чем просто сокет в ядре плюс данные в процессе на эту коннекцию, поэтому 1000 тредов/процессов на 1000 клиентских коннекций это заметный оверхед. В то время как nginx может обслуживать эти 1000 коннекций несколькими процессами, а на бэкенде для генерации контента нужно будет несколько десятков тредов/процессов. AOLServer насколько знаю на каждую коннекцию держит по треду а 1000 тредов это - память в ядре расходуемая на каждый тред, частые переключения контекста при передаче выполнения от одного треда к другому...

А еще забавно, что сайт aolserver.com работает под nginx :)

Печников Алексей комментирует...

Логику приложения вовсе ни к чему реализовывать в рамках архитектуры прокси. Раз уж мы перешли к обсуждению веб-серверов, то приведу несколько строчек из конфига AOLServer:

# Tuning options
ns_param maxconnections 100 ;# Max connections to put on queue
ns_param maxthreads 100 ;# Tune this to scale your server
ns_param minthreads 10 ;# Tune this to scale your server

Т.е. обрабатывать 100 запросов в каждом треде (последовательно), одновременно выполнять от 10 до 100 тредов. Полагая среднее время обработки запроса равным 100 мс (при использовании пула подключений к PostgreSQL примерно так и выходит), при 100 тредах можно обрабатывать 1000 запросов в секунду на каждом бэкенде. И вовсе ни к чему создавать 1000 тредов, а тем более тратить ресурсы на их создание/удаление (разработчики AOLServer рекомендуют обрабатывать от 1000 и более запросов в каждом треде, при этом треды работают еще эффективнее). При использовании СУБД SQLite обработка запроса с несколькими обращениями к БД занимает всего лишь около 12 мс (на полной рабочей базе, сконвертированной из постгреса в эскулайт), а в таком случае и 20-ти тредов хватит.


(C) Alexey Pechnikov aka MBG, mobigroup.ru