АйТи Фреш
Главная / Статьи / Сети
Сети

Почему оба узла Keepalived стали MASTER, хотя ping работает

Автор: Семёнов Евгений Сергеевич, директор ООО «АйТи-Фреш» · · ~18 мин чтения
Почему оба узла Keepalived стали MASTER, хотя ping работает
Иллюстрация к статье «Почему оба узла Keepalived стали MASTER, хотя ping работает».

Знакомая картина: реальные адреса двух серверов пингуются, сервис Keepalived запущен, но один VIP обнаруживается сразу на обоих узлах. Администратор добавляет `nopreempt`, перезапускает демоны — и ничего принципиально не меняется. Я, Семёнов Евгений Сергеевич, обычно начинаю не с конфигурации приоритетов, а с проверки VRRP-пакетов в обе стороны. Ниже покажу, почему ICMP здесь почти ничего не доказывает, как за несколько минут локализовать разрыв и какую схему я оставляю в эксплуатации.

Ping проверяет не тот протокол

Если оба узла перешли в MASTER, я первым делом предполагаю потерю VRRP-объявлений. Не «сломанные приоритеты» и не мистическую ошибку Keepalived, а именно отсутствие нормального обмена. BACKUP не получает объявления действующего MASTER, выдерживает Master Down Interval и закономерно назначает VIP себе. Первый узел при этом продолжает считать себя MASTER. Получается split-brain: два независимых хозяина одного адреса.

Успешный ping между реальными адресами проверяет только прохождение ICMP Echo Request и Echo Reply. VRRP для IPv4 — отдельный IP-протокол с номером 112. Это не TCP и не UDP, поэтому открыть какой-нибудь «порт VRRP» невозможно. Стандартный VRRPv3 отправляет объявления на link-local multicast-адрес 224.0.0.18 с TTL 255. Современным нормативным документом является RFC 9568 от апреля 2024 года, который заменил RFC 5798. У unicast-режима Keepalived назначение другое, но номер IP-протокола остаётся 112.

Особенно обманчив ping самого VIP с одного из узлов. Когда адрес уже добавлен локально, ядро может ответить, вообще не отправляя пакет к соседу. Даже стабильный ping с третьей машины не оправдывает конфигурацию: ARP-запись клиента способна некоторое время указывать на один сервер, пока другие клиенты уже ходят на второй. В результате часть соединений работает, часть обрывается, а команда дежурного видит четыре красивых ответа ICMP и считает сеть исправной.

Не начинайте лечение с изменения `priority` или `nopreempt`. Сначала докажите захватом пакетов, что каждый узел получает объявления второго.
Памятка: Ping проверяет не тот протокол — схема
Памятка: Ping проверяет не тот протокол. Открыть схему в полном размере

Как я подтверждаю split-brain за десять минут

Сначала отделяю состояние демона от состояния ядра. В журнале ищу переходы Entering MASTER STATE, затем проверяю наличие адреса на каждом сервере. Если оба журнала говорят MASTER и ip показывает VIP на обоих интерфейсах, это настоящий dual-active. Если Keepalived на одном узле пишет BACKUP, но VIP там остался, причина другая: адрес добавлен статически, сторонним скриптом или не был убран после нештатной операции.

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

keepalived --version
systemctl status keepalived --no-pager
journalctl -u keepalived --since '-15 min' --no-pager
ip -br -4 address show dev ens192
sudo timeout 12 tcpdump -ni ens192 -e -vvv 'ip proto 112'

При advert_int 1 за 12 секунд должен появиться не один случайный пакет, а устойчивая последовательность примерно раз в секунду. В выводе смотрю источник, назначение, TTL, версию VRRP, VRID и priority.

Захват нужен с двух сторон. Если узел A отправляет объявления, а на B их нет, и наоборот, проблема лежит между интерфейсами: VLAN, multicast-фильтрация, distributed firewall, security group, ACL или виртуальный коммутатор. Если B видит кадр через tcpdump, но Keepalived на него не реагирует, проверяю локальный firewall и параметры пакета. Захватчик видит кадр раньше части сетевой обработки ядра, поэтому наличие строки в tcpdump ещё не доказывает, что пакет дошёл до VRRP-сокета.

