ADG Оставить заявку
Блог Инфраструктура 2 мин чтения

Балансировка нагрузки с nginx: ставим перед двумя Apache и смотрим что получается

Корпоративный портал клиента падал под конец месяца, когда все лезли за отчётами. Поставили nginx 0.5.x перед двумя Apache-бэкендами - round-robin и keep-alive сняли большую часть нагрузки.

Контекст момента

Nginx 0.5.x активно развивается в 2006 году, приобретает популярность в рунете как reverse proxy и балансировщик нагрузки

Раз в месяц, примерно 28-30 числа, корпоративный портал клиента превращался в тыкву. Финансовый отдел, логистика и руководство одновременно заходили за отчётами - и Apache на единственном сервере начинал задыхаться. Время ответа улетало в десятки секунд, часть запросов просто обрывалась. До конца месяца обычно как-то доживали, но с каждым разом становилось хуже: бизнес рос, отчётов прибавлялось.

Первая идея клиента была простая: купить сервер помощнее. Мы прикинули нагрузку и поняли, что дело не в железе - дело в архитектуре. Apache держал по одному процессу на соединение, а соединений в пиковый момент было немало, и каждое удерживалось браузером долго. Второй сервер с таким же Apache просто удвоил бы проблему.

Вместо этого мы предложили поставить nginx как фронтенд перед двумя существующими Apache-бэкендами.

Почему nginx, а не что-то привычное

Nginx в рунете сейчас активно обсуждается - Игорь Сысоев из Рамблера развивает его уже несколько лет, и репутация у проекта складывается. Ключевое отличие от Apache: nginx не держит поток на каждое соединение, а работает на событийной модели. Это означает, что тысячи «спящих» keep-alive соединений с браузерами не стоят ему почти ничего.

Версия 0.5.x - не финальный релиз, changelog обновляется активно, но на reverse proxy и статику она уже вполне годится. Мы это проверили сначала на тестовом стенде, прежде чем идти в прод клиента.

Схема, которую поставили

Браузеры (много)
      |
   [nginx 0.5.x]   <- фронтенд, принимает все соединения
      |
  upstream backend {
    server app01:80;   <- Apache 1
    server app02:80;   <- Apache 2
  }

Оба Apache-бэкенда - это один и тот же портал, развёрнутый на двух серверах, которые у клиента уже были (второй стоял как резервный и практически не использовался). Никаких новых машин покупать не пришлось.

Конфиг nginx свёлся к нескольким ключевым блокам.

upstream с round-robin. По умолчанию nginx распределяет запросы между бэкендами по очереди. Управление соединениями к бэкендам в версии 0.5.x базовое: каждый запрос проксируется через отдельное TCP-соединение к Apache. Это означает, что Apache по-прежнему платит за handshake на каждый запрос - но зато медленные браузерные соединения уже не удерживают его процессы.

Буферизация ответов. Nginx забирает ответ от Apache целиком, а потом отдаёт браузеру в своём темпе. Медленный клиент перестаёт занимать Apache-процесс - тот отдал ответ в буфер nginx и свободен. Это, пожалуй, главный практический эффект в нашем случае: у клиента много пользователей на медленных каналах внутри сети.

Статика на nginx. CSS, картинки, JS-файлы портала мы вынесли напрямую на nginx, убрав их из маршрута к Apache вообще. Небольшой, но чистый выигрыш.

Таймауты. Выставили proxy_read_timeout с запасом - бэкенды иногда генерируют тяжёлые отчёты по несколько секунд. Без этого nginx начинал возвращать 504 раньше, чем Apache успевал ответить.

Что получилось

Нагрузку в конце месяца мы специально не ждали - прогнали искусственный тест с несколькими десятками параллельных соединений. Разница ощутимая. Apache-процессы перестали работать в режиме «принять соединение - ждать пока клиент дочитает - закрыть». Они стали быстро отдавать контент в буфер nginx и освобождаться. Утилизация памяти на бэкендах заметно снизилась - меньше одновременно живых apache2-процессов при том же числе браузерных сессий.

Примерную оценку в 70% снижения нагрузки на бэкенды мы получили сравнивая пиковое число процессов Apache до и после при одинаковом синтетическом потоке запросов. Не научная методология, но порядок цифр понятен.

Конец месяца ещё не наступил - посмотрим как поведёт себя в реальный пиковый день. Возможно, обнаружатся нюансы, которые синтетика не показала.

Что стоит иметь в виду

Несколько моментов, которые мы выяснили по ходу.

Сессии и балансировка. Портал клиента хранит состояние сессии на стороне сервера, в файлах. При round-robin один пользователь может попасть на разные бэкенды в разных запросах - и потерять сессию. Решили через sticky sessions на уровне cookie: nginx направляет пользователя всегда на один и тот же бэкенд после первого запроса. Это не встроено в nginx 0.5.x напрямую - пришлось использовать IP hash (ip_hash директиву). Не идеально для случая, когда несколько пользователей за одним NAT-ом, но для внутренней сети клиента - приемлемо.

Логи. Apache теперь видит в качестве клиента адрес nginx, а не браузера. Добавили X-Forwarded-For в nginx-конфиге и научили Apache его читать через mod_rpaf. Без этого аналитика по IP теряет смысл.

Мониторинг бэкендов. Если один Apache упадёт - nginx по умолчанию продолжит слать на него запросы и получать ошибки. Выставили max_fails и fail_timeout в upstream-блоке: после нескольких неудачных попыток бэкенд временно исключается из ротации. Простая, но рабочая защита.

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

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.