Возможности Angie в качестве веб‑сервера и обратного прокси довольно известны. Но кроме работы на уровне L7 (HTTP), мы можем применять его на L4 (TCP и UDP) в качестве балансировщика и прокси‑сервера. Именно об этой функциональности мы и поговорим в сегодняшней статье.
Навигация по циклу
Настройка location в Angie. Разделение динамических и статических запросов.
Перенаправления в Angie: return, rewrite и примеры их применения.
Сжатие текста в Angie: статика, динамика, производительность.
Балансировка и проксирование L4-трафика в Angie.
Видеоверсия
Для вашего удобства подготовлена видеоверсия этой статьи, доступна на Rutube, VKVideo и YouTube.
Отличие модулей HTTP и Stream
Надеюсь, вы использовали Nginx или Angie в роли HTTP‑сервера, так как мы будем отталкиваться от конфигурации сервера в контексте HTTP‑модуля. Все директивы для работы на уровне HTTP указываются в рамках блока http. Для L4 используется отдельный модуль и соответствующий блок конфигурации: stream.
Ключевое отличие модулей HTTP и stream — работа на разных уровнях модели OSI (Open Systems Interconnection). Модуль HTTP относится к L7 — уровню приложения и реализует HTTP‑протокол. При работе модуля http сервер оперирует отдельными HTTP‑запросами, формируя ответ в соответствии с конфигурацией.
Модуль stream работает на уровне L4 — транспортном уровне, реализуя проксирование протоколов TCP и UDP. Для модуля stream понятие «запрос» в общем случае отсутствует. Можно считать, что модуль stream не пытается распознавать протоколы выше L4 и содержимое трафика остаётся черным ящиком. Исключение составляют модули с ограниченной возможностью разбора протоколов уровня приложения (MQTT Preread, SSL Preread, RDP Preread). Поэтому в модуле stream обычно оперируют термином «соединение», а не «запрос».
С точки зрения функциональности ограничения модуля stream определены работой на уровне L4, в остальном конфигурация и поведение похожи на аналогичные модули HTTP‑контекста.
Важно не путать одноимённые модули и переменные из http‑контекста и stream‑модули. Они могут иметь одинаковые директивы, но при этом иметь различие в доступных параметрах и вариантах применения. В документации Angie модули разделены по контекстам: «HTTP‑модуль» и «Потоковый модуль». Алфавитные указатели директив и переменных также указывают на контекст.
Проксирование TCP и UDP-приложений
Начнём изучение потокового модуля (stream) с простой задачи: нужно проксировать TCP‑приложение (MySQL) через Angie для реализации ограничений по количеству подключений и списка доступа на основе IP‑адреса клиента.
Первое, что необходимо сделать, определить сервер для привязки к TCP‑сокету в контексте stream:
stream { server { listen 6306; } }
В этом примере мы создали сервер, слушающий TCP‑порт 6306. Для директивы listen доступен широкий список дополнительных параметров. Например, можно включить создание сокетов для каждого рабочего процесс�� (reuseport) или указать режим SSL (ssl). Варианты указания сокета такие же, как и для HTTP‑режима (IP с портом, только порт, диапазон портов, отдельные сокеты для IPv4 и IPv6, UNIX‑сокеты).
Второе, что мы укажем, — на какой сервер нужно перенаправлять трафик. За это отвечает модуль proxy и его директива proxy_pass:
server { listen 6306; proxy_pass 192.168.1.2:3306; }
Обратите внимание, что схема (http(s)://) перед адресом сервера не указывается.
Теперь минимальная конфигурация проксирования готова, и нужно реализовать требование управления доступом и ограничения количества подключений с IP‑адреса.
stream { limit_conn_zone $binary_remote_addr zone=addr:10m; server { listen 6306; proxy_pass 192.168.1.2:3306; limit_conn addr 1; allow 192.168.1.0/24; deny all; } }
Итак, все требования учтены: мы ограничиваем количество подключений до одного на каждый клиентский IP‑адрес и разрешаем доступ только из подсети 192.168.1.0/24. В отличие от HTTP‑модуля, в stream по умолчанию не ведётся access.log. Это можно исправить, определив формат лога и подключив его в stream‑контексте.
stream { log_format basic '$remote_addr [$time_local] ' '$protocol $status $bytes_sent $bytes_received ' '$session_time ' "$upstream_addr"; access_log /var/log/angie/stream-access.log basic; }
Теперь в stream-access.log будут попадать записи следующего вида:
192.168.122.1 [08/Aug/2025:17:18:57 +0300] TCP 200 180902 3535 301.392 127.0.0.1:82
Такие записи появляются после завершения сессии работы с клиентом и показывают общую статистику трафика (напоминаю, что запросов здесь нет). Для удобства введены искусственные коды статуса сессии ($status), значения которых выбраны по аналогии с HTTP‑кодами ответа: 200, 400, 403, 500, 502, 503.
Проксирование UDP‑сервисов в целом похоже на работу с TCP, но имеет свои нюансы. Например, для отслеживания UDP‑сессий требуется указание параметра reuseport. Также можно явно указать количество ожидаемых ответов от сервера для завершения UDP‑сессии. Рассмотрим следующий пример:
server { listen 553; listen 553 udp reuseport; proxy_pass 192.168.1.1:53; proxy_responses 1; }
В приведённой конфигурации Angie принимает TCP и UDP‑трафик на порт 553 и проксирует его на сервер 192.168.1.1:53. При этом здесь явно указано, что мы ожидаем ровно один ответ от сервера в случае обращения по UDP. Также стоит упомянуть, что трафик не подвергается конвертации из TCP в UDP и обратно. Для проксирования всегда используется протокол, с которым клиент подключился к Angie.
Работа с TLS
При работе на L4 доступен полный спектр возможностей Angie для TLS. Разберём типичную задачу: нужно обернуть в TLS открытый трафик приложения (реализовать TLS‑терминацию) для безопасной передачи в публичных сетях. Сделать это можно следующим образом.
stream { server { listen 443 ssl; ssl_certificate /etc/angie/ssl/1.crt; ssl_certificate_key /etc/angie/ssl/1.key; proxy_pass 192.168.1.2:80; } }
Как и в HTTP‑модуле, достаточно включить TLS в директиве listen параметром ssl и указать сертификат с ключом. При этом уже открытый трафик проксируется на сервер.
В примере выше мы не использовали определение сервера по доменному имени (нет директивы server_name). Допустим, стоит задача распределять трафик на 443 порту на различные приложения: часть доменов будет обслуживаться в самом Angie в модуле HTTP, а часть проксироваться на другие сервера на L4. Решение такой задачи возможно; достаточно использовать директивы server_name и pass.
http { server { listen 8000; location / { root html; } } } stream { server { listen 443 ssl; server_name test.ru; ssl_certificate /etc/angie/ssl/1.crt; ssl_certificate_key /etc/angie/ssl/1.key; pass 127.0.0.1:8000; } server { listen 443 ssl; server_name serv.test.ru; ssl_certificate /etc/angie/ssl/1.crt; ssl_certificate_key /etc/angie/ssl/1.key; proxy_pass 192.168.1.2:9000; } }
В примере выше Angie открывает 443 TCP‑сокет для приёма соединений и терминации TLS. На основе данных из расширения SNI определяется имя сервера, и для домена test.ru сработает директива pass. Эта директива передаёт клиентское соединение в другой модуль сервера (HTTP в нашем случае), и обработка запросов будет происходить в модуле HTTP на сервере c сокетом 8000. Для домена serv.test.ru будет работать TLS‑терминация и L4-проксирование на сокет 192.168.1.2:9000.
А что, если терминация на уровне Angie не нужна, но мы всё ещё хотим распределять трафик на основе доменного имени? Для этого существует модуль ssl_preread. Он реализует фазу предварительного чтения запроса и может получать версию TLS, доменное имя и список протоколов по ALPN (например, h2 или h3). Эти значения записываются в соответствующие переменные: $ssl_preread_protocol, $ssl_preread_server_name и $ssl_preread_alpn_protocols. Более того, на основе этих переменных можно настроить гибкую маршрутизацию трафика. Рассмотрим пример:
map $ssl_preread_protocol $upstream { "" 192.168.1.2:22; "TLSv1.2" 192.168.1.2:443; default 192.168.1.2:443; } server { listen 8443; ssl_preread on; proxy_pass $upstream; }
Здесь нет TLS‑терминации (трафик передаётся «как есть»), но включен режим ssl_preread. В блоке map создаётся переменная $upstream, которая зависит от $ssl_preread_protocol. Если версия прокола имеет пустое значение, то трафик проксируется на SSH‑сервер (192.168.1.2:22); если там TLSv1.2 или что‑нибудь еще, то трафик проксируется на 192.168.1.2:443. Такие же схемы можно делать на основе имени сервера или протоколов в расширении ALPN.
Конечно, можно реализовать перешифрование TLS, то есть общаться с проксируемым сервером по TLS. Настройка аналогична проксированию с помощью модуля HTTP.
server { listen 443 ssl; ssl_certificate /etc/angie/ssl/1.crt; ssl_certificate_key /etc/angie/ssl/1.key; proxy_ssl on; proxy_ssl_verify on; proxy_ssl_name test.ru; proxy_ssl_server_name on; proxy_ssl_trusted_certificate /etc/angie/ssl/trust.crt; proxy_pass 192.168.1.2:443; }
Пример показывает проксирование на сервер 192.168.1.2:443 c использованием TLS. При этом происходит проверка сертификата проксируемого сервера (proxy_ssl_verify) с учетом имени test.ru (proxy_ssl_name) и доверенного сертификата (proxy_ssl_trusted_certificate).
Наконец, мы добрались до возможностей балансировки TCP‑ и UDP‑приложений.
Балансировка на транспортном уровне
Если вы знакомы с балансировкой HTTP‑приложений в Angie, переход на L4 будет простым. Поддерживаются все алгоритмы и режимы балансировки, кроме ip_hash и режима sticky cookie.
Список доступных возможностей (пометка PRO означает доступность только в коммерческой версии Angie PRO):
алгоритм Round Robin;
алгоритм Least Connection;
алгоритм Hash;
алгоритм Random Least Connection;
пассивные проверки серверов (
max_fails);опции
slow_start, resolveдля серверов;режим Sticky Route;
алгоритм Least Time (random) (PRO);
алгоритм Feedback (PRO);
режимы Sticky Learn и Sticky Learn Remote action (PRO);
активные проверки серверов (PRO);
опции
state, drain, backup_switchдля серверов (PRO).
Для реализации балансировки, как обычно, нужно определить блок upstream для описания группы серверов. Далее мы можем проксировать на эту группу серверов, обращаясь к ней по имени.
stream { upstream backend { server 192.168.0.1:8000; server 192.168.0.2:8000; } server { listen 9090; proxy_pass backend; } }
Так выглядит простейшая конфигурация для балансировки трафика между двумя серверами. По умолчанию используется метод Round Robin, веса имеют одинаковое значение.
Для указания нужного алгоритма балансировки используются директивы модуля upstream: hash, random, least_conn, feedback (PRO), least_time (PRO). Также не забываем про параметры директивы server. Допустим, нам необходимо реализовать балансировку методом least connections с функцией привязки сессий по доменам и ограничением количества подключений к каждому серверу. Получим следующую конфигурацию:
map $ssl_preread_server_name $route { test.ru a; test2.ru b; default ""; } upstream backend { least_conn; server 192.168.0.1:443 sid=a max_fails=2 fail_timeout=10s max_conns=100; server 192.168.0.2:443 sid=b max_fails=2 fail_timeout=10s max_conns=100; sticky route $route; } server { listen 443; ssl_preread on; proxy_pass backend; }
Обратите внимание, что в конфигурации выше для серверов установлены параметры max_fails и fail_timeout, которые отвечают за пассивные проверки. В контексте L4-балансировки max_fails контролирует количество попыток подключения к серверу (а не запросов, как в HTTP‑модуле), а fail_timeout как обычно определяет время, за которое эти попытки должны быть сделаны для признания сервера нерабочим (до прохождения еще одного периода fail_timeout).
Здесь можно отметить еще одно отличие от балансировки HTTP‑сервисов. Настройка переключения на следующий сервер (proxy_next_upstream) здесь имеет булевый тип: включена или выключена. При включении (по умолчанию) переход на следующий сервер происходит, если подключиться к выбранному невозможно.
С другой стороны, есть возможности, недоступные в HTTP‑модуле: ограничение скорости чтения ответа от сервера (proxy_download_rate) и ограничение скорости чтения данных от клиента (proxy_upload_rate).
Итоги
Мы разобрали основные сценарии использования Angie в качестве TCP и UDP‑прокси и балансировщика; нашли отличия модуля stream от модуля HTTP. Можно сказать, что возможности Angie на L4 практически сравнялись с возможностями модуля HTTP, за исключением естественных ограничений при работе на более низком уровне.
Следующая статья цикла: Клиентское кэширование в Angie.
