Nginx 1.29.6: sticky-сессии в upstream, route, drain и ещё немного полезного

Вышел nginx 1.29.6. Релиз mainline, и в нём есть прикладная новинка: в open source-версии появилась нормальная привязка клиентских сеансов к одному backend в группе upstream. Добавили директиву sticky, а у server появились параметры route и drain.

В идеальном мире все приложения stateless, сессии лежат в Redis, а балансировщик просто гоняет запросы. В проде иначе: старый Java-стек, backend сам метит маршрут, сессия привязана к ноде, или узел нужно вывести из пула, не порвав активных пользователей. Вот здесь 1.29.6 уже интересен.

Что это за релиз

Nginx 1.29.6 относится к mainline. Стабильная линейка живёт отдельно, в mainline выкатывают новые возможности, которые потом попадут в future stable. На базе 1.29.x позже сформируют стабильную 1.30. Stable по-прежнему получает только серьёзные исправления и закрытие уязвимостей.

Срочно обновляться этой ночью не нужно. Но на стенде релиз стоит прогнать, если у вас sticky-сессии, state на ноде или мягкий вывод backend из работы.

Главное: sticky-сессии в upstream

Смысл простой. Клиент один раз попал на backend, и nginx направляет его следующие запросы туда же. Это session affinity, она же sticky session. Для блока upstream доступна директива sticky с тремя режимами: cookie, route и learn.

Раньше в open source nginx приходилось городить костыли через приложение, сторонние модули или архитектурные компромиссы. Теперь базовая session affinity есть в основном инструменте.

Самый прямой сценарий. Nginx сам выставляет cookie с информацией о выбранном backend. Первый запрос балансируется как обычно, дальше клиент ходит на ту же ноду.

upstream backend {
    server backend1.example.com route=a;
    server backend2.example.com route=b;

    sticky cookie srv_id expires=1h domain=.example.com path=/;
}

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

Вариант 2: sticky route

Режим route интереснее. Nginx не придумывает свою cookie, а берёт уже существующую маршрутную информацию из запроса: например, из JSESSIONID или параметра в URI. Потом сопоставляет маршрут со значением route у серверов в upstream и отправляет запрос на нужную ноду.

map $cookie_jsessionid $route_cookie {
    ~.+\.(?P<route>\w+)$ $route;
}

map $request_uri $route_uri {
    ~jsessionid=.+\.(?P<route>\w+)$ $route;
}

upstream backend {
    server backend1.example.com route=a;
    server backend2.example.com route=b;

    sticky route $route_cookie $route_uri;
}

Хороший вариант для старых Java-приложений и систем со своей логикой маршрутизации. Nginx не ломает схему, а подстраивается под неё. Не всегда лучший подход – прийти и за пять минут объяснить легаси, что оно живёт неправильно.

Вариант 3: sticky learn

В режиме learn nginx смотрит ответы upstream и запоминает сессии, которые создало само приложение. Backend при первом ответе выставляет cookie, nginx потом помнит, на какой узел сажать этого клиента.

upstream backend {
    server backend1.example.com:8080;
    server backend2.example.com:8081;

    sticky learn
           create=$upstream_cookie_examplecookie
           lookup=$cookie_examplecookie
           zone=client_sessions:1m;
}

Удобно там, где приложение давно живёт своей жизнью и переделывать его ради красивой архитектуры никто не будет. Иногда лучшая автоматизация – не мешать тому, что уже работает.

Зачем route и drain

С route всё просто: метка сервера, по которой nginx понимает, к какому backend привязывать запрос. drain полезнее в эксплуатации.

upstream backend {
    server backend1.example.com route=a;
    server backend2.example.com route=b drain;

    sticky cookie srv_id expires=1h domain=.example.com path=/;
}

Параметр drain мягко выводит сервер из пула. Новые непривязанные запросы туда не идут, а уже связанные через sticky продолжают ходить. Полезно при обслуживании, rolling update или аккуратной миграции, когда не хочется рубить активные сессии.

В кабинетах, CRM и внутренних панелях, где пользователи сидят по часу, это экономит нервы. Фраза «разлогиньтесь и зайдите заново» для бизнеса обычно звучит не как совет, а как мелкая диверсия.

Где это реально нужно

Старые приложения, где state ещё на ноде. Java-стек с JSESSIONID и маршрутами. Инфраструктура, где backend сам создаёт сессии и его лучше не трогать. Обслуживание и постепенный вывод серверов, когда жёсткий отстрел ноды слишком груб.

При этом sticky – не замена нормальной архитектуре. Это эксплуатационный инструмент, а не лечение фундаментальных проблем. Если можно сделать сервис stateless и вынести состояние в общее хранилище, обычно так и стоит. Но жизнь чаще подкидывает не идеальные системы, а реальные. Nginx 1.29.6 как раз про работу с реальностью.

Что ещё вошло в 1.29.6

Sticky – главная новость, но не единственная. В выпуске есть доработки вокруг QUIC, исправления в HTTP/2, SCGI, модуле ngx_http_mp4_module, обработке заголовка Cookie и разборе IMAP literal argument.

Релиз не сводится к одной директиве. Просто sticky, route и drain – то, что можно быстро примерить к своим конфигам, а не только прочитать в changelog.

Стоит ли обновляться

Если вы уже на mainline и ждали нормальную session affinity в open source nginx, на 1.29.6 стоит посмотреть. По классике: стенд, тесты, проверка балансировки, cookie, route и вывода узлов через drain, и только потом прод.

Если production спокойно живёт на stable и sticky прямо сейчас не нужны, срываться необязательно. Релиз хороший, но nginx любит аккуратные обновления, а не «накатим ночью и посмотрим».

Итог

Nginx 1.29.6 получился прикладным. Главное – sticky в upstream с тремя режимами, плюс route и полезный drain. Для тех, кто балансирует не сферический stateless-сервис, а живые приложения с историей, обновление рабочее, а не декоративное.

Источники и ссылки


Комментарии загружаются…