Если вам нужен простой и предсказуемый high availability для фронтенда на Nginx без внедрения массивных балансировщиков, схема с floating IP и keepalived (VRRP) остаётся проверенной классикой. Один адрес «плавает» между двумя (или более) узлами в одной L2-сети: активный сервер обслуживает трафик, резервный мониторит здоровье и, при деградации или падении мастера, за считанные секунды принимает адрес и продолжает работу. В статье соберу практические детали: от ARP/GARP и тонких sysctl до настроек health check и проверки failover без сюрпризов.
Что такое floating IP и зачем он Nginx
Floating IP — это адрес, закреплённый не за «железом», а за сервисом. Он держится на активном узле и переезжает на резервный по сигналу протокола VRRP (реализован в keepalived). Для Nginx это даёт:
- быстрый и локальный failover без зависимости от внешних DNS TTL;
- прозрачность для клиентов: одна конечная точка, один IP;
- простую операционку: два небольших конфига вместо целого зоопарка.
Если всё же рассматриваете DNS-failover, оцените влияние TTL и кешей резолверов: см. тестирование влияния TTL на кеширование DNS. Для практичного старта подойдёт пара узлов на облачном VDS в одном L2-домене.
Архитектура и принцип работы VRRP
VRRP (Virtual Router Redundancy Protocol) назначает роли: мастер анонсирует доступность виртуального IP, бэкап держит руку на пульсе. Состояние синхронизируется VRRP-пакетами (IP protocol 112), по таймингам рассчитывается, когда «мастер считается недоступным». Как только бэкап принимает лидерство, он добавляет VIP на интерфейс и рассылает серию GARP (Gratuitous ARP), чтобы коммутаторы и хосты обновили ARP-кеши и начали слать трафик на новый MAC.
В keepalived вы создаёте vrrp_instance с общим virtual_router_id и пулом virtual_ipaddress. Приоритеты определяют, кто лидер. advert_int задаёт период объявлений. Здоровье мастера отслеживается скриптами vrrp_script (health check): если Nginx не отвечает как ожидается, приоритет понижается, и лидерство переходит к резервному узлу.
Ключ к предсказуемому RTO — не просто отслеживать процесс Nginx, а проверять реальную готовность: открытый сокет и корректный HTTP-ответ нужного сервиса.

Предпосылки и требования к сети
Для стабильной работы необходимо:
- оба узла в одном L2-домене (одна VLAN), интерфейсы в одинаковой подсети VIP;
- разрешён протокол 112 (VRRP) на межсетевых экранах;
- мультикаст VRRP не фильтруется или используется unicast-режим;
- сетевое оборудование не подавляет GARP, либо есть альтернативы (адресный flush/arping к шлюзу);
- на серверах согласованы sysctl для ARP и rp_filter.
Сетевые sysctl: ARP, rp_filter и nonlocal bind
Чтобы избежать ARP-флукса и гарантировать корректное поведение VIP, настройте:
net.ipv4.ip_nonlocal_bind=1— позволяет Nginx слушать VIP заранее, даже когда адрес ещё не назначен интерфейсу;net.ipv4.conf.all.arp_ignore=1иarp_announce=2— уменьшают вероятность ответов на ARP с «неправильного» интерфейса, стабилизируют ARP;net.ipv4.conf.all.rp_filter=2(loose) — полезно, если у вас асимметричные маршруты или несколько путей;- для IPv6 аналогично следим за ND (Neighbor Discovery), keepalived рассылает не ARP, а unsolicited NA.
# /etc/sysctl.d/99-vip.conf
net.ipv4.ip_nonlocal_bind=1
net.ipv4.conf.all.arp_ignore=1
net.ipv4.conf.default.arp_ignore=1
net.ipv4.conf.all.arp_announce=2
net.ipv4.conf.default.arp_announce=2
net.ipv4.conf.all.rp_filter=2
net.ipv4.conf.default.rp_filter=2
После изменения не забудьте применить параметры и перезапустить Nginx/keepalived, если это необходимо. Часть настроек (ARP) может влиять на все интерфейсы, поэтому проверяйте влияние на другие сервисы.
Базовая конфигурация keepalived (VRRP + VIP)
Рассмотрим пару узлов: nodeA (приоритет 200) и nodeB (приоритет 100). VIP 203.0.113.10/32 на интерфейсе eth0. В unicast-сценариях обязательно указываем unicast_src_ip и unicast_peer. Если сеть допускает мультикаст VRRP, можно обойтись без peer-списка.
# /etc/keepalived/keepalived.conf
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 51
priority 200
advert_int 1
preempt_delay 5
authentication {
auth_type PASS
auth_pass s3cret123
}
unicast_src_ip 203.0.113.21
unicast_peer {
203.0.113.22
}
virtual_ipaddress {
203.0.113.10/32 dev eth0 label eth0:vip
}
garp_master_delay 0
garp_master_repeat 5
garp_lower_prio_delay 10
track_script {
chk_nginx
}
notify_master "/usr/local/sbin/vip-transition.sh master"
notify_backup "/usr/local/sbin/vip-transition.sh backup"
notify_fault "/usr/local/sbin/vip-transition.sh fault"
}
vrrp_script chk_nginx {
script "/usr/local/sbin/check-nginx.sh"
interval 2
timeout 1
fall 2
rise 2
weight -50
}
Обратите внимание на weight -50: если чек проваливается, приоритет снижается, что позволяет резервному узлу перехватить VIP. preempt_delay даёт времени «новому мастеру» прогреться (например, открыть сокеты) перед активной эксплуатацией.
Health check Nginx: проверяем не процесс, а ответ
Минимально надёжный health check проверяет открытие TCP и получение корректного HTTP-статуса. Например, отдаём 200 OK на локальном http://127.0.0.1/health и удостоверяемся, что это действительно Nginx, а не «фантом» от systemd socket activation или чужой процесс.
# /usr/local/sbin/check-nginx.sh
#!/bin/sh
curl -sS -o /dev/null --max-time 1 http://127.0.0.1/health || exit 1
exit 0
На стороне Nginx есть смысл держать отдельный серверный блок только для health:
# /etc/nginx/conf.d/health.conf
server {
listen 127.0.0.1:80 reuseport;
server_name localhost;
location = /health {
add_header X-Health ok;
return 200 "ok\n";
}
}
Такой чек дешёв по ресурсам, не зависит от внешней сети, и отражает реальную готовность воркеров Nginx. Если вы используете reverse proxy к бэкендам, можно усложнить проверку и сходить до апстрима (но аккуратно с нагрузкой и таймаутами).
Настройка Nginx для VIP: bind и тайминги
Чтобы Nginx был готов «принять» VIP сразу после перехода, обычно достаточно слушать все адреса (0.0.0.0) или явно прописать VIP и включить ip_nonlocal_bind. На высоконагруженных серверах полезны reuseport и аккуратные таймауты, чтобы уменьшить количество полуброшенных коннектов при переключении.
# /etc/nginx/conf.d/site.conf
server {
listen 203.0.113.10:80 reuseport backlog=4096;
server_name example.test;
location / { proxy_pass http://app_upstream; }
}
server {
listen 203.0.113.10:443 ssl http2 reuseport backlog=4096;
server_name example.test;
ssl_certificate /etc/ssl/live/fullchain.pem;
ssl_certificate_key /etc/ssl/live/privkey.pem;
location / { proxy_pass https://app_upstream; }
}
Во многих случаях проще оставить listen 80 и listen 443 ssl без привязки к VIP, а перевязку адреса на интерфейс обеспечит keepalived. Главное — nonlocal bind, чтобы nginx стартовал на резерве и сразу прошёл health check. Если требуется публичный HTTPS, заранее подготовьте и установите сертификат — подойдут SSL-сертификаты.
GARP и обновление ARP-кешей
После перехода VIP на новый узел важно быстро «переубедить» соседей, что MAC-адрес изменился. Keepalived умеет рассылать GARP сериями. Если в вашей сети GARP ограничен, применяйте дополнительный скрипт в notify_master: можно вызвать arping к gateway и broadcast, а также выполнить точечный flush соседей на локальном хосте.
# /usr/local/sbin/vip-transition.sh
#!/bin/sh
state="$1"
VIP="203.0.113.10"
IFACE="eth0"
logger -t vip "state=$state"
if [ "$state" = "master" ]; then
ip neigh flush dev "$IFACE"
arping -c 3 -A -I "$IFACE" "$VIP"
arping -c 3 -U -I "$IFACE" "$VIP"
fi
Параметры -A и -U дают две формы gratuitous ARP, что повышает шансы на быстрое обновление таблиц у разных вендоров сетевого оборудования.
Тайминги failover: advert_int, мастер-даун и preempt
Время переключения складывается из нескольких частей: timeout VRRP, исполнение health check, назначение VIP, GARP/NA-обновление и время восстановления коннектов у клиентов. При advert_int 1 и fall 2 вполне реально держать RTO в районе нескольких секунд, не создавая избыточных ложных срабатываний.
preempt и preempt_delay контролируют возврат лидерства исходному мастеру. Если приоритеты фиксированы, мастер после восстановления «отберёт» VIP обратно. Многие предпочитают no-preempt поведение для стабильности: если резерв стал мастером, пусть работает до следующего планового окна. В keepalived это настраивается через nopreempt в vrrp_instance или через баланс веса скриптов.
IPv6: VIP и Neighbor Discovery
Для IPv6 вместо ARP используется ND. Keepalived сам разошлёт unsolicited Neighbor Advertisement. В конфиг добавляется блок virtual_ipaddress с адресом вида 2001:db8::10/128. Проверяйте, что сеть допускает эти объявления и не режет ICMPv6. Аналогично IPv4, держите health check локальным и быстрым.
Firewall и протокол 112
Если вы используете nftables/iptables, откройте VRRP (proto 112) между узлами. Также убедитесь, что политики не мешают GARP/NA. Типичные проблемы: stateful-фильтры вообще не видят «не-UDP/TCP» и дропают по умолчанию, либо на портах включена защита от spoofing, которая отвергает gratuitous объявления.
Тест-план failover: что и как проверять
- Базовая смена ролей: остановите keepalived на мастере и убедитесь, что VIP появился на резерве. Проверьте
ping VIP,curl http://VIP, логи. - Падение только Nginx: остановите Nginx на мастере, убедитесь, что
vrrp_scriptснижает приоритет и происходит переключение. - Сбой линка: отключите интерфейс у мастера, проверьте срабатывание по отсутствию VRRP-анонсов.
- Возврат мастера: проверьте политику preempt и что повторный переход не ломает активные клиенты (оцените процент RST и повторных TLS handshakes).
- GARP-эффект: измерьте время от смены мастера до появления корректного next hop у клиентов. Если медленно — увеличьте повтор GARP, добавьте
arping.

Надёжные health checks: несколько сигналов
Один HTTP-чек — хорошо, но ещё лучше дополнить его наблюдением за системным сервисом и сетью. Keepalived позволяет комбинировать несколько vrrp_script с разными весами: падение интерфейса, ошибки на диске, исчерпание воркеров.
vrrp_script svc_systemd {
script "/bin/systemctl is-active --quiet nginx"
interval 2
fall 1
rise 1
weight -20
}
vrrp_script iface_link {
script "/usr/bin/test -d /sys/class/net/eth0 && /usr/bin/cat /sys/class/net/eth0/operstate | grep -q up"
interval 1
fall 1
rise 1
weight -30
}
Так вы снижаете шанс «ложного мастера», который вроде как отвечает по HTTP, но фактически потерял линк или ушёл в деградацию.
Особые случаи: контейнеры, облака и L2-ограничения
В контейнеризированных раскладках VRRP-пакеты могут не выходить за пределы namespace, а ARP может работать нетипично. Старайтесь запускать keepalived в host-сети и привязывать VIP к физическому интерфейсу. В облачных средах часто требуется включить «разрешённые адресные пары» для VIP, иначе гипервизор будет отбрасывать кадры с «неожиданным» исходным MAC/IP. Некоторые провайдеры предлагают свой механизм «плавающих адресов» — интеграция с ним надёжнее, чем ручной GARP.
Производительность и пользовательский опыт при переключении
Даже при быстром failover неизбежны обрывы части долгоживущих TCP-сессий. Чтобы сгладить эффект:
- умеренные
keepalive_timeoutв Nginx; - разумные
proxy_read_timeoutиsend_timeoutна бэкенды; - ограничение очередей SYN, достаточные
backlogиreuseport; - для TLS — корректная сессия резюмирования, чтобы повторное рукопожатие было быстрее.
Если нужна липкость сессий, она не обеспечивается VRRP: придётся решать на уровне приложения, куки или распределённого хранилища сессий.
Наблюдаемость и тревоги
Включите подробные логи keepalived, метрики по состоянию VRRP-инстансов и тайминги health checks. Полезно собирать события переходов (notify-скрипт пишет в системный журнал) и строить графы времени до восстановления. Также контролируйте ARP-кеш на граничном маршрутизаторе: слишком долгий удерживающий таймер увеличит время недоступности после переключения.
Проверочный чеклист внедрения
- Уточнить L2-возможности: VRRP (proto 112), мультикаст, GARP/NA, ограничения безопасности.
- Согласовать sysctl:
ip_nonlocal_bind,arp_ignore,arp_announce,rp_filter. - Установить keepalived, подготовить
vrrp_instanceи health checks. - Настроить Nginx: слушатели под VIP или 0.0.0.0, быстрый /health, таймауты.
- Прописать notify-скрипт для GARP и логирования переходов.
- Открыть firewall для VRRP и убедиться в отсутствии фильтрации GARP.
- Провести тест-план: падение процесса, сети, сервера; замерить RTO и донастроить тайминги.
Пример полной связки: два узла, один VIP
Ниже — консолидированный пример, который можно взять за основу и адаптировать под свою сеть и требования. Обязательно замените адреса, интерфейсы, секреты, а тайминги подгоните под SLA.
# sysctl (общий для обоих узлов)
net.ipv4.ip_nonlocal_bind=1
net.ipv4.conf.all.arp_ignore=1
net.ipv4.conf.all.arp_announce=2
net.ipv4.conf.all.rp_filter=2
# keepalived (на мастере — priority 200, на бэкап — 100)
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 51
priority 200
advert_int 1
nopreempt
authentication {
auth_type PASS
auth_pass s3cret123
}
unicast_src_ip 203.0.113.21
unicast_peer { 203.0.113.22 }
virtual_ipaddress {
203.0.113.10/32 dev eth0 label eth0:vip
}
garp_master_repeat 5
garp_lower_prio_delay 10
track_script {
chk_nginx
svc_systemd
iface_link
}
notify_master "/usr/local/sbin/vip-transition.sh master"
notify_backup "/usr/local/sbin/vip-transition.sh backup"
notify_fault "/usr/local/sbin/vip-transition.sh fault"
}
С таким набором у вас есть чёткие сигналы деградации, аккуратный переход и управляемое время недоступности. Дальше можно усложнять: несколько VIP, разделение по портам/протоколам, отдельные инстансы под IPv6. Если раскатываете на новых серверах — начните с пары на VDS, это сократит время запуска.
Типичные проблемы и их диагностика
- Долгое восстановление после переключения. Проверьте GARP: увеличьте
garp_master_repeat, добавьтеarpingв notify-скрипт. Измерьте ARP timeouts на uplink. - VRRP-пакеты дропаются. Убедитесь, что firewall пропускает IP protocol 112. В unicast-режиме проверьте правильность
unicast_src_ipиunicast_peer. - Ложный мастер. Увеличьте строгость health check и добавьте проверки линка/сервиса. Используйте отрицательные веса для приоритета.
- Nginx не стартует на резерве. Включите
ip_nonlocal_bindили слушайте 0.0.0.0 вместо VIP. - Сеть облака запрещает GARP. Используйте встроенные механизмы провайдера для плавающих IP или настраивайте «разрешённые адресные пары».
Заключение
Связка keepalived (VRRP) + Nginx с floating IP остаётся одним из самых понятных и экономичных способов дать фронтенду высокую доступность. Потратьте время на корректный health check, аккуратные sysctl и надёжное оповещение соседей через GARP — и вы получите предсказуемый failover без сложных внешних зависимостей. Для масштабирования горизонтально вы всегда сможете добавить ещё один уровень балансировки, но начать стоит именно с этого простого и хорошо контролируемого кирпичика.


