upstream и балансировка нагрузки

Рейтинг: 48.7% · 7 голосов
Самый подробный курс по Nginx - от первого конфига до production API-gateway. Архитектура и event loop, контексты и location, виртуальные хосты, статика, reverse proxy и upstream-балансировка, кэширование, HTTPS и TLS (post-quantum), HTTP/2 и HTTP/3 QUIC, безопасность и rate limiting, realip, JSON-логи и сквозная трассировка, метрики, тюнинг и траблшутинг. Плюс OpenResty (Lua, фазы, JWT, динамика) и Angie (нативные Prometheus, ACME, dynamic upstream). На основе эталонного gateway и лучших практик. Для новичков и SRE/DevOps. Актуально на 2026 (nginx 1.30/1.31, Angie, ingress-nginx EOL -> Gateway API).
Ответить
Аватара пользователя
Igor_NgINX
Сообщения: 44
Зарегистрирован: 11 май 2026, 05:31

upstream и балансировка нагрузки

Сообщение Igor_NgINX »

Оглавление курса (44)
  1. Что такое Nginx и почему он захватил веб
  2. Установка Nginx, Angie и OpenResty: пакеты, версии, Docker
  3. Первый конфиг с нуля: минимальный сервер за 5 минут
  4. Архитектура процессов: master, worker и event loop
  5. Анатомия конфигурации: контексты, наследование и модульность
  6. Виртуальные хосты: server и server_name
  7. Директива location: матчинг и приоритеты по шагам
  8. Раздача статики: root, alias, try_files, sendfile
  9. Переменные, map и блок if
  10. rewrite, return и канонизация URL
  11. Работа с заголовками: add_header и proxy_set_header
  12. Модель доверия прокси-заголовков: realip и защита от спуфинга
  13. Сжатие: gzip, brotli и zstd
  14. Reverse proxy: proxy_pass и проброс запроса
  15. upstream и балансировка нагрузки (вы здесь)
  16. Таймауты, повторы и устойчивость прокси
  17. FastCGI и PHP: nginx + PHP-FPM (и uwsgi/scgi)
  18. WebSocket, gRPC и стриминг через Nginx
  19. Кэширование ответов: proxy_cache и микрокэш
  20. HTTPS и TLS: сертификаты, протоколы, шифры (2026)
  21. Let's Encrypt и ACME: certbot, acme.sh, нативный ACME Angie
  22. HTTP/2 и HTTP/3 (QUIC) в 2026
  23. Безопасность: заголовки, скрытие версии, ограничения
  24. Rate limiting и защита от перегрузки
  25. Контроль доступа: allow/deny, auth_basic, auth_request и mTLS
  26. Логирование: access_log, форматы и структурированный JSON
  27. Сквозная трассировка запросов и OpenTelemetry
  28. Метрики и мониторинг Nginx
  29. Производительность и тюнинг под нагрузку
  30. Траблшутинг: 502, 504, 403, 404 и debug-лог
  31. OpenResty: Nginx как платформа на LuaJIT
  32. Фазы обработки запроса и Lua-хуки
  33. Lua API: ngx.*, shared dict, cosocket и lua-resty-core
  34. Практика OpenResty: авторизация, JWT, кэш, Redis
  35. Angie: современный форк Nginx 2026
  36. Динамическая конфигурация и service discovery
  37. Nginx как API-gateway и Kubernetes Gateway API
  38. Динамические модули, njs и WAF
  39. Stream-модуль: проксирование TCP и UDP
  40. Деплой, перезагрузка и конфиг как код
  41. Сценарий: статика, SPA и кэширование за CDN
  42. Сценарий: микросервисный gateway по образцу ngx-trace-gateway
  43. Капстоун: собираем production-gateway с нуля
  44. Чек-лист безопасности, CVE 2026 и аудит конфигурации
Один бэкенд - это всегда временное явление. Сегодня твоё приложение живёт на одном PHP-FPM или одном Node-процессе, а завтра в него стучатся тысячи клиентов, и единственный сервер либо упирается в CPU, либо падает на деплое, унося с собой весь сайт. Тебе нужно несколько одинаковых бэкендов и кто-то, кто будет раскидывать запросы между ними, выкидывать из ротации мёртвые узлы и переживать перезапуски без 502 в лицо пользователю. Этим кем-то и становится nginx upstream - встроенный балансировщик, который не требует отдельного HAProxy, отдельной железки и отдельной команды эксплуатации.