Фильтр `port 112` неверен: у VRRP нет порта. Используйте `ip proto 112` или поддерживаемый вашей сборкой tcpdump фильтр `vrrp`.
Почему оба узла Keepalived стали MASTER, хотя ping работает — схема
Схема к статье. Открыть схему в полном размере

Где объявления пропадают чаще всего

На физических коммутаторах чаще всего виноваты неверный VLAN, port isolation, ACL для IP protocol 112 или неудачная multicast-политика. В виртуальной среде добавляются распределённый firewall, правила security group и ограничения облачной сети. Некоторые платформы вообще не обещают L2 multicast. Там я сразу выбираю unicast Keepalived, если сама платформа разрешает плавающий адрес и L2/L3-модель этого решения. Просто заменить multicast на unicast, не сверившись с устройством сети, тоже нельзя.

В облаках и на части гипервизоров multicast режется ещё до гостевой ОС. Публичные облака, как правило, не передают multicast между виртуальными машинами, а виртуальные коммутаторы с включёнными фильтрами MAC/multicast или IGMP snooping без querier тихо отбрасывают кадры на 224.0.0.18. Симптом одинаковый: каждый узел отправляет объявления, но на соседе захват пуст. В такой среде я не спорю с платформой, а перевожу пару на unicast_src_ip и unicast_peer, предварительно убедившись, что облако вообще позволяет переносить VIP между интерфейсами — иначе адрес переезжает только через API провайдера, и VRRP здесь не поможет.

На Linux-хосте типовой промах выглядит скучно: политика INPUT — drop, ICMP разрешён отдельным правилом, TCP-порты сервиса тоже разрешены, а протокол 112 забыли. Поэтому ping проходит идеально. Я просматриваю полный ruleset с handle и счётчиками:

sudo nft -a list ruleset
sudo iptables-save

Если используется unicast, разрешаю VRRP только от реального адреса второго узла и, при фильтрации OUTPUT, симметрично наружу. Временное правило годится для доказательства причины, но постоянное изменение вношу в управляемый ruleset или Ansible, иначе следующий reload всё сотрёт.

Ещё одна группа причин — узлы получают пакеты, но считают их чужими или недействительными. На обоих должны совпадать address family, virtual_router_id, версия VRRP, набор VIP и интервал объявлений. В unicast-конфигурации адрес локального unicast_src_ip должен существовать, а в unicast_peer должен быть настоящий адрес соседа. Опция check_unicast_src полезна: она не позволяет принимать объявления от незаявленного источника. Для пары на одном VLAN я также фиксирую TTL 255 и проверяю его на приёме.

Нехватка CPU и длинная пауза планировщика действительно могут задержать Keepalived, особенно в перегруженной VM. Но это обычно даёт кратковременное переключение, а не устойчивые два MASTER при нормальном обмене. Я проверяю scheduler и загрузку после сети, а не до неё. Сначала смотрю фактические объявления — это быстрее и почти всегда приводит к причине.

Отдельно проверяю расхождения, при которых пакеты доходят, но отбрасываются. Разные virtual_router_id означают для Keepalived два независимых виртуальных роутера: каждый честно становится MASTER своего экземпляра, а VIP оказывается на обоих. Несовпадение блока authentication (auth_type/auth_pass) или ключей auth_hmac приводит к тому же результату — объявления соседа отвергаются, и в журнале обычно видно сообщение о неверной аутентификации или неизвестном VRID. Ещё один источник ложного MASTER — track_script: если скрипт проверки на одном узле постоянно падает (нет прав, неверный путь, enable_script_security без script_user), приоритет снижается или экземпляр уходит в FAULT, и картина переключений становится непредсказуемой. Сам по себе track_script не создаёт два MASTER, но маскирует настоящую причину.

Для multicast-схемы на хосте с политикой drop разрешение выглядит так — отдельно для nftables и для классического iptables; цепочку подставьте из своего ruleset:

# nftables
sudo nft insert rule inet filter input ip daddr 224.0.0.18 ip protocol vrrp counter accept comment "VRRP multicast"
# iptables
sudo iptables -I INPUT -p 112 -d 224.0.0.18 -j ACCEPT
# firewalld
sudo firewall-cmd --permanent --add-rich-rule='rule protocol value="vrrp" accept'
sudo firewall-cmd --reload

Счётчик counter сразу показывает, начали ли пакеты попадать в правило. Если счётчик растёт, а узлы всё ещё оба MASTER, причина уже не в firewall, а в параметрах экземпляра.

Обычная аутентификация `PASS` не шифрует VRRP и не чинит доставку пакетов. RFC 9568 не содержит аутентификации для VRRPv3, а man keepalived.conf прямо называет блок `authentication` несовместимым со стандартом; для защиты от подмены в Keepalived 2.4 есть расширение `auth_hmac`, но и оно не заменяет сетевую изоляцию.
Порядок действий: Где объявления пропадают чаще всего — схема
Порядок действий: Где объявления пропадают чаще всего. Открыть схему в полном размере

Практический случай: компьютерная школа «Клавиатура и класс», 49 РМ

Название школы условное; схема и последовательность работ взяты из моей практики без раскрытия заказчика. В компьютерной школе «Клавиатура и класс» 49 рабочих мест: четыре учебных класса, преподавательская и администрация. Внутренний учебный портал и сервер заданий стояли за двумя виртуальными балансировщиками lb01 и lb02, по 2 vCPU, 2 Гбайт RAM и одному интерфейсу 1 Гбит/с. Оба работали на Debian 12 с пакетным Keepalived 2.2.7. Реальные адреса — 10.20.49.11/24 и 10.20.49.12/24, VIP портала — 10.20.49.20/24, VRID 49. Приоритеты 150 и 100, advert_int 1. Для этой нагрузки ресурсы были с большим запасом; добавлять CPU ради VRRP не требовалось.

После переноса VLAN на новый хост виртуализации оба узла оказались MASTER. Ping между .11 и .12 не терялся, доступ по SSH работал. Во время занятия в двух классах за 14 минут у девяти учеников появились плавающие ошибки входа на портал: новый TCP-сеанс попадал то на один балансировщик, то на другой, и сессия терялась посреди контрольной. В ARP-кэшах разных компьютеров VIP соответствовал разным MAC-адресам. Дежурный добавил nopreempt на резервный узел и перезапустил Keepalived. Это не помогло — резервный по-прежнему не слышал MASTER и после тайм-аута назначал адрес себе.

Мы одновременно запустили tcpdump на двух VM. Каждый узел исправно отправлял VRRP, а захват на принимающей стороне видел пакеты второго узла. Затем проверили nftables: шаблон hardening разрешал ct state established,related, ICMP, SSH и HTTPS, после чего цепочка INPUT завершалась политикой drop. Разрешения для IP protocol 112 в шаблоне не было. Именно поэтому ping создавал ложное ощущение исправности, а Keepalived не получал объявления из сетевого стека.

Я сначала остановил Keepalived на резервном узле, чтобы убрать второй VIP и прекратить ARP-качели. Затем мы добавили двустороннее разрешение protocol 112 только между .11 и .12. Заодно перевели пару на явный unicast: для управляемой пары VM он проще в диагностике и не зависит от multicast-политики виртуальной сети. После проверки конфигурации последовательно запустили узлы. При аварийном исчезновении MASTER резерв с priority 100 принимал VIP примерно за 3,6 секунды — это ровно 3 × advert_int + (256 − 100)/256; при штатной остановке с объявлением priority 0 — заметно быстрее. За следующую учебную неделю синтетическая проверка не зафиксировала ни одного обрыва, а из шаблона firewall сделали отдельный тест.

При действующем split-brain не перезапускайте одновременно оба узла. Сначала выберите, кто временно останется MASTER, и безопасно снимите VIP со второго.

Конфигурация, которую я оставляю

Для такой двухузловой виртуальной пары я выбираю unicast. Это расширение Keepalived, а не стандартный режим взаимодействия любых VRRP-реализаций, и я говорю об этом прямо. Зато peer задан явно, источник легко ограничить firewall, а диагностика не зависит от IGMP snooping. На обычной физической LAN без ограничений multicast стандартная схема тоже нормальна — переделывать её только ради моды не нужно.

На lb01 конфигурация выглядела так:

vrrp_script chk_nginx {
    script "/usr/bin/systemctl is-active --quiet nginx"
    interval 2
    fall 2
    rise 2
}

vrrp_instance VI_WEB {
    state BACKUP
    interface ens192
    version 3
    virtual_router_id 49
    priority 150
    advert_int 1
    nopreempt

    unicast_src_ip 10.20.49.11
    check_unicast_src
    unicast_ttl 255
    unicast_peer {
        10.20.49.12 min_ttl 255 max_ttl 255
    }

    track_script {
        chk_nginx
    }

    virtual_ipaddress {
        10.20.49.20/24 dev ens192
    }
}

На lb02 меняются unicast_src_ip на 10.20.49.12, peer на 10.20.49.11, а priority — на 100. VRID, версия, интервал и VIP остаются одинаковыми. Ограничение TTL 255 здесь уместно, потому что peer находится в одном L2-сегменте. Для маршрутизируемого unicast такой предел применять вслепую нельзя: каждый L3-переход уменьшает TTL.

Блок vrrp_script с track_script здесь проверяет не соседа, а собственный сервис: если nginx на узле остановлен, экземпляр без weight уходит в FAULT и снимает VIP, а BACKUP получает роль. Скрипт должен быть быстрым и отрабатывать без ошибок под пользователем, заданным в global_defs (script_user, enable_script_security), иначе Keepalived отключит проверку или будет считать её проваленной. Сначала я запускаю ту же команду вручную и смотрю код возврата, а уже потом доверяю журналу переключений.

Оба узла стартуют из BACKUP, потому что это требуется для корректной работы nopreempt. Опция означает только одно: узел с более высоким priority не отберёт роль у уже работающего MASTER с меньшим priority. Она не является heartbeat, не проверяет соседа и не блокирует назначение VIP после истечения таймера. Если объявления недоступны, каждый BACKUP в конце концов станет MASTER независимо от nopreempt. Более того, находясь в MASTER, узел при разрешении конфликта руководствуется полученными объявлениями и приоритетами, а не превращает nopreempt в вечную блокировку.

Для unicast правило на lb01 разрешает источник .12, а на lb02 — источник .11. Конкретная цепочка зависит от вашего ruleset, поэтому ниже именно диагностический пример, а не универсальный файл firewall:

sudo nft insert rule inet filter input ip saddr 10.20.49.12 ip daddr 10.20.49.11 ip protocol 112 counter accept comment "VRRP from lb02"

После подтверждения я переношу правило в постоянную конфигурацию. Если OUTPUT фильтруется, добавляю зеркальное разрешение. У VRRP снова нет портов: правило с tcp dport 112 или udp dport 112 работать не будет.

Не копируйте `min_ttl 255 max_ttl 255` в routed-unicast схему. Сначала измерьте реальный путь и разберитесь, какую модель безопасности поддерживает ваша версия Keepalived.

Как принять работу, а не просто увидеть один MASTER

После исправления я наблюдаю захват не меньше минуты. На BACKUP должны регулярно приходить объявления MASTER с правильными VRID и priority. Затем проверяю три сценария: штатная остановка активного демона, аварийная потеря узла и возвращение узла с более высоким приоритетом. При advert_int 1 аварийное переключение обычно укладывается примерно в 3–4 секунды; точное время зависит от priority и формулы таймера. Обещать строго три секунды неправильно.

Тестирую с третьей машины, а не только между балансировщиками. Запускаю непрерывный прикладной запрос к VIP, фиксирую MAC и одновременно смотрю журналы обоих узлов:

while true; do date +%T.%3N; curl -fsS --connect-timeout 1 http://10.20.49.20/health || echo FAIL; sleep 0.2; done

Сначала останавливаю действующий MASTER через systemctl stop keepalived, затем запускаю его обратно. С nopreempt вернувшийся высокоприоритетный узел должен остаться BACKUP. После этого останавливаю текущий MASTER и убеждаюсь, что первый сервер принимает VIP. Финальный тест — оставить ICMP доступным, временно заблокировать именно protocol 112 и проверить, что мониторинг действительно обнаружит dual-active.

