Урок по треку DevOps: CI/CD, IaC, облака 8 мин

Балансировка и nginx за 8 минут

L4 и L7, алгоритмы балансировки, reverse proxy, upstream и health checks, таймауты, лимиты и логи.

Балансировщик принимает запросы и распределяет их по нескольким экземплярам сервиса. Он даёт масштабирование, отказоустойчивость (умерший экземпляр выводится) и единую точку для TLS, лимитов и логов.

L4 и L7

  • L4 (транспортный уровень) видит IP и порты, балансирует TCP- и UDP-соединения, не разбирая содержимое. Быстро, подходит для любых протоколов: базы, gRPC без маршрутизации, MQTT. Примеры: LVS, HAProxy в режиме tcp, сетевые балансировщики облаков, stream в nginx.
  • L7 (прикладной) понимает HTTP: маршрутизирует по хосту, пути, заголовкам, завершает TLS, добавляет заголовки, кеширует, ограничивает частоту. Примеры: nginx, HAProxy в режиме http, Envoy, Traefik, Application Load Balancer.

Алгоритмы

  • Round robin (по умолчанию в nginx): по очереди, можно с весами weight=3.
  • Least connections (least_conn): туда, где меньше активных соединений. Хорош для запросов разной длительности.
  • Хеш (ip_hash, hash $cookie_session consistent): один клиент на один бэкенд. Липкие сессии, кеши на бэкендах. Консистентное хеширование меньше перемешивает клиентов при добавлении узла.
  • Random two choices (random two least_conn): выбрать двух случайных и взять менее загруженного, хорошо работает при нескольких балансировщиках.

nginx как reverse proxy

upstream api {
    least_conn;
    server 10.0.1.10:8080 max_fails=3 fail_timeout=30s;
    server 10.0.1.11:8080 max_fails=3 fail_timeout=30s;
    server 10.0.1.12:8080 backup;
    keepalive 32;
}

server {
    listen 443 ssl;
    server_name api.example.ru;

    client_max_body_size 10m;

    location /api/ {
        proxy_pass http://api;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_connect_timeout 2s;
        proxy_read_timeout 30s;
        proxy_next_upstream error timeout http_502 http_503;
    }
}

Что здесь важно:

  • keepalive в upstream и proxy_http_version 1.1 с пустым Connection переиспользуют соединения к бэкендам, иначе на каждый запрос открывается новое TCP-соединение.
  • Заголовки X-Forwarded-* передают бэкенду реальный IP клиента и схему. Доверять им бэкенд должен только от своего балансировщика.
  • proxy_pass со слешем в конце (http://api/) заменяет совпавшую часть location, без слеша передаёт URI как есть. Частый источник 404 после настройки.
  • По умолчанию client_max_body_size 1m: загрузка большого файла получит 413.

Health checks

Открытый nginx делает пассивные проверки: если за время fail_timeout набралось max_fails неудачных попыток обращения к серверу, он считается недоступным на тот же fail_timeout (по умолчанию max_fails=1, fail_timeout=10s). Что считается неудачей, задаёт proxy_next_upstream. Активные проверки (периодический запрос на /health) есть в NGINX Plus, HAProxy, Envoy и облачных балансировщиках. В Kubernetes роль активных проверок выполняет readiness probe.

Повторы запросов

proxy_next_upstream пересылает запрос на другой сервер при ошибке. Неидемпотентные запросы (POST, PATCH) по умолчанию не повторяются, если запрос уже ушёл на бэкенд: иначе можно дважды списать деньги. Не включайте non_idempotent бездумно.

Таймауты и коды ошибок

  • 502 Bad Gateway: бэкенд отказал в соединении, оборвал его или вернул мусор. Часто: приложение упало или не слушает порт.
  • 504 Gateway Timeout: бэкенд не ответил за proxy_read_timeout (или не принял соединение за proxy_connect_timeout).
  • 499: код nginx, клиент закрыл соединение, не дождавшись ответа. Рост 499 часто означает медленный бэкенд.
  • 503: нет доступных серверов в upstream или сработал лимит запросов.

Лимиты

limit_req_zone $binary_remote_addr zone=perip:10m rate=10r/s;
location /login { limit_req zone=perip burst=20 nodelay; }

rate средняя скорость, burst размер очереди всплеска, nodelay обслуживает всплеск сразу. Отклонённые запросы получают 503 (меняется limit_req_status 429).

Логи

Формат combined: IP, время, строка запроса, статус, размер, referer, user agent. В свой формат полезно добавить $request_time (полное время запроса) и $upstream_response_time (время ответа бэкенда). Разница между ними показывает, где теряется время: в бэкенде или в медленном клиенте и сети. Логи по умолчанию ротирует logrotate, после ротации nginx получает сигнал USR1 и переоткрывает файлы.

Изменение конфига без простоя

nginx -t проверяет конфиг, nginx -s reload (или systemctl reload nginx) запускает новые воркеры с новым конфигом, старые дорабатывают текущие запросы. Перезапуск restart рвёт соединения.

Частые ошибки

  • Нет keepalive к upstream: тысячи соединений в TIME_WAIT.
  • proxy_read_timeout 60 секунд по умолчанию для API, который должен отвечать за секунду.
  • Доверие X-Forwarded-For от кого угодно: клиент подделает свой IP.
  • restart вместо reload.

Что спросят на собесе

  • Чем L4 отличается от L7 балансировки.
  • Какие алгоритмы балансировки знаете и когда какой.
  • Чем 502 отличается от 504 и как искать причину.
  • Как nginx понимает, что бэкенд умер.
  • Как получить реальный IP клиента за балансировщиком.

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.