Возможности 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, за исключением естественных ограничений при работе на более низком уровне.