На сентябрь 2026 года актуальный upstream-релиз Keepalived — 2.4.3 (тег от 20 июля 2026 года). Это не означает, что любой стабильный дистрибутив надо немедленно переводить на самосборный пакет: версия 2.2.7 в Debian 12 остаётся нормальной для описанной базовой схемы. Но функции и синтаксис всегда сверяйте с локальным keepalived --version и man keepalived.conf именно установленного пакета — например, auth_hmac появился только в 2.4.0 и в 2.2.7 его нет. Если используете auth_hmac, учитывайте изменения 2.4.3: там исправлено восстановление счётчика последовательности после коррекции часов и истечение отметок anti-replay, а при anti_replay time узлам нужен синхронный NTP — иначе сосед отбрасывает объявления, и пара снова уходит в dual-active.

В мониторинге мне важны не только доступность VIP и статус systemd. Я собираю состояние роли с обоих узлов, наличие VIP, переходы MASTER/BACKUP/FAULT и отсутствие входящих объявлений дольше расчётного интервала. Алерт «оба MASTER» должен быть отдельным и громким. На проверку обычного ping можно почти забить: как дополнительный сигнал он полезен, но для здоровья VRRP недостаточен.

Работа завершена только после двустороннего failover-теста. Один снимок `ip address`, сделанный сразу после перезапуска, ничего не говорит о поведении через четыре секунды или после возврата узла.
Порядок действий: Как принять работу, а не просто увидеть один MASTER — схема
Порядок действий: Как принять работу, а не просто увидеть один MASTER. Открыть схему в полном размере

Частые вопросы

Почему Keepalived не использует ping второго узла как heartbeat?

Потому что выбор MASTER выполняется по VRRP Advertisement, а не по ICMP. Узел может отвечать на ping и одновременно не получать IP protocol 112.

Поможет ли `nopreempt`, если VRRP-пакеты заблокированы?

Нет. `nopreempt` запрещает высокоприоритетному BACKUP отбирать роль у доступного MASTER, но для этого его объявления сначала надо получить. Без связи оба узла по таймеру становятся MASTER.

Какой порт нужно открыть для VRRP?

Никакой. VRRP использует отдельный IP-протокол 112. Для multicast IPv4 стандартное назначение — `224.0.0.18`; в unicast Keepalived назначением служит адрес peer.

Почему tcpdump видит объявления, а Keepalived остаётся MASTER?

Пакет может быть захвачен на интерфейсе, а затем отброшен firewall или самим Keepalived из-за VRID, версии, TTL, checksum либо неверного unicast-источника. Сопоставьте захват с журналом и ruleset.

Стоит ли всегда переходить с multicast на unicast?

Нет. Для управляемой пары VM я выбираю unicast из-за явных peer и простой фильтрации. В стандартной физической LAN multicast короче и обеспечивает совместимость с другими VRRP-реализациями.

Можно ли настроить оба узла с `state MASTER`?

Технически начальное состояние допустимо, но для схемы с `nopreempt` документация требует старт не из MASTER. Я задаю BACKUP обоим, а победителя определяю разными priority.

Может ли split-brain быть вызван разными virtual_router_id или паролем?

Да. При разных `virtual_router_id` узлы участвуют в разных виртуальных роутерах и каждый становится MASTER своего экземпляра. При несовпадении `auth_pass` или ключей `auth_hmac` объявления соседа отбрасываются. Сверяю эти параметры в конфигурации обоих узлов и ищу в журнале сообщения об ошибке аутентификации.

Столкнулись с похожей задачей? Обращайтесь — решим

Если у вас происходит что-то из описанного в этой статье — или любая другая проблема с ИТ-инфраструктурой, — обращайтесь в любое время. Мои специалисты и я лично разберём ситуацию, найдём настоящую причину и доведём до решения.

Возьмёмся и за разовую задачу, и за постоянное обслуживание. Первичная консультация — бесплатно и без обязательств.

📞 +7 903 729-62-41 💬 MAX: +7 903 729-62-41 ✈ Telegram @ITfresh_Boss

С уважением, Семёнов Евгений Сергеевич, директор «АйТи Фреш» — IT-аутсорсинг для компаний до 50 рабочих мест, 15+ лет практики

Источники

© ООО «АйТи-Фреш» · Москва · Все статьи