Keepalived — небольшой демон, который реализует протокол VRRP и умеет переключать виртуальный IP между серверами. Это самый простой способ сделать отказоустойчивым обратный прокси, балансировщик, шлюз или панель управления, не покупая оборудование и не усложняя схему. Ниже — рабочая конфигурация из двух узлов, проверка сервиса, защита от разделения сети и разбор ошибок, которые встречаются чаще всего.
Как работает VRRP
Два и более узла объединяются в виртуальный маршрутизатор с общим идентификатором VRID. Один узел становится MASTER и отвечает на ARP-запросы для виртуального адреса, остальные находятся в состоянии BACKUP и слушают служебные пакеты. Если MASTER перестаёт отправлять анонсы, BACKUP ждёт истечения таймера и забирает адрес себе.
Пакеты VRRP передаются по multicast на адрес 224.0.0.18, протокол номер 112. Это важно для настройки фильтра пакетов: если вы разрешили только TCP и ICMP, VRRP молча не заработает. В облачных сетях, где multicast запрещён, используется режим unicast_peer с явным списком адресов.
Установка и базовая проверка
Пакет есть во всех основных дистрибутивах. После установки служба запускается автоматически, но с пустой конфигурацией пользы не приносит. Проверьте, что демон работает, и сразу определите имена интерфейсов — они понадобятся в конфиге.
sudo apt install keepalived
systemctl status keepalived --no-pager
ip -br addr show
ip route show default
В примерах ниже используется интерфейс eth0, адреса 10.0.0.11 и 10.0.0.12, виртуальный адрес 10.0.0.10 и VRID 51. Замените их на свои, но сохраните логику: у каждого узла свой приоритет, а virtual_router_id и пароль совпадают.
Конфигурация MASTER
Файл /etc/keepalived/keepalived.conf читается при старте. Параметр state задаёт желаемую роль, но итоговое состояние определяется приоритетом: узел с большим priority становится MASTER. Параметр advert_int задаёт интервал анонсов в секундах.
vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 51
priority 150
advert_int 1
authentication {
auth_type PASS
auth_pass ChangeMe2026
}
virtual_ipaddress {
10.0.0.10/24 dev eth0
}
}
На втором узле меняются только state, priority и, при желании, ничего больше. Второй узел получает priority 100 и state BACKUP. Пароль должен совпадать: несовпадение — классическая причина, по которой оба узла остаются в состоянии BACKUP или конфликтуют за адрес.
Проверки доступности сервиса
VRRP следит за сетевым интерфейсом, но не за приложением. Если nginx упал, а хост жив, адрес останется на нём, и пользователи получат ошибку. Решение — скрипт проверки, который уменьшает приоритет при сбое. Модуль weight должен превышать разницу приоритетов, иначе после снижения узел всё равно останется MASTER.
vrrp_script check_nginx {
script "/usr/bin/curl -fsS -o /dev/null http://127.0.0.1/health"
interval 2
timeout 2
fall 3
rise 2
weight -60
}
vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 51
priority 150
advert_int 1
track_script {
check_nginx
}
virtual_ipaddress {
10.0.0.10/24 dev eth0
}
}
Скрипт должен быть исполняемым, быстрым и возвращать ноль только при реальной готовности сервиса. Проверка вида «порт открыт» слабее: процесс может слушать порт, но не отдавать ответы. Для баз данных и очередей пишите отдельный health-эндпоинт, который проверяет зависимость, а не только сам факт запуска.
Проверка состояния и переключения
Утилита ip показывает, на каком узле сейчас находится виртуальный адрес. Журнал демона фиксирует переходы роли и причины. Для теста остановите keepalived на MASTER или погасите nginx и наблюдайте за вторым узлом.
ip -br addr show eth0
sudo journalctl -u keepalived -n 50 --no-pager
sudo systemctl stop keepalived
ping -c 4 10.0.0.10
После остановки службы на основном узле адрес должен появиться на резервном в течение нескольких секунд. Если этого не происходит, проверьте, что оба узла видят друг друга, VRID совпадает, а интерфейс не находится в состоянии DOWN.
Защита от split-brain
Split-brain — ситуация, когда оба узла считают себя MASTER и одновременно отвечают на ARP. Обычно причина в потере связи между узлами при живых сервисах. Последствия неприятны: часть клиентов уходит на один узел, часть на другой, сессии теряются, а данные могут разойтись.
Меры профилактики: отдельный канал связи между узлами, unicast вместо multicast в облаке, запрет на два узла с одинаковым приоритетом и внешний арбитр. Если приложение пишет данные, плавающий IP сам по себе не гарантирует целостность — нужна репликация или общее хранилище.
Unicast в облаке и фильтр пакетов
В облачных сетях multicast часто запрещён, и VRRP приходится переводить в режим явных адресов. Тогда каждый узел знает, кому отправлять анонсы, и служебный трафик идёт обычным unicast. Это же решение помогает в сетях, где IGMP-снупинг настроен непредсказуемо.
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 51
priority 100
unicast_src_ip 10.0.0.12
unicast_peer {
10.0.0.11
}
virtual_ipaddress {
10.0.0.10/24 dev eth0
}
}
Не забудьте разрешить протокол 112 между адресами узлов. Правило вида «разрешить весь трафик внутри подсети» работает, но слишком широко: ограничьте источник и назначение конкретными адресами. Для диагностики используйте tcpdump с фильтром proto 112 — он покажет, доходят ли анонсы и совпадают ли VRID.
Типичные ошибки
- Одинаковый priority на обоих узлах: роль выбирается непредсказуемо.
- Разные virtual_router_id или пароли: узлы не объединяются в группу.
- Закрытый протокол 112 на межсетевом экране: анонсы не доходят.
- Скрипт проверки без веса или с малым weight: приоритет не меняется, переключения нет.
- Виртуальный адрес не в той подсети: клиенты не могут до него достучаться.
- Отсутствие проверки приложения: адрес остаётся на сломанном узле.
Чек-лист внедрения
- Приоритеты различаются минимум на величину weight скрипта.
- Проверка сервиса возвращает ошибку при реальной деградации.
- VRRP разрешён на обоих узлах, multicast или unicast настроен явно.
- Виртуальный адрес не пересекается с DHCP-пулом.
- Переключение протестировано вручную и под нагрузкой.
- Настроен мониторинг того, на каком узле находится адрес.
Итог
Keepalived решает узкую, но важную задачу: переносит IP-адрес на живой узел без участия человека. Для веб-сервера или прокси этого часто достаточно. Для баз данных добавьте репликацию и продумайте, что произойдёт с незавершёнными транзакциями. Начните с двух узлов, проверьте сценарии отказа и только потом усложняйте схему.