Проксирование на уровне HTTP закрывает большинство задач, но иногда приложению нужен «сырой» TCP или UDP. Типичные примеры — PostgreSQL и MySQL, DNS, MQTT, syslog, игровые серверы и старые протоколы без HTTP-обёртки. Модуль stream в Nginx работает именно с потоком байтов: он не разбирает заголовки и не маршрутизирует по URL, зато даёт балансировку, таймауты, ограничения и TLS на транспортном уровне. Разберём рабочие конфигурации и диагностику.

Когда stream уместен, а когда нет

Stream полезен, когда нужно спрятать несколько backend-серверов за одним адресом, добавить отказоустойчивость или завершать TLS перед приложением, которое само не поддерживает TLS. Он не подходит для маршрутизации по пути, кэширования ответов, проверки заголовков и переписывания URL — это задачи http-модуля. Если база умеет репликацию, а клиентские драйверы поддерживают несколько хостов, прокси может оказаться лишним звеном; но для старых клиентов он спасает.

  • TCP: PostgreSQL, MySQL, Redis, MQTT, SMTP, игровые серверы.
  • UDP: DNS, syslog, SNMP, потоковое видео, VoIP.
  • TLS passthrough: маршрутизация по SNI без расшифровки трафика.

Проверка модуля и место в конфигурации

Сначала убедитесь, что модуль собран. В Debian и Ubuntu он часто вынесен в пакет libnginx-mod-stream, в RHEL-семействе — в nginx-mod-stream. Директива stream допустима только в главном контексте, рядом с http, но не внутри него: это частая ошибка при копировании примеров из документации.

nginx -V 2>&1 | tr ' ' '\n' | grep -i stream
nginx -t

Чтобы не раздувать основной файл, вынесите виртуальные серверы в отдельный каталог и подключите его одним include. Проверка синтаксиса выполняется до перезагрузки, поэтому опечатка не уронит рабочий сервис.

# /etc/nginx/nginx.conf
stream {
    include /etc/nginx/stream.d/*.conf;
}

TCP: PostgreSQL за прокси

Для базы важны короткий таймаут подключения и длинный таймаут простоя: транзакция может думать минутами, и обрыв по таймауту простоя недопустим. Директива backup оставляет сервер в резерве, а max_fails и fail_timeout включают пассивную проверку доступности.

upstream postgres_backend {
    server 10.0.10.11:5432 max_fails=3 fail_timeout=10s;
    server 10.0.10.12:5432 backup;
}

server {
    listen 5432;
    proxy_pass postgres_backend;
    proxy_connect_timeout 3s;
    proxy_timeout 1h;
    proxy_buffer_size 16k;
}

Пассивная проверка означает, что Nginx узнаёт о проблеме только после неудачного соединения. Активные health checks в открытой версии отсутствуют — это возможность коммерческой подписки. Не включайте повтор запроса к другому серверу без понимания семантики: возможно двойное выполнение.

UDP: DNS и другие датаграммы

В UDP нет соединения, поэтому сессия живёт по таймауту, а число ожидаемых ответов задаётся явно. Для DNS обычно достаточно одного ответа на запрос; если поставить слишком большое значение, сессия будет висеть до истечения proxy_timeout.

upstream dns_backend {
    server 10.0.20.21:53;
    server 10.0.20.22:53;
}

server {
    listen 53 udp;
    proxy_pass dns_backend;
    proxy_responses 1;
    proxy_timeout 2s;
}

Для DNS over TCP добавьте отдельный server с listen 53; — это разные транспорты, и один блок их не покрывает. Проверяйте, что upstream отвечает на запросы с адреса прокси, а не только с localhost, иначе после переключения трафика клиенты получат отказ.

Адрес клиента и PROXY protocol

После прокси backend видит адрес Nginx, а не пользователя. Если приложение пишет аудит или ограничивает доступ по IP, включите PROXY protocol, но только когда backend его поддерживает. Иначе соединение развалится на первом пакете.

server {
    listen 5432;
    proxy_pass postgres_backend;
    proxy_protocol on;
}

В http-части принимающая сторона настраивается через set_real_ip_from и real_ip_header proxy_protocol. Не доверяйте заголовку со всех адресов: поддельный PROXY-префикс исказит источник в логах и проверках доступа.

TLS passthrough и маршрутизация по SNI

Иногда сертификаты должны оставаться на backend, а Nginx — только распределять соединения по имени. Тогда используется ssl_preread: прокси читает ClientHello, но не расшифровывает трафик и не видит запросы.

stream {
    upstream web_app { server 10.0.30.11:8443; }
    upstream api_app { server 10.0.30.12:8443; }

    map $ssl_preread_server_name $backend {
        app.example.com   web_app;
        api.example.com   api_app;
        default           web_app;
    }

    server {
        listen 443;
        ssl_preread on;
        proxy_pass $backend;
    }
}

Учтите ограничения: без расшифровки недоступны HTTP-логи, WAF и проверка заголовков, а SNI не является механизмом аутентификации. Клиенты без SNI попадут в default, поэтому он должен вести на корректный backend. Если нужен именно TLS-терминатор, используйте ssl_certificate внутри stream-сервера, но тогда сертификаты придётся обновлять на прокси.

Логи, лимиты и диагностика

Формат лога в stream отличается от http: здесь есть протокол, байты в обе стороны и время сессии, но нет URL и кода ответа. Логируйте upstream_addr — без него сложно понять, кто именно тормозил.

log_format stream_main '$remote_addr:$remote_port $protocol $status '
                        '$bytes_sent $bytes_received $session_time '
                        '$upstream_addr';
access_log /var/log/nginx/stream.log stream_main;

Для защиты от злоупотреблений работают limit_conn_zone и limit_conn, а также proxy_download_rate и proxy_upload_rate. Ограничение скорости легко ломает легитимные бэкапы и репликацию, поэтому вводите его точечно для конкретного server, а не глобально.

ss -lntup | grep -E ':(5432|53|443)\b'
journalctl -u nginx --since '15 min ago' --no-pager
tcpdump -ni any 'port 5432' -c 20

Если соединение устанавливается, но данных нет, смотрите таймауты и MTU. Симптом «мелкое работает, крупное зависает» часто указывает на PMTU и блокировку ICMP, а не на Nginx. Для проверки слушающих сокетов и очередей используйте ss, для журналов — journalctl, для пакетов — tcpdump на своём интерфейсе.

Чек-лист перед продакшеном

  • nginx -t проходит, конфигурация включена в главный контекст.
  • Таймауты соответствуют поведению приложения, а не взяты наугад.
  • PROXY protocol включён только там, где backend его понимает.
  • В логах есть upstream и время сессии.
  • Настроен мониторинг ошибок и количества соединений.
  • Отдельно отслеживаются задержки и повторные подключения.
  • Изменения проверены на стенде с реальным клиентом.

Итог

Stream превращает Nginx в аккуратный L4-балансировщик: несколько директив закрывают TCP и UDP, TLS passthrough и ограничения. Главное — не включать функции вслепую: PROXY protocol ломает неподготовленный backend, а агрессивные таймауты рвут долгие транзакции. Начните с одного сервиса и метрик.

Насколько публикация полезна?

Нажмите на звезду, чтобы оценить!

Средняя оценка 0 / 5. Количество оценок: 0

Оценок пока нет. Поставьте оценку первым.

Поделиться: Telegram ВКонтакте