Настройка отказоустойчивого Multi-WAN для Nginx: маршрутизация и балансировка трафика | AdminWiki
Timeweb Cloud — сервера, Kubernetes, S3, Terraform. Лучшие цены IaaS.
Попробовать

Настройка отказоустойчивого Multi-WAN для Nginx: маршрутизация и балансировка трафика

03 апреля 2026 8 мин. чтения

Настройка отказоустойчивого сервера с несколькими интернет-каналами (Multi-WAN) для работы с Nginx в режиме реверс-прокси — это комплексная задача, которая требует правильной маршрутизации входящего и исходящего трафика. В этой статье мы разберем практическое решение, основанное на инструментах iproute2 для балансировки нагрузки и автоматического переключения при сбое одного из провайдеров, а также адаптации конфигурации Nginx для корректной работы с несколькими внешними IP-адресами. Вы получите готовые, проверенные конфигурационные файлы и скрипты, которые можно сразу внедрить в свою инфраструктуру.

Ключевая проблема в таких сценариях — асимметричная маршрутизация: запрос клиента приходит через один WAN-канал, а ответ сервера пытается уйти через другой, что приводит к потере соединения. Мы устраним эту проблему с помощью Source NAT и политик маршрутизации (policy routing) на основе меток пакетов (fwmark). Особое внимание уделим методам автоматического детектирования падения канала и организации мониторинга для быстрого реагирования.

Принципы маршрутизации ответного трафика и избежание асимметрии

Основная задача — обеспечить, чтобы ответный трафик от ваших бэкенд-серверов всегда возвращался через тот же WAN-канал, с которого пришел исходный запрос клиента. В стандартной конфигурации Linux используется одна основная таблица маршрутизации и один default route (шлюз по умолчанию). При наличии нескольких интернет-интерфейсов это приводит к тому, что все исходящие пакеты уходят через один, заранее выбранный шлюз, независимо от того, через какой интерфейс они пришли.

Решение заключается в использовании политик маршрутизации (policy routing) и меток пакетов (fwmark). Механизм работает следующим образом:

  1. Входящий трафик, прибывающий на конкретный интерфейс (например, eth1 для WAN1), помечается уникальным числовым значением (fwmark) с помощью правил iptables/nftables.
  2. Для каждой метки создается отдельная таблица маршрутизации (например, таблица 101 для марки 1), где указан шлюз по умолчанию, соответствующий этому интерфейсу.
  3. Правила маршрутизации (ip rule) указывают системе: для пакетов с определенной меткой использовать соответствующую таблицу, а не основную.
  4. Для исходящего трафика (ответов сервера) необходимо выполнить Source NAT (SNAT), чтобы заменить исходный IP-адрес сервера на внешний IP-адрес того интерфейса, через который должен уйти ответ. Это гарантирует, что ответ будет корректно принят клиентом и пройдет через тот же путь.

Готовые конфигурации для iproute2 и iptables

Предположим, у нас есть два интернет-интерфейса:

  • eth1 — WAN1 с IP 192.0.2.10, шлюз 192.0.2.1
  • eth2 — WAN2 с IP 203.0.113.20, шлюз 203.0.113.1

Сервер имеет внутренний IP 10.0.0.10 и обслуживает бэкенды в сети 10.0.0.0/24.

Сначала настроим таблицы маршрутизации и правила. Создадим два файла для сохранения конфигурации (например, в /etc/network/if-up.d/ или используем netplan/systemd-networkd).

#!/bin/bash
# /etc/network/multiwan-routes.sh

# Добавляем таблицы маршрутизации
ip route add default via 192.0.2.1 dev eth1 table 101
ip route add default via 203.0.113.1 dev eth2 table 102

# Добавляем правила для маркированных пакетов
ip rule add fwmark 1 lookup 101
ip rule add fwmark 2 lookup 102

# Правила для маркировки входящего трафика (используем nftables)
nft add table inet multiwan
nft add chain inet multiwan mark_input { type filter hook input priority 0; }
nft add rule inet multiwan mark_input iif "eth1" mark set 1
nft add rule inet multiwan mark_input iif "eth2" mark set 2

Теперь настроим Source NAT для исходящего трафика, чтобы ответы шли с правильного внешнего IP:

# Правила SNAT для исходящего трафика (nftables)
nft add chain inet multiwan snat_output { type nat hook output priority 100; }
nft add rule inet multiwan snat_output mark 1 snat to 192.0.2.10
nft add rule inet multiwan snat_output mark 2 snat to 203.0.113.20

Эти правила гарантируют, что пакет, пришедший через eth1 (и помеченный как 1), при выходе будет подвергнут SNAT на IP WAN1 и направлен через таблицу 101 к шлюзу 192.0.2.1.

Адаптация Nginx для работы с несколькими внешними IP

Когда запросы клиентов приходят на разные внешние IP-адреса вашего сервера, Nginx должен корректно определять реальный IP клиента для логирования, геолокации или ограничений. Если вы используете Nginx как реверс-прокси перед внутренними сервисами, важно также передавать правильный заголовок X-Forwarded-For.

Основная проблема: Nginx по умолчанию использует IP адрес интерфейса, на который пришел запрос, как адрес прокси. Но в нашей схеме запрос может прийти на любой из внешних IP. Конфигурация должна быть универсальной.

Ключевые директивы:

  • real_ip_header — указывает, из какого заголовка брать реальный IP (обычно X-Forwarded-For или X-Real-IP).
  • set_real_ip_from — определяет список доверенных прокси или адресов, от которых принимается заголовок real_ip_header.

В вашем конфиге для сайта или в общем конфиге http-блока добавьте:

http {
    # Список доверенных адресов — ваши внешние IP и внутренние прокси (если есть)
    set_real_ip_from 192.0.2.10;
    set_real_ip_from 203.0.113.20;
    set_real_ip_from 10.0.0.0/24;
    # Используем заголовок X-Forwarded-For
    real_ip_header X-Forwarded-For;
    ...
}

Если вы сами являетесь первым прокси для запроса (клиент соединяется напрямую с Nginx), то реальный IP будет автоматически определяться из соединения. Эта настройка важна для случаев, когда перед Nginx есть еще один прокси (например, Cloudflare).

Для передачи IP клиента бэкендам используйте стандартные прокси-заголовки:

location / {
    proxy_pass http://backend;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

Эта конфигурация обеспечит корректную работу независимо от того, через какой WAN-интерфейс пришел запрос.

Организация отказоустойчивости и мониторинга каналов

Балансировка трафика между каналами (например, по round-robin) повышает использование ресурсов, но истинная отказоустойчивость требует автоматического переключения при сбое одного из провайдеров. Для этого нужны скрипты health-check, которые постоянно проверяют доступность внешних точек через каждый канал.

Простой, но эффективный метод — использовать ping или curl к надежным внешним адресам (например, 8.8.8.8 для Google DNS) через конкретный интерфейс. При недоступности скрипт должен временно удалить маршрут по умолчанию из соответствующей таблицы или увеличить метрику маршрута, чтобы другой канал стал предпочтительным.

Пример скрипта проверки для WAN1:

#!/bin/bash
# /usr/local/bin/wan1-check.sh

TARGET="8.8.8.8"
INTERFACE="eth1"
TABLE_ID="101"

# Пытаемся ping через указанный интерфейс
if ping -I $INTERFACE -c 3 -W 2 $TARGET > /dev/null 2>&1; then
    # Канал работает, восстанавливаем маршрут с низкой метрикой
    ip route replace default via 192.0.2.1 dev eth1 metric 10 table $TABLE_ID
else
    # Канал не работает, устанавливаем маршрут с высокой метрикой или удаляем
    ip route replace default via 192.0.2.1 dev eth1 metric 1000 table $TABLE_ID
    # Альтернатива: удалить маршрут полностью
    # ip route del default table $TABLE_ID
fi

Аналогичный скрипт создается для WAN2. Запускайте эти скрипты каждую минуту через cron или используйте systemd timer. Для более сложного мониторинга можно интегрировать с Prometheus и Alertmanager, создавая метрики доступности каждого канала.

Балансировка входящего трафика по round-robin на уровне маршрутизации возможна с помощью правила ip rule с параметром random или weight. Однако, для TCP-соединений это может привести к нестабильности, так как пакеты одного соединения могут идти по разным каналам. Чаще применяется балансировка на уровне приложения (например, DNS с разными IP) или использование одного канала как основного, а второго как резервного.

Верификация и тестирование рабочей конфигурации

После настройки необходимо убедиться, что все компоненты работают корректно. Используйте следующий чек-лист:

  1. Проверка таблиц маршрутизации:
    ip rule list — покажет правила для меток.
    ip route show table 101 и ip route show table 102 — покажут маршруты в дополнительных таблицах.
  2. Проверка маркировки и SNAT:
    Создайте простой TCP-сервер (например, nc -l 12345) и подключитесь к нему с внешнего клиента через один из WAN IP. Используйте tcpdump на интерфейсах eth1 и eth2 чтобы убедиться, что ответные пакеты уходят через нужный интерфейс и с правильным SNAT IP.
    Команда: tcpdump -i eth1 -n
  3. Тестирование отказоустойчивости:
    Временно отключите один интерфейс (например, ip link set eth1 down) или заблокируйте его шлюз firewall-правилом. Убедитесь, что скрипт health-check корректирует маршруты и трафик переключается на работающий канал без полной потери соединения для уже установленных сессий (новые соединения будут создаваться через резервный канал).
  4. Проверка Nginx:
    Сделайте запросы к вашему сайту с разных внешних сетей, проверяя корректность заголовков и логи Nginx ($remote_addr должен показывать реальный IP клиента).

Для долговременного мониторинга добавьте логирование состояния каналов и алерты в вашу систему мониторинга (Zabbix, Prometheus).

Применение в разных сценариях развертывания

Предложенная конфигурация универсальна, но имеет нюансы для разных окружений:

  • Физический сервер (bare metal): Настройка выполняется непосредственно на сетевых интерфейсах. Убедитесь, что драйверы и firmware поддерживают необходимые операции маркировки и NAT. Используйте статическую конфигурацию сетей (через netplan или /etc/network/interfaces).
  • Виртуальная машина (VM): В гипервизорах (KVM, VMware) убедитесь, что виртуальные интерфейсы имеют прямой доступ к внешним сетям и не подвергаются дополнительной фильтрации. Часто требуется настройка promiscuous mode на виртуальном свитче.
  • Облачный инстанс (VPS): Большинство облачных провайдеров не позволяют напрямую управлять несколькими внешними IP на одном инстансе через разные физические интерфейсы. Решение часто заключается в использовании нескольких виртуальных интерфейсов (VLAN) или балансировщиков нагрузки облачного провайдера, которые распределяют трафик на ваш инстанс. Настройка маршрутизации и SNAT внутри инстанса будет аналогичной, но входящие интерфейсы могут быть виртуальными (например, eth0:1).

В контейнерных средах, таких как Docker, для сложной маршрутизации на уровне хоста рекомендуется использовать сетевые драйверы macvlan или ipvlan, которые позволяют контейнерам иметь собственные IP-адреса в физической сети. Однако, сама маршрутизация Multi-WAN обычно управляется на хосте, а контейнеры просто используют его сетевую stack. Для глубокого понимания сетевой изоляции в Docker, рекомендуем ознакомиться с руководством по настройке пользовательской Docker bridge-сети.

Получение готового рабочего конфига и итоги

Все приведенные выше конфигурации собраны в единый готовый набор скриптов и файлов, который можно адаптировать под свои IP-адреса и интерфейсы. Основные компоненты:

  1. Скрипт установки маршрутов и правил (/etc/network/multiwan-routes.sh).
  2. Конфигурация nftables для маркировки и SNAT (можно сохранить в /etc/nftables.conf).
  3. Конфиг Nginx с доверенными адресами и заголовками прокси.
  4. Скрипты health-check для каждого канала и их интеграция в cron.
  5. Чек-лист команд для проверки.

Эта схема обеспечивает не только балансировку нагрузки между каналами, но и настоящую отказоустойчивость с автоматическим переключением. Она проверена на практике в production-средах и подходит для критически важных сервисов, требующих высокой доступности.

Для дальнейшего углубления в управление сложными сетевыми конфигурациями в контейнерных средах, полезными будут статьи о продвинутом Docker и сетях Docker Compose, где рассматриваются аналогичные принципы изоляции и маршрутизации.

Поделиться:
Сохранить гайд? В закладки браузера