Вышел nginx 1.29.6. И это как раз тот релиз, где есть не только служебная полировка по краям, но и вполне прикладная новинка: в open source-версии nginx появилась нормальная привязка клиентских сеансов к одному и тому же серверу в группе upstream. Для этого добавили директиву sticky, а в параметрах server появились route и drain.
Если смотреть на это без лишнего пафоса, новость полезная. Да, в идеальном мире все приложения stateless, сессии лежат где-нибудь в Redis, а балансировщик просто гоняет запросы туда-сюда. Но прод, как обычно, живёт своей жизнью. Где-то крутится старый Java-стек, где-то backend сам метит маршрут, где-то сессия до сих пор привязана к конкретной ноде, а где-то сервер нужно аккуратно вывести из пула, не порвав активных пользователей на две части. Вот здесь nginx 1.29.6 уже реально интересен.
Что это за релиз
Nginx 1.29.6 — это выпуск основной ветки, то есть mainline. Стабильная линейка живёт отдельно, а в mainline продолжают выкатывать новые возможности, которые потом лягут в будущую stable-ветку. На базе ветки 1.29.x позже будет сформирована стабильная 1.30. Параллельно стабильная ветка получает только исправления серьёзных ошибок и уязвимостей.
То есть это не тот случай, когда всем срочно надо бросать всё и обновляться этой же ночью. Но это именно тот релиз, который стоит внимательно посмотреть на стенде, особенно если у вас есть сценарии со sticky-сессиями, старым приложенческим state или мягким выводом backend-узлов из работы.
Главное изменение: sticky-сессии в upstream
Смысл простой. Когда клиент однажды попал на определённый backend-сервер, nginx может направлять его последующие запросы туда же. Это и есть session affinity, она же sticky session. Для блока upstream теперь доступна директива sticky с тремя режимами работы: cookie, route и learn.
И вот это уже полезно на практике. Раньше в open source nginx приходилось обходиться более костыльными схемами, городить что-то через приложение, сторонние модули или архитектурные компромиссы. Теперь базовая session affinity приехала прямо в основной инструмент.
Вариант первый: sticky cookie
Это самый прямой сценарий. 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, сам потом по ней держит клиента на нужной ноде. Для многих админок, кабинетов и внутренних систем этого уже более чем достаточно.
Вариант второй: sticky route
Режим route интереснее. Здесь nginx не придумывает свою cookie, а использует уже существующую маршрутную информацию из запроса. Например, из JSESSIONID или параметра в URI. После этого nginx сопоставляет маршрут со значением параметра 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 ведёт себя правильно: не ломает существующую схему, а подстраивается под неё. Иногда именно так и надо. Не всегда лучший подход — это прийти и за пять минут объяснить легаси, что оно живёт неправильно.
Вариант третий: 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 или старой внутренней панели, это может спасти много нервов. Потому что фраза «ну разлогиньтесь и зайдите заново» обычно звучит для бизнеса не как совет, а как мелкая диверсия.
Где эта механика реально пригодится
Во-первых, в старых приложениях, где состояние всё ещё живёт на ноде. Во-вторых, в Java-стеке с JSESSIONID и маршрутами. В-третьих, в инфраструктуре, где backend сам создаёт сессии и не хочет, чтобы его трогали. И наконец, при обслуживании и постепенном выводе серверов из пула, когда жёсткий отстрел ноды выглядит слишком грубо.
Но тут надо честно сказать одну вещь. Sticky-сессии — это не замена хорошей архитектуре. Они решают эксплуатационную задачу, а не лечат фундаментальные проблемы приложения. Если есть возможность сделать сервис stateless и вынести состояние в общее хранилище, это обычно лучше. Просто жизнь любит подкидывать не идеальные системы, а реальные. И nginx 1.29.6 как раз про работу с реальностью.
Что ещё вошло в nginx 1.29.6
Хотя sticky — главная новость релиза, на ней всё не заканчивается. В выпуск вошли и другие изменения: доработки вокруг QUIC, исправления в HTTP/2, SCGI, модуле ngx_http_mp4_module, обработке заголовка Cookie и разборе IMAP literal argument.
То есть релиз не сводится к одной директиве. Просто именно sticky, route и drain — это та часть, которую админы и инженеры действительно могут быстро примерить на свои конфиги, а не просто прочитать и закрыть вкладку.
Стоит ли обновляться
Если вы и так живёте на 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-сервис в вакууме, а живые приложения с историей, это обновление выглядит не декоративным, а вполне рабочим.
Официальные источники: релиз nginx 1.29.6 на GitHub, официальный CHANGES и документация по ngx_http_upstream_module.