В этом уроке разберём механику nginx балансировки до самого дна: как устроен пул бэкендов, какие методы распределения бывают и когда какой брать, что реально делают max_fails и backup, почему keepalive к апстриму - это не опция, а необходимость, и что важного поменялось в nginx 1.30 в 2026 году (спойлер: дефолты стали умнее, а sticky-сессии наконец-то приехали в open-source). Опираться будем на боевой upstream.conf из эталонного gateway-проекта.

Что такое nginx upstream и как он включается в поток запроса

Директива upstream объявляет именованную группу серверов - пул бэкендов. Сам по себе блок ничего не проксирует, он только описывает, куда и как раскидывать. Подключается он по имени в proxy_pass (или fastcgi_pass, uwsgi_pass и т.д.).

Код: Выделить всё

upstream backend_upstream {
    server 10.0.0.1:9000 weight=5 max_fails=3 fail_timeout=30s;
    server 10.0.0.2:9000 max_fails=3 fail_timeout=30s;
    server 10.0.0.3:9000 max_fails=3 fail_timeout=30s;
    server 10.0.0.4:9000 backup;

    keepalive 64;
}

server {
    location ^~ /api/ {
        proxy_pass http://backend_upstream;
    }
}
Это ровно фрагмент upstream.conf из эталонного проекта (имена и адреса оттуда). Когда запрос попадает в location и nginx видит proxy_pass http://backend_upstream, он идёт в одноимённый upstream-блок, выбирает по текущему методу балансировки один живой server и устанавливает (или переиспользует) к нему соединение. Если выбранный сервер ответил ошибкой - в дело вступает proxy_next_upstream и nginx пробует следующий. Так nginx превращается в полноценный nginx балансировщик без единой сторонней зависимости.

Важно держать в голове область видимости: upstream живёт в блоке http, а выбор сервера происходит на каждый запрос. Пул соединений (keepalive) и счётчики ошибок - per-worker, то есть каждый рабочий процесс ведёт свой учёт. Это объясняет часть неинтуитивного поведения, к которому вернёмся в граблях.

Изображение

Методы балансировки: round-robin, least_conn, ip_hash, hash, random

Метод задаётся одной директивой внутри upstream-блока. По умолчанию - взвешенный round-robin (директиву писать не нужно). Разберём арсенал по делу.

Round-robin (по умолчанию). Запросы идут по кругу пропорционально весам. weight=5 у первого сервера в эталоне означает, что он получит примерно в 5 раз больше запросов, чем узел с весом по умолчанию (1). Идеален, когда все запросы примерно одинаковы по стоимости и бэкенды stateless. Это базовая nginx load balancing-стратегия, с которой стоит начинать.

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

Код: Выделить всё

upstream backend_upstream {
    least_conn;
    server 10.0.0.1:9000;
    server 10.0.0.2:9000;
    keepalive 64;
}
ip_hash - привязка по IP клиента. nginx ip_hash вычисляет хеш от первых октетов IP клиента и стабильно отправляет один и тот же IP на один и тот же сервер. Это самый простой способ session persistence (липкости), когда сессия хранится локально на бэкенде, а не в общем Redis. Минус виден сразу: если за одним корпоративным NAT или мобильным оператором сидят тысячи людей с одним внешним IP - все они улетят на один бэкенд, и баланс перекосит. И ещё: ip_hash несовместим с параметром weight в чистом виде слабо и плохо переживает добавление/удаление серверов (часть привязок переедет).

Код: Выделить всё

upstream backend_upstream {
    ip_hash;
    server 10.0.0.1:9000;
    server 10.0.0.2:9000;
}
hash $key [consistent] - хеш по произвольному ключу. Гибкая замена ip_hash: ключом может быть что угодно из переменных - $request_uri, $cookie_session_id, $arg_user_id. Параметр consistent включает ketama-хеширование (рандеву-подобное распределение), при котором добавление сервера переносит лишь малую долю ключей, а не пересобирает всю карту. Это то, что нужно для шардирования кэша или липкости по cookie:

Код: Выделить всё

upstream backend_upstream {
    hash $cookie_session_id consistent;
    server 10.0.0.1:9000;
    server 10.0.0.2:9000;
}
random two [least_conn]. nginx берёт два случайных сервера и из них выбирает лучший по least_conn (это и есть классический алгоритм power of two choices). Хорош для очень больших пулов и кластеров с несколькими балансировщиками: чистый least_conn там может создавать стадный эффект (все балансировщики дружно ломятся на один наименее загруженный узел), а random two его размазывает.

Практическое правило: stateless и равномерные запросы - round-robin; разброс по времени обработки - least_conn; нужна липкость без общего стораджа - hash по cookie с consistent (а не ip_hash, если за прокси/NAT много клиентов).

Параметры серверов: weight, max_fails, fail_timeout, backup, down, max_conns

Каждая строка server конфигурируется параметрами, и именно тут живёт встроенная отказоустойчивость.
  • weight=N - относительный вес в распределении (по умолчанию 1). В эталоне weight=5 у более мощного узла.
  • max_fails=N и fail_timeout=T - это пассивный health check. Если за окно fail_timeout накопится N неудачных попыток, сервер на это же время помечается недоступным и выводится из ротации. В эталоне max_fails=3 fail_timeout=30s: три неудачи за 30 секунд - и узел отдыхает 30 секунд, потом nginx снова попробует. Что считается неудачей, задаёт proxy_next_upstream (по умолчанию error и timeout; HTTP 500/502/503/504 туда можно добавить явно).
  • backup - запасной сервер, который вступает в игру, только когда все основные недоступны. В эталоне это 10.0.0.4. Удобно держать на нём заглушку-страницу обслуживания или резервный дешёвый инстанс.
  • down - помечает сервер как выведенный из эксплуатации вручную (для плановых работ), не удаляя строку из конфига.
  • max_conns=N - жёсткий потолок одновременных соединений к узлу. Незаменим, когда бэкенд (например, PHP-FPM с фиксированным pm.max_children) физически не тянет больше N параллельных запросов: лишние подождут в очереди nginx, а не задушат FPM.
Ключевое и неприятное про open-source nginx: его health check - только пассивный. nginx узнаёт о смерти бэкенда лишь когда сам пошлёт туда живой клиентский запрос и получит ошибку. То есть первый запрос после падения узла обязательно попадёт в мёртвый сервер и словит задержку или ошибку (дальше сработает next_upstream и перекинет на живой). Активных проверок ("nginx сам по таймеру долбит /health и заранее убирает труп") в бесплатном nginx нет - это фишка nginx Plus. А вот в Angie (форк nginx) активные health checks и slow_start доступны бесплатно, о чём ниже.

keepalive к апстриму: главный рычаг производительности и что изменилось в 2026

Самый частый и самый дорогой промах новичков в nginx балансировке - забыть про keepalive к бэкенду. Без него nginx на каждый клиентский запрос открывает новое TCP-соединение к апстриму, прогоняет три рукопожатия, отдаёт ответ и закрывает сокет. На высоком RPS это выливается в тысячи TIME_WAIT-сокетов, выжранные эфемерные порты и лишние миллисекунды latency на ровном месте.

Директива keepalive N включает пул постоянных соединений: каждый worker держит до N idle-соединений к группе и переиспользует их. В эталоне keepalive 64 - безопасное стартовое значение. Важно понимать: N - это размер кэша idle-соединений на worker, а не лимит общего числа соединений. Под нагрузкой их может быть больше, просто сверх N idle-сокеты закрываются.

Исторически (до 1.30) одного keepalive было мало - нужно было ещё на уровне location явно сказать nginx говорить с бэкендом по HTTP/1.1 и не слать заголовок Connection: close:

Код: Выделить всё

location ^~ /api/ {
    proxy_pass http://backend_upstream;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
}
Без этих двух строк до 1.30 соединение к бэкенду шло по HTTP/1.0 с close, и keepalive фактически не работал - грабли, на которые наступали все. Именно поэтому в эталонном global_proxy.conf обе директивы прописаны глобально.

Что нового в nginx 1.30 (stable, апрель 2026). Соединение к бэкенду теперь по умолчанию HTTP/1.1 с keepalive - без явных proxy_http_version 1.1 и Connection "". Это снимает классическую ловушку для тех, кто забыл. Но - рекомендация на 2026: если твои конфиги должны работать и на старых версиях (а они почти всегда должны - дистрибутивы тащат разное), всё равно пиши эти две директивы явно. Они валидны, ничего не ломают и страхуют от сюрпризов при разном nginx на разных хостах.

Ещё в 1.30 у keepalive появился новый параметр - keepalive ... local. Он привязывает переиспользуемое соединение к локальному адресу клиентского соединения (полезно, когда исходящий IP к бэкенду важен, например для маршрутизации или ACL на стороне бэкенда). Плюс в 1.30 завезли HTTP/2-соединения к апстриму. Для большинства gateway-сценариев тебе по-прежнему нужен ровно keepalive 64 + (для совместимости) две строки proxy_*.

Sticky-сессии и активные health-чеки: nginx 1.30 против Angie

Раньше за липкость в open-source отвечал только ip_hash (со всеми его проблемами за NAT) или сторонний nginx-sticky-module сомнительной свежести; нормальные cookie-based sticky-сессии были привилегией nginx Plus. В 2026 это изменилось: native sticky приехал в open-source nginx (мейнлайн 1.29.x, вошёл в stable 1.30). Теперь можно прямо в upstream написать:

Код: Выделить всё

upstream backend_upstream {
    server 10.0.0.1:9000;
    server 10.0.0.2:9000;
    sticky name=srv_id expires=1h;
    keepalive 64;
}
nginx сам выдаёт клиенту cookie srv_id, привязывает его к конкретному бэкенду и при следующих запросах гонит на тот же узел. Это честная липкость по cookie, без проблем NAT, которыми страдает ip_hash, и без общего стораджа сессий. Если ты раньше держал ip_hash только ради session persistence - на 1.30 пора переезжать на sticky.

Грабли липких сессий за NAT (важно для любого варианта). ip_hash ломается, потому что тысячи клиентов за одним внешним IP выглядят как один клиент и едут на один сервер. Cookie-based sticky этого лишён, но имеет свою тонкость: если клиент не принимает cookie (боты, агрессивные приватные режимы, межсервисные вызовы без cookie-jar) - липкость для него не работает, и он раскидывается обычным методом. Поэтому липкость - это оптимизация, а не гарантия: бэкенд должен корректно переживать запрос, прилетевший "не на свой" узел. Лучшее же решение для масштаба - вообще вынести сессии в общий Redis/Memcached и сделать бэкенды stateless, тогда липкость не нужна совсем.

Про Angie. Если тебе нужны именно активные health checks (nginx сам по таймеру опрашивает бэкенды и заранее убирает мёртвые из ротации) и slow_start (плавный набор веса только что поднявшимся узлом, чтобы не завалить его холодным трафиком сразу после старта) - в бесплатном nginx их нет. В Angie они есть из коробки и бесплатно. Выглядит это так:

Код: Выделить всё

# синтаксис Angie
upstream backend_upstream {
    server 10.0.0.1:9000 max_conns=200 slow_start=30s;
    server 10.0.0.2:9000 max_conns=200 slow_start=30s;
    keepalive 64;
}

server {
    location ^~ /api/ {
        proxy_pass http://backend_upstream;
        health_check interval=5s fails=3 passes=2 uri=/healthz;
    }
}
Это та граница, на которой решают: остаться на ванильном nginx с пассивными проверками, доплатить за nginx Plus или перейти на Angie ради бесплатных активных проверок и slow_start. Для большинства проектов пассивного max_fails хватает, но если первый запрос после падения узла в лицо пользователю для тебя недопустим - смотри в сторону Angie.

Типичные грабли
  • Забытый keepalive. На старых nginx (до 1.30) - ещё и забытые proxy_http_version 1.1 + Connection "". Симптом: высокий latency, гора TIME_WAIT в ss -s, исчерпание портов. Лечится тремя строками.
  • ip_hash за NAT/CDN. Перекос балансировки в ноль, один узел в огне, остальные простаивают. Лекарство - native sticky (1.30) или общий сторадж сессий.
  • Иллюзия активного health check. max_fails - это пассив. Мёртвый бэкенд будет ловить по одному запросу каждые fail_timeout секунд (проверочный заход). Если это больно - Angie или Plus.
  • max_fails=0. Отключает пассивную проверку полностью - сервер считается живым всегда. Иногда нужно (когда проверки делает внешний оркестратор), но по незнанию это выстрел в ногу: nginx будет упорно слать на труп.
  • Слишком большой keepalive. Завышенный N держит толпу idle-соединений к каждому бэкенду на каждый worker - бэкенд (особенно PHP-FPM) может упереться в лимит коннектов от простаивающих сокетов. Начинай с 64 и смотри метрики.
  • Несоответствие weight и реальной мощности. weight=5 на узле, который на деле не в 5 раз мощнее, просто перегрузит его. Веса надо мерить, а не угадывать.
Мини-лаба: собери балансировщик руками

Поднимем два простых бэкенда и раскидаем трафик между ними.
  • Шаг 1. Запусти два игрушечных бэкенда, отдающих свой номер. Если есть Docker:

    Код: Выделить всё

    docker run -d --name b1 -p 9001:80 -e NGINX_PORT=80 hashicorp/http-echo -text="backend-1"
    docker run -d --name b2 -p 9002:80 hashicorp/http-echo -text="backend-2"
    (или любые два http-echo/whoami на портах 9001 и 9002).
  • Шаг 2. Опиши upstream и проксирование:

    Код: Выделить всё

    upstream lab_upstream {
        server 127.0.0.1:9001 weight=2 max_fails=3 fail_timeout=10s;
        server 127.0.0.1:9002          max_fails=3 fail_timeout=10s;
        keepalive 32;
    }
    
    server {
        listen 8080;
        location / {
            proxy_pass http://lab_upstream;
            proxy_http_version 1.1;
            proxy_set_header Connection "";
        }
    }
  • Шаг 3. Проверь конфиг и перезагрузи:

    Код: Выделить всё

    nginx -t && nginx -s reload
  • Шаг 4. Постучись десяток раз и посмотри распределение - backend-1 должен встречаться примерно вдвое чаще из-за weight=2:

    Код: Выделить всё

    for i in $(seq 1 12); do curl -s http://127.0.0.1:8080/; done | sort | uniq -c
  • Шаг 5. Убей один бэкенд (docker stop b2) и снова постучись - весь трафик уедет на оставшийся узел, без ошибок в лицо клиенту. Подними обратно (docker start b2) и убедись, что узел вернулся в ротацию после fail_timeout.
  • Шаг 6. Поменяй метод: добавь первой строкой в upstream least_conn; затем замени на ip_hash; - перезагружай (nginx -s reload) и наблюдай, как меняется распределение при повторных запросах.
Контрольные вопросы
  • Чем nginx least_conn отличается от round-robin и при каком профиле нагрузки он выигрывает?
  • Почему nginx ip_hash плохо работает, когда много клиентов сидит за одним NAT, и что брать вместо него в nginx 1.30?
  • Что конкретно делает связка max_fails=3 fail_timeout=30s и почему это называют пассивным, а не активным health check?
  • Зачем нужен keepalive 64 в upstream-блоке и что изменилось в поведении по умолчанию в nginx 1.30?
Итог

nginx upstream - это встроенный, бесплатный и быстрый балансировщик: пул серверов, метод распределения (round-robin/least_conn/ip_hash/hash/random two) и пассивная отказоустойчивость через max_fails/fail_timeout/backup. Производительность держится на keepalive к апстриму - в 2026 он по умолчанию в nginx 1.30, но две строки proxy_http_version 1.1 + Connection "" всё равно стоит писать ради совместимости. Native sticky наконец-то в open-source 1.30 и должен вытеснить ip_hash там, где нужна липкость. А если упёрся в потолок пассивных проверок - активные health checks и slow_start ждут тебя бесплатно в Angie. Базовый эталонный шаблон (weight=5, max_fails=3, fail_timeout=30s, backup, keepalive 64) бери как стартовую точку и подкручивай под свои метрики.
👍6 ❤️3 🔥2 😄 🤔1
Аватара пользователя
anton2003
Сообщения: 1
Зарегистрирован: 14 май 2026, 09:34

Re: upstream и балансировка нагрузки

Сообщение anton2003 »

А я-то думал ip_hash это нормальная липкость, а у нас как раз весь офис за одним NATом сидит и один бэкенд всегда в огне. Теперь понятно, спасибо, переезжаю на sticky.
👍2 ❤️ 🔥1 😄 🤔
Аватара пользователя
zlovechno
Сообщения: 1
Зарегистрирован: 28 май 2026, 03:12

Re: upstream и балансировка нагрузки

Сообщение zlovechno »

Уточните: keepalive 64 это на каждый worker или на всю группу? Если воркеров 8, то получается до 512 idle-соединений к бэкенду суммарно?
👍1 ❤️ 🔥 😄 🤔
Ответить
← Предыдущая глава
Reverse proxy: proxy_pass и проброс запроса
Следующая глава →
Таймауты, повторы и устойчивость прокси

Все главы курса «Nginx профессионально: от первого конфига до API-gateway на OpenResty и Angie»

Поделиться темой: ✈ Telegram VK
Похожие запросы: Angie: форк nginx с нативными метрикамиОшибка nginx 502 Bad Gateway: причины и решениеupstream и балансировка нагрузки в nginxReverse proxy на nginx: proxy_passStream в nginx: проксирование TCP и UDP

Вернуться в «Nginx профессионально: от первого конфига до API-gateway на OpenResty и Angie»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость