NGINX Unit: где и зачем использовать
Если коротко: NGINX Unit — это не «ещё один nginx». Это рантайм и маршрутизатор в одном процессе. Он запускает приложения на разных языках, рулит HTTP/HTTPS-потоком и меняет конфигурацию на лету. Звучит смело? Да. И именно поэтому важно понять, в каких сценариях Unit даёт ощутимую пользу, а где лучше остаться на классическом nginx.
Что это и как у него устроено управление
Unit — демон с REST‑подобным JSON‑API для конфигурации через UNIX‑сокет или HTTP. Меняете JSON — и изменения применяются без перезапуска: Unit тут же пересобирает маршруты, поднимает/уменьшает процессы приложений и обновляет слушатели. Это экономит нервы при частых релизах. А если вы привыкли к «nginx + uWSGI/php-fpm + перезапуск», такой подход сперва кажется странным, но в реальных пайплайнах он часто сокращает стек и точки отказа.
Ключевые сущности в конфигурации
Вся жизнь крутится вокруг четырёх разделов: listeners, routes, applications, upstreams (пути API: /config/listeners, /config/routes, /config/applications, /config/upstreams). Listener принимает трафик на адресе или UNIX‑сокете, routes — это цепочка шагов match → action, applications описывают рантайм (Python, Node.js, PHP и т. д.) и процессы, а upstreams нужны для балансировки внешних бэкендов. Хотите залить весь конфиг сразу? Простая команда через control‑socket:
curl -X PUT --data-binary @config.json --unix-socket /var/run/control.unit.sock http://localhost/config
Прямо просится в контейнеры и CI.
Слушатели и TLS без сюрпризов, но с нюансами
Слушатель можно задать точным адресом и портом (127.0.0.1:8300), вайлдкардом (*:8400) или UNIX‑сокетом (unix:@abstract_socket). Важно: на Linux вайлдкард *:PORT конфликтует с другими слушателями на том же порту, так что старый нужно удалить прежде, чем «перехватывать» порт. Это особенность ядра, а не прихоть Unit. Очередь подключений (backlog) регулируется параметром backlog и упирается в системные лимиты (например, net.core.somaxconn), поэтому не удивляйтесь, если поставленное значение «режет» ОС. TLS настраивается пакетами сертификатов (certificate bundle), можно задать minprotocol и ciphersuites через conf_commands, а для сессий доступны cache_size, timeout и tickets. Тикеты управляются ключами в base64; поддерживаются ключи AES‑256 (80 байт) и AES‑128 (48 байт), причём последний в списке выпускает новые тикеты, что удобно для плановой ротации. Обновлять ключи можно прямо через API PUT/POST/DELETE — удобно для кластера за балансировщиком, особенно когда несколько инстансов Unit должны «делить память» о TLS‑сессиях. Секреты через API? Да, но только при аккуратном доступе к control‑socket и дисциплинированной автоматизации.
X‑Forwarded и подмена клиента: как Unit не даёт себя обмануть
Объект forwarded отвечает за интерпретацию X‑Forwarded-* безопасным способом. Вы задаёте source — список доверенных адресов (или «unix» для локального сокета), client_ip — из каких заголовков брать IP, protocol — из какого заголовка считать схему (например, X-Forwarded-Proto), и recursive — как разбирать цепочки X‑Forwarded‑For. Важная деталь: Unit применит эти заголовки только если исходный IP попадает в доверенный source, так что внешнему клиенту не получится просто прислать нужный заголовок и «притвориться» прокси. Исторический момент: прежний синтаксис client_ip заменили на forwarded (совместимость сохранена), и это стоит учесть при апгрейдах.
Маршрутизация: гибкость без трюков
Routes — это последовательность шагов с match и action; если match нет, action выполнится сразу (обычно такой шаг один и в конце). Условия сопоставления богатые: host, uri, method, headers, cookies, arguments, source/destination, с подстановками (), регулярками (~^...), отрицаниями (!pattern) и диапазонами IP/портов. Unit корректно декодирует percent‑encoding перед сравнением, что снимает ряд «краевых» багов. Действия — pass (в application/route/upstream), proxy к внешнему HTTP, share для статики с fallback, return для статуса/редиректа и rewrite для переписывания URI. А если нужна динамика, используйте переменные: $host, $uri, $request_uri, arg_, cookie_*, header_*. Это позволяет, например, направлять трафик в applications/blogs$uri или формировать путь к статике на лету. Хотите поправить заголовки ответа? Есть response_headers, куда можно добавить, скажем, политику кэширования.
Где Unit реально выручает: микросервисы, Python/Node.js и не только
NGINX Unit для микросервисов хорош тем, что он сам и рантайм, и роутер, и менеджер процессов: меньше компонентов, меньше сопряжений. Для Python и Node.js Unit снимает зависимость от uWSGI, gunicorn, pm2 и прочих «обвязок» — конфигом задаёте тип приложения, путь и модуль, а дальше API делает остальное; при этом стоит мерить производительность под вашу нагрузку, потому что универсальных победителей не существует. Нужна смесь: статика и SSR (например, Next.js или SvelteKit)? Share отдаёт файлы, а всё остальное — pass в приложение; просто и без акробатики. Миграция с легаси‑монолита тоже становится мягче: поднимите Unit на поддомене, отрежьте долю трафика, посмотрите на поведение — и уже потом переключайте основной поток.
Контейнеры и Kubernetes: минимум скриптов, максимум предсказуемости
NGINX Unit в контейнерах чувствует себя естественно: образ с нужным языковым модулем, приложение в /app, а конфигурацию заливаете через control‑socket при старте; никакой громоздкой entrypoint‑магии. В Kubernetes вариантов несколько: ConfigMap с конфигом, init‑container, который заливает JSON, или отдельный sidecar, меняющий конфигурацию по API. Часто control‑socket кладут на общий volume, а доступ к нему ограничивают — не хочется, чтобы любой Pod мог крутить маршруты, верно? Подумайте и о ротации TLS tickets через PUT/POST/DELETE: это избавит от сюрпризов при многосерверной терминтации.
Ещё немного о безопасности и ловушках
Проверьте права на control‑socket и чётко разделите окружения — смешивать стейдж и прод с одним сокетом опасно. На Linux сначала удаляйте конфликтующие listeners на том же порту, иначе вайлдкард не поднимется; это системное поведение, с ним не поспоришь. При загрузке сертификатов используйте именно --data-binary в curl, чтобы двоичные данные не повредились. Forwarded настраивайте только с корректным source, иначе кто угодно сможет «подложить» X‑Forwarded‑For. И помните про системные лимиты backlog: ваш «идеальный» параметр всё равно прижмут net.core.somaxconn или аналоги в BSD.
NGINX Unit vs Nginx: не битва, а раздел труда
Философии разные. Unit — это рантайм с маршрутизацией и процесс‑менеджментом приложений; nginx — мощный L7‑прокси и edge‑инструмент с кэшированием и огромной экосистемой. Где важна гибкая конфигурация без перезапусков, мультиязыковая поддержка, переменные и маршрутизация в одном месте — NGINX Unit уместен и экономичен. Где критичны продвинутый кэш, сложные схемы проксирования и расширяемость — nginx остаётся королём. Комбинация тоже здравая: nginx на периметре, Unit — как универсальный рантайм внутри кластера; звучит утилитарно, но работает удивительно гладко.
Когда этот инструмент действительно нужен
- У вас много сервисов на Python, Node.js, PHP, и хочется один процесс, который ими управляет и маршрутизирует запросы. Сократите зоопарк.
- Вы в контейнерах и Kubernetes и не хотите городить лишние sidecar’ы и init‑скрипты. Конфиг — это API, а не ритуал.
- Нужны тонкие правила маршрутизации (регулярки, отрицания, переменные, rewrite) с отдачей статики и прокси — без отдельного сервера на прокладке.
- Требуется безопасно принимать X‑Forwarded‑* только от доверенных источников и аккуратно вращать TLS tickets между несколькими инстансами.
Если вас раздражают бессмысленные перезапуски, а конфигурации хочется применять мгновенно — Unit придётся кстати. Если же вам нужны навороченные edge‑возможности и экстремальная гибкость плагинов, оставайтесь на nginx или соедините оба мира. Думаю, идеальный стек — это не догма, а набор инструментов, которыми управляете вы, а не они вами.