Линк UP, а сети нет: настраиваем переключение bond active-backup
Сервер с двумя сетевыми портами и bond0 в режиме active-backup может остаться без связи при полностью исправном резерве. Обычно дело не в драйвере, а в том, какой именно признак мы назвали «живым линком». Я разберу реальный инцидент, покажу рабочие настройки для Debian 12, объясню `arp_validate`, VLAN и ограничения ARP-монитора, а в конце дам сценарий проверки, который действительно воспроизводит тихий отказ.
Линк UP — это ещё не работающая сеть
Этот сценарий я разбирал у разных клиентов не раз. Сервер собран с двумя сетевыми портами, bond0 работает в режиме active-backup, порты разведены по двум коммутаторам. Ночью на активном пути меняют конфигурацию: из транка исчезает нужный VLAN, порт остаётся в состоянии forwarding, но кадры нужного сегмента больше не проходят, либо блокируется аплинк коммутатора. Кабель на месте, физическая несущая есть, а сервер недоступен. Резервный порт исправен, однако bond не видит основания для переключения.
Параметр miimon задаёт интервал проверки состояния линка в миллисекундах. В ядре Linux 6.1 значение 100 прямо названо хорошей отправной точкой. По умолчанию use_carrier=1, поэтому драйвер bonding получает состояние через netif_carrier_ok(), а его, в свою очередь, поддерживает драйвер сетевой карты. В той же версии ядра ещё допустимо use_carrier=0, чтобы вернуться к MII или ETHTOOL ioctl. Для нашей задачи это различие ничего не меняет: обе проверки говорят о локальном состоянии физического линка, а не о доступности конкретного VLAN или узла.
Отсюда и граница возможностей. miimon хорошо обнаруживает выдернутый патч-корд, погасший SFP, административно выключенный порт, потерю сигнала и другие события, после которых драйвер сетевой карты снимает carrier. Он не обязан увидеть ошибку списка разрешённых VLAN, фильтрацию нужных кадров, STP-состояние на удалённом участке или обрыв аплинка за первым коммутатором, если порт сервера продолжает держать несущую. В документации ядра для многокоммутаторной схемы отдельно сказано: при отказе удалённого конца соединения MII-монитор не имеет прямого способа его обнаружить.
Важно не превращать это правило в лозунг «miimon видит только выдернутый кабель». Например, err-disable на многих коммутаторах физически выключает порт, и тогда carrier пропадёт — такой отказ miimon как раз поймает. Результат зависит от сетевой карты, оптики и поведения конкретного коммутатора. Точная формулировка такая: MII-монитор видит то, что базовый драйвер сообщил как потерю линка; состояние сервиса дальше по тракту он не проверяет.
ARP-монитор отвечает на другой вопрос. Драйвер периодически посылает ARP-запросы к выбранным IPv4-узлам локальной сети и учитывает ответный или иной релевантный трафик по правилам выбранного режима. Это уже проверка прохождения кадров до реального соседа, поэтому исчезнувший VLAN на активном порту способен привести к переводу этого порта в down внутри bond. Но и ARP — не сквозной тест приложения: доступность ARP-цели не доказывает, что работает DNS, маршрутизация за шлюзом, TCP-порт базы или обратный путь клиента. Монитор надо проектировать под конкретную границу отказа.
Перед настройкой я всегда формулирую проверяемое условие словами: «активный порт должен переносить кадры VLAN 210 до двух стабильных соседей». Такая фраза сразу показывает, почему одного carrier недостаточно, какие цели подходят и какой тест нужен. Если требование звучит как «сервер должен открываться из филиала», одного bonding ARP monitor уже мало — это задача внешнего мониторинга сервиса.
- нужный VLAN удалён из списка разрешённых на транке, хотя физический порт остаётся поднятым;
- порт попал не в тот access-VLAN, но продолжает передавать кадры другого сегмента;
- STP заблокировал удалённый участок пути, не сняв carrier на порту сервера;
- ACL, port-security или storm-control отбрасывает именно ARP либо весь нужный трафик, сохраняя несущую;
- коммутатор доступа работает, но его аплинк к остальной части сегмента отказал;
- промежуточное устройство удерживает линк со стороны сервера при неисправности дальнего участка.
Разбор из практики: рекламное агентство «Пиксель», 20 рабочих мест
Клиент — рекламное агентство «Пиксель», 20 рабочих мест. У агентства своя стойка в ЦОД: три физических сервера на Debian 12 с ядром Linux 6.1 и два гипервизора. На каждом физическом сервере собран bond0 из enp1s0f0 и enp1s0f1: режим active-backup, miimon=100, downdelay=200, updelay=200, первичным назначен enp1s0f0. Порты подключены к двум независимым коммутаторам доступа, между которыми есть межсоединение. Адреса серверов находятся на теговом интерфейсе bond0.210, серверный VLAN — 210. Схема два года не вызывала вопросов, потому что проверяли её только отключением кабеля.
Во время ночного обновления прошивки первый коммутатор перезагрузили. После загрузки транк к enp1s0f0 сервера базы поднялся, но шаблон разрешённых VLAN применился без VLAN 210. Физический порт показывал up, служебные кадры шли, а трафик серверного сегмента — нет. База оставалась недоступной 41 минуту, пока дежурный не сверил конфигурацию коммутатора. Резервный enp1s0f1 всё это время был исправен, но для MII-монитора оба порта выглядели одинаково живыми.
Содержимое /proc/net/bonding/bond0 в момент инцидента не указывало на потерю carrier. Формат этого файла зависит от версии драйвера, поэтому его нельзя парсить как неизменный API, но для оперативной диагностики он очень удобен:
Bonding Mode: fault-tolerance (active-backup)
Primary Slave: enp1s0f0 (primary_reselect always)
Currently Active Slave: enp1s0f0
MII Status: up
MII Polling Interval (ms): 100
Slave Interface: enp1s0f0
MII Status: up
Link Failure Count: 0
Slave Interface: enp1s0f1
MII Status: up
Link Failure Count: 0Это было ожидаемое поведение, а не дефект bonding. Несущая на активном интерфейсе не исчезла, поэтому Link Failure Count оставался равным 0. Значения downdelay=200 и updelay=200 тоже не помогали: оба параметра действуют только вместе с miimon и лишь задерживают реакцию на уже обнаруженное изменение carrier. Они не добавляют проверку VLAN и не превращают MII-монитор в проверку связности.
После разбора мы перевели три физических сервера рекламного агентства «Пиксель», 20 рабочих мест, на ARP-мониторинг. Интервал установили 1000 мс, целями выбрали SVI серверного VLAN и отдельный стабильный узел в том же L2-сегменте, включили arp_validate=filter_active, оставили arp_all_targets=any и arp_missed_max=2. miimon, updelay и downdelay убрали. Для возврата на предпочтительный порт задали primary_reselect=failure, чтобы восстановившийся основной линк не отбирал роль у исправно работающего резервного без необходимости.
Проверяли не отключением патч-корда, а удалением VLAN 210 из разрешённого списка на активном порту. Физический линк оставался up, ARP-ответы через него прекращались, после чего bond выбрал enp1s0f1. В этом стенде пауза составила около трёх секунд. Это измеренный результат конкретной схемы, а не гарантированный срок из документации: момент отказа относительно цикла проверки, планирование ядра и состояние резервного пути дают разброс. Прикладные сессии базы пережили паузу благодаря собственным тайм-аутам и повторной передаче TCP.
За следующие девять месяцев монитор дважды пригодился именно на тихих отказах. Сначала при подключении нового оборудования на активном порту ошибочно изменили VLAN. Позже STP заблокировал аплинк одного из коммутаторов, не погасив порт сервера. В обоих случаях переключение заняло секунды вместо десятков минут, а о событии мы узнали из мониторинга bond. Я намеренно не приписываю сюда err-disable: если конкретная модель коммутатора снимает carrier, такой случай обнаружил бы и прежний miimon.
Как выбрать ARP-цели и не ошибиться с VLAN
В Linux 6.1 параметр arp_interval измеряется в миллисекундах; значение 0 отключает ARP-мониторинг. При положительном интервале нужен хотя бы один arp_ip_target. Ядро принимает до 16 IPv4-адресов в обычной точечной записи, а в параметре модуля несколько адресов разделяются запятыми. Для IPv6 предусмотрен отдельный ns_ip6_target, также максимум с 16 адресами; механизм использует Neighbor Solicitation и Neighbor Advertisement, но запускается тем же положительным arp_interval.
Цель должна отвечать непосредственно по ARP на том канальном сегменте, который мы хотим проверить. Адрес в удалённой сети за маршрутизатором не годится: ARP-запрос не маршрутизируется до такого узла. Я предпочитаю две стабильные цели, администрируемые разными компонентами: например, SVI серверного VLAN и физический служебный узел. Две виртуальные машины на одном гипервизоре — слабая пара: одно обслуживание выключит обе и заставит bond переключаться при исправной сети.
Параметр arp_all_targets=any означает, что для признания интерфейса рабочим достаточно доступности любой цели; all требует доступности каждой. Эта настройка влияет на active-backup только при включённой ARP-валидации. С any отказ одной цели не вызывает лишнее переключение, зато полностью исчезнувший путь до сегмента обнаруживается. С all монитор становится чувствителен к обслуживанию любого контрольного узла. Поэтому я начинаю с any, а all выбираю только после формального решения, что потеря любой цели действительно равна отказу канала.
Исходное опасение насчёт bond0.210 требует важной поправки. В ядре 6.1 ARP-монитор не обязан отправлять все пробы нетегированными с голого bond0. Драйвер строит маршрут до каждой цели, проверяет путь по верхним сетевым устройствам и собирает VLAN-теги; если маршрут проходит через bond0.210, служебный ARP помечается VLAN 210. В исходном коде bonding это сделано намеренно, потому что коммутаторы в VLAN-режиме могут не пропускать нетегированные кадры.
Значит, адрес только на bond0.210 — нормальная конфигурация. Проблемы начинаются, когда маршрут к цели указывает на другой интерфейс, адрес цели объявлен on-link не в том сегменте, VLAN-устройство не поднято или policy routing выбирает чужую таблицу. При включённом arp_validate ядро также предупреждает, если не находит маршрут к arp_ip_target. Проверяю это до переключения: маршрут должен вести через нужное верхнее устройство, а захват трафика должен показать правильный тег.
Для проверки маршрута и кадров использую короткий набор команд. Захват на физическом порту с флагом -e показывает Ethernet-заголовок и VLAN; на системах с аппаратным VLAN offload тег в захвате на одной точке может быть не виден, поэтому при сомнении дублирую захват на зеркальном порту коммутатора или на цели:
ip route get 10.210.0.1
ip route get 10.210.0.5
tcpdump -eni enp1s0f0 arp
tcpdump -eni enp1s0f1 arpЕщё одна ловушка — сама цель. Плавающий VIP может переехать одновременно с аварией, виртуальная машина может регулярно перезагружаться, а фильтрация на хосте может менять ARP-поведение через параметры arp_ignore и arp_filter. Обычный IP-файрвол не равен фильтру ARP: ARP находится ниже IPv4, поэтому фразу «его блокирует firewall» надо подтверждать конкретным мостовым фильтром, eBPF-программой или настройкой узла. Цели я документирую вместе с владельцем, назначением и окном обслуживания — иначе через год их удалят как «непонятные адреса».
- адрес находится в проверяемом L2-сегменте и разрешается непосредственно через ARP;
- маршрут к адресу проходит через `bond0` или нужный VLAN-интерфейс над ним;
- узел стабилен, контролируется вашей командой и не является краткоживущим VIP;
- две цели не зависят от одного гипервизора, одного процесса обслуживания или одного питания;
- ответы и VLAN-тег подтверждены захватом трафика до изменения боевого bond.
Рабочие конфигурации для Debian 12
Синтаксис зависит не только от ядра, но и от программы, которая передаёт параметры ядру. Это место породило несколько ошибок в исходной конфигурации. Debian 12 поставляется с systemd 252 и NetworkManager ветки 1.42, а пакет ifenslave 2.13 предоставляет свои ifupdown-хуки. Все три оболочки поддерживают не одинаковый набор ключей, хотя в ядре Linux 6.1 сам параметр arp_missed_max уже существует.
Для systemd-networkd 252 в .netdev доступны ARPIntervalSec=, ARPIPTargets=, ARPValidate=, ARPAllTargets=, PrimaryReselectPolicy= и GratuitousARP=. Однако ARPValidate= в этой версии принимает только none, active, backup и all: написание filter-active не существует, а варианты filter_active оболочка systemd 252 не предоставляет. Ключ ARPMissedMax= добавлен только в systemd 256. Поэтому на Debian 12 я ставлю ARPValidate=active, а arp_missed_max оставляю равным документированному значению ядра по умолчанию 2.
# /etc/systemd/network/10-bond0.netdev
[NetDev]
Name=bond0
Kind=bond
[Bond]
Mode=active-backup
ARPIntervalSec=1s
ARPIPTargets=10.210.0.1 10.210.0.5
ARPValidate=active
ARPAllTargets=any
PrimaryReselectPolicy=failure
GratuitousARP=3Сам PrimaryReselectPolicy= не назначает первичный интерфейс. В systemd-networkd это делается параметром PrimarySlave=true в секции [Network] файла физического порта. Второй порт присоединяется к тому же bond без этого признака. Адреса и создание bond0.210 остаются в отдельных файлах .netdev и .network вашей схемы:
# /etc/systemd/network/20-enp1s0f0.network
[Match]
Name=enp1s0f0
[Network]
Bond=bond0
PrimarySlave=true
# /etc/systemd/network/21-enp1s0f1.network
[Match]
Name=enp1s0f1
[Network]
Bond=bond0NetworkManager ветки 1.42 умеет передать filter_active, но ещё не содержит константу для arp_missed_max; она появилась в более новых выпусках. Чтобы не спорить с экранированием запятой внутри словаря bond.options, ниже показываю однозначный фрагмент keyfile. Реальное имя файла профиля можно узнать через nmcli; после ручной правки файл должен принадлежать root и иметь режим 600, затем NetworkManager нужно перечитать профиль. Перед изменением действующего соединения нужен консольный доступ, потому что повторная активация оборвёт текущий путь управления.
nmcli -g connection.filename connection show bond0# /etc/NetworkManager/system-connections/bond0.nmconnection
[bond]
mode=active-backup
arp_interval=1000
arp_ip_target=10.210.0.1,10.210.0.5
arp_validate=filter_active
arp_all_targets=any
primary=enp1s0f0
primary_reselect=failure
num_grat_arp=3chown root:root /etc/NetworkManager/system-connections/bond0.nmconnection
chmod 600 /etc/NetworkManager/system-connections/bond0.nmconnection
nmcli connection reload
nmcli connection up bond0В классическом ifupdown с хуками пакета ifenslave 2.13 поддерживаются bond-arp-interval, bond-arp-ip-target, bond-arp-validate, bond-primary-reselect и bond-num-grat-arp. Директив bond-arp-all-targets и bond-arp-missed-max в списке пакета нет. Их нельзя без проверки переносить из имён sysfs, добавив дефисы. В примере ниже остаются значения ядра по умолчанию: arp_all_targets=any и arp_missed_max=2.
# /etc/network/interfaces
auto bond0
iface bond0 inet manual
bond-slaves enp1s0f0 enp1s0f1
bond-mode active-backup
bond-primary enp1s0f0
bond-primary-reselect failure
bond-arp-interval 1000
bond-arp-ip-target 10.210.0.1 10.210.0.5
bond-arp-validate filter_active
bond-num-grat-arp 3Независимо от оболочки финальная истина находится в sysfs. Путь /sys/class/net/bond0/bonding/ создаёт драйвер ядра, а имена файлов соответствуют параметрам bonding. Команда ниже показывает фактически применённые значения; для целей вывод обычно содержит адреса через пробел. Если нужного файла нет, сначала сверяю версию запущенного ядра, а не версию установленного, но ещё не загруженного пакета.
grep . /sys/class/net/bond0/bonding/{mode,primary,primary_reselect,miimon,arp_interval,arp_ip_target,arp_validate,arp_all_targets,arp_missed_max,num_grat_arp}
cat /proc/net/bonding/bond0После перехода ожидаю miimon равным 0, arp_interval равным 1000, две цели, выбранный режим валидации и arp_missed_max равным 2. Если оболочка не поддерживает нужный параметр, я не подменяю постоянную настройку разовой записью в sysfs без документации: после перезагрузки она исчезнет. Либо оставляю безопасное значение ядра по умолчанию, либо обновляю управляющий компонент и фиксирую минимальную версию в регламенте.
Что на самом деле делают arp_validate и arp_all_targets
Стандартный ARP-монитор без валидации не ограничивается ответами на собственные пробы. По документации он оценивает, передавал или принимал ли интерфейс трафик в последнее время, а ARP-запросы к целям нужны для регулярной генерации такого трафика. В насыщенном сегменте посторонние кадры способны поддерживать видимость активности даже после обрыва пути к контрольной цели. Поэтому утверждение «без валидации считается любой входящий ARP» неточно: без фильтра учитывается и не-ARP-трафик.
У arp_validate в ядре Linux 6.1 семь значений: none (0), active (1), backup (2), all (3), filter (4), filter_active (5) и filter_backup (6). Значения active, backup и all включают проверку релевантности ARP для соответствующих портов. Для активного интерфейса драйвер проверяет ответы от адресов из arp_ip_target; при проверке резервных портов учитываются широковещательные ARP-запросы, отправленные через активный интерфейс. Это помогает отсеять пробы других серверов.
Слово filter имеет более узкий смысл, чем часто пишут в статьях. Фильтрация заставляет монитор учитывать для оценки доступности только входящие ARP-пакеты; любой ARP, независимо от источника и назначения, подходит на стадии фильтра. Она не делает пакет «своим». Комбинация filter_active сначала исключает не-ARP-трафик для всех портов, а затем включает строгую валидацию именно активного. Поэтому этот режим я использую, когда управляющая программа умеет передать числовое значение 5 или имя filter_active.
В systemd-networkd 252 такого имени нет, хотя ядро его поддерживает; актуальная документация systemd также перечисляет только четыре значения. Практичный компромисс — ARPValidate=active: активный порт валидируется по целям, но не-ARP-трафик на резервных интерфейсах не фильтруется тем же способом. Это не повод тайно записывать 5 в конфиг, который обещает только четыре значения. Ограничение надо учитывать в тесте и мониторинге, а если обязательно нужен filter_active, использовать управляющий компонент, который документированно передаёт это значение.
Проверять резервные порты значениями backup, all или filter_backup по умолчанию я не спешу. Документация предупреждает, что некоторые конфигурации коммутаторов не доставляют резервным портам широковещательные ARP-запросы. Тогда исправный резерв будет помечен как нерабочий. Более того, успешная валидация резервного порта не гарантирует, что он заработает после назначения активным; она лишь помогает выбрать более вероятного кандидата. Реальную гарантию даёт только управляемый тест переключения.
arp_all_targets дополняет валидацию целей. При any достаточно хотя бы одной доступной цели, при all нужны все. Здесь важно согласовать смысл двух адресов. Если это два взаимозаменяемых стабильных свидетеля одного и того же L2-пути, any защищает от ложного failover при обслуживании одного узла. Если потеря любой из целей должна считаться отказом именно этого канала, выбирают all, но получают более чувствительную систему. Значение не следует выбирать по принципу «all надёжнее»: оно просто реализует другое условие.
На боевых серверах я начинаю с filter_active плюс any, если оболочка это поддерживает, и с active плюс any в systemd 252. Затем моделирую потерю VLAN, выключение одной цели и восстановление основного пути. Если поведение расходится с замыслом, меняю не случайные числа, а модель: цели, валидацию и точку отказа. Такой подход быстрее, чем неделями лечить ложные переключения увеличением интервалов.
- `arp_validate=none` — нет ни валидации собственных ARP, ни фильтрации не-ARP-трафика;
- `arp_validate=active` — валидируются ARP-пакеты активного интерфейса;
- `arp_validate=filter_active` — для доступности учитывается только ARP, а активный интерфейс дополнительно валидируется;
- `arp_all_targets=any` — достаточно одной доступной цели при включённой валидации;
- `arp_all_targets=all` — каждая указанная цель должна оставаться доступной.
Ограничения, интервалы и поведение после переключения
MII- и ARP-монитор нельзя держать включёнными одновременно. Документация ядра объясняет это ограничением реализации, а код параметров при включении одного механизма отключает другой. Поэтому в итоговой конфигурации miimon должен стать 0. Вместе с ним теряют смысл updelay и downdelay: оба параметра допустимы только для MII-монитора. ARP всё равно обнаружит физический обрыв, потому что ответы прекратятся, но реакция будет определяться ARP-интервалом, а не проверкой carrier каждые 100 мс.
arp_missed_max в Linux 6.1 задаёт число проваленных циклов arp_interval, после которого интерфейс помечается down. Допустимый диапазон — от 1 до 255, значение по умолчанию — 2. Для резервных интерфейсов драйвер разрешает дополнительную неудачную проверку, то есть arp_missed_max + 1, чтобы переключение проходило упорядоченно. Эта дополнительная попытка относится к оценке backup и не даёт права обещать строго три секунды простоя для любой схемы.
При arp_interval=1000 и arp_missed_max=2 активный путь обычно обнаруживается за несколько секунд, но точное время надо измерять. Отказ может случиться сразу после удачной пробы или непосредственно перед следующей; затем нужны обработка состояния, выбор резервного порта и обновление таблиц у соседей. Для рекламного агентства «Пиксель», 20 рабочих мест, мы получили около трёх секунд. В другой топологии результат изменится, поэтому в регламенте я записываю измеренный максимум серии тестов, а не арифметику из двух параметров.
Опускать интервал ниже 1000 мс можно только из измеримого требования. Формального запрета на 500 мс нет, и утверждение о гарантированном вреде для «нагруженного коммутатора» было бы выдумкой. Нагрузка зависит от числа bond, количества целей и возможностей оборудования: 100 серверов с двумя целями и интервалом 500 мс создают порядка 400 исходящих проб в секунду, не считая ответов. Для современного коммутатора это может быть мелочью, а для control-plane старого устройства — заметным фоном. Сначала считаю и измеряю, затем уменьшаю интервал.
Параметр num_grat_arp имеет диапазон от 0 до 255 и значение по умолчанию 1. В active-backup после failover ядро отправляет уведомления для bond и VLAN-интерфейсов над ним, если на них есть адреса. Значение 3 означает три уведомления, но не гарантирует мгновенного ускорения: при peer_notif_delay=0 интервал повторов совпадает с активным интервалом мониторинга. Первый пакет уходит сразу, а последующие служат повторением на случай потери. Поднимать число до 3 я готов только после теста совместимости и захвата трафика; правило «всегда ставить пять» документация не даёт.
primary_reselect=always является значением по умолчанию: восстановившийся primary снова становится активным. При нестабильном основном пути это создаёт лишний failback. Значение failure возвращает primary только тогда, когда текущий активный интерфейс отказал; исключения возникают при отсутствии активных портов и при первоначальном присоединении. Это снижает число переключений, но не лечит флапающий SFP — физическую причину всё равно надо найти и устранить.
ARP-монитор проверяет именно ARP/соседство, поэтому возможны слепые зоны. Если коммутатор пропускает ARP, но отбрасывает только определённый unicast, TCP или MTU-зависимые кадры, цель продолжит отвечать и bond останется up. Если обе цели доступны через один и тот же неисправный компонент, резерв логически не независим. Если порт сервера подключён к двум коммутаторам без согласованного L2-домена, переход MAC может потребовать дополнительной сетевой настройки. Эти случаи закрываются внешними сервисными проверками, мониторингом коммутаторов и полноценным тест-планом, а не ещё одним адресом в списке целей.
Для режима 802.3ad этот рецепт не подходит. В коде bonding ядра 6.1 arp_interval помечен как неподдерживаемый для 802.3ad, balance-tlb и balance-alb. В LACP обычно оставляют miimon для carrier и отдельно контролируют состояние агрегатора и LACP-партнёра. Сам LACP не доказывает доступность VLAN или приложения за партнёром, поэтому тихие отказы там проверяют мониторингом сети и сервисов, а не попыткой добавить параметры active-backup.
Как я внедряю и проверяю ARP-мониторинг
На работающем сервере я начинаю не с редактирования файла, а со снимка фактического состояния. Нужны версия запущенного ядра, сетевой менеджер, активный порт, параметры sysfs, адреса и маршруты. Это сразу показывает расхождение между конфигурацией на диске и тем, что принял драйвер. Команды выполняю из консоли управления или при наличии проверенного out-of-band доступа: перезапуск профиля bond способен оборвать SSH-сеанс.
uname -r
networkctl --version 2>/dev/null || nmcli --version
ip -d link show bond0
ip -br address show bond0
ip -br address show bond0.210
ip route get 10.210.0.1
grep . /sys/class/net/bond0/bonding/*
cat /proc/net/bonding/bond0Дальше вместе с владельцем сети выбираю две цели, сверяю VLAN на обоих коммутаторах и заранее определяю ожидаемое поведение primary после восстановления. Изменение оформляю с окном работ и способом отката. Массово переключать все серверы одним запуском опасно: одна ошибка маршрута или неподходящая цель положит сразу весь кластер. Сначала один узел, затем наблюдение, затем остальные по очереди.
После применения проверяю sysfs и запускаю захват ARP на обоих физических портах. На активном должны быть видны запросы и ответы в нужном сегменте. Поведение backup зависит от режима и сети, поэтому отсутствие на нём тех же ответов само по себе не ошибка. Одновременно наблюдаю текущий активный интерфейс и сообщения ядра:
watch -n 0.5 cat /proc/net/bonding/bond0
journalctl -kfОсновной тест — удалить VLAN 210 из разрешённого списка именно на активном порту, не выключая его физически. Это действие выполняет сетевой администратор на конкретном проверенном интерфейсе коммутатора. Я держу непрерывный ICMP-трафик, прикладную сессию и захват пакетов, записываю момент последнего ответа, смену active slave и первый устойчивый ответ через резерв. Затем возвращаю VLAN и убеждаюсь, что primary_reselect=failure не вызывает немедленного обратного переключения.
Одного удачного прохода мало. Повторяю отказ в обе стороны, отдельно выключаю одну ARP-цель при arp_all_targets=any, перезагружаю цель в согласованном окне и проверяю физический обрыв. Первый тест доказывает обнаружение тихого отказа, второй — отсутствие ложного failover из-за одной цели, третий — устойчивость модели обслуживания, четвёртый — что более медленный ARP-контроль всё же обрабатывает потерю carrier в приемлемое время. Если используются VLAN, хотя бы один прогон сопровождаю зеркалированием порта на коммутаторе.
После внедрения мониторю не только Link Failure Count. Полезны текущий Currently Active Slave, состояние каждого slave, изменение счётчика отказов, отсутствие всех ARP-целей и сообщения bonding в журнале. Смена активного порта — не обязательно авария для пользователя, но это событие для расследования. Если его проигнорировать, система может месяц работать на резерве, а следующий отказ уже окажется одиночным.
Я также ставлю внешний контроль сервиса из другой точки сети. Он отвечает на вопрос, который ARP-монитор принципиально не решает: доступно ли приложение клиентам. Хорошая схема состоит из нескольких уровней — carrier, ARP-соседство, состояние коммутаторов и прикладной запрос. Bond должен быстро локально выбрать рабочий порт; мониторинг должен объяснить, почему это произошло, и заметить проблемы дальше выбранных ARP-целей.
Наконец, фиксирую версионные ограничения рядом с конфигом. Для Debian 12 это особенно важно: читающий свежую онлайн-документацию systemd увидит ARPMissedMax=, добавленное в версии 256, но установленный systemd 252 ключ не применит. Аналогично свежий NetworkManager знает больше параметров, чем ветка 1.42. Рабочий регламент содержит версии ядра и менеджера сети, фактический вывод sysfs, владельцев целей и дату последнего теста — так настройка переживает обновления и смену администраторов.
- сохранить версии и фактическое состояние `/sys/class/net/bond0/bonding/` до изменений;
- выбрать две стабильные цели в проверяемом L2-сегменте и подтвердить маршрут к каждой;
- проверить поддержку каждого ключа установленной версией сетевого менеджера;
- применять настройку по одному серверу с консольным или out-of-band доступом;
- убедиться, что `miimon=0`, цели и режим валидации реально появились в sysfs;
- удалить рабочий VLAN с активного порта при сохранённом carrier и измерить failover;
- вернуть VLAN, проверить политику failback и повторить тест в обратную сторону;
- добавить алерт на смену active slave и внешний прикладной мониторинг.
Частые вопросы
Можно ли одновременно включить miimon и ARP-мониторинг?
Нет. Драйвер bonding не поддерживает одновременную работу этих двух мониторов: включение одного отключает другой. При переходе на ARP проверьте в sysfs, что `miimon` стал равен 0. Параметры `updelay` и `downdelay` относятся только к MII-монитору и в ARP-схеме не задают задержку.
Какой arp_interval выбрать и когда произойдёт failover?
Я начинаю с `arp_interval=1000` мс и значения ядра `arp_missed_max=2`. Обычно это даёт обнаружение за несколько секунд, но фиксированного срока документация не обещает: он зависит от фазы цикла, состояния backup и сетевой топологии. Измерьте серию отказов с сохранённым carrier и зафиксируйте худший результат.
Какие адреса подходят для arp_ip_target?
Один или несколько стабильных IPv4-адресов, которые отвечают непосредственно в проверяемом L2-сегменте. Я использую две цели, не зависящие от одного гипервизора или окна обслуживания. Адрес за маршрутизатором и краткоживущий VIP не подходят. Лимит ядра — 16 целей, но количество само по себе не создаёт независимость.
Работает ли ARP-монитор, если адрес находится на bond0.210?
Да, в ядре Linux 6.1 драйвер строит маршрут к цели, проходит по верхним устройствам и добавляет найденный VLAN-тег к ARP-пробе. Проверьте `ip route get` и захват кадров с Ethernet-заголовком. Ошибка обычно связана не с самим VLAN над bond, а с неверным маршрутом, неподнятым VLAN-интерфейсом или целью вне доступного L2-сегмента.
Почему нельзя скопировать ARPMissedMax в systemd-networkd на Debian 12?
Потому что Debian 12 использует systemd 252, а ключ `ARPMissedMax=` добавлен в systemd 256. Ядро 6.1 уже имеет параметр `arp_missed_max` со значением по умолчанию 2, поэтому отсутствие ключа оболочки не означает отсутствие механизма. Проверяйте версию systemd и фактическое значение в `/sys/class/net/bond0/bonding/arp_missed_max`.
Что выбрать: arp_validate=active или filter_active?
Если менеджер сети поддерживает `filter_active`, этот режим исключает не-ARP-трафик из оценки и валидирует ARP активного порта. NetworkManager 1.42 и ifenslave 2.13 его передают. systemd-networkd 252 принимает только `none`, `active`, `backup` и `all`, поэтому в его конфиге используйте `ARPValidate=active` или другой документированный способ управления bond.
Нужен ли этот ARP-мониторинг для LACP 802.3ad?
Нет: в ядре Linux 6.1 `arp_interval` помечен как неподдерживаемый для режима `802.3ad`. Для LACP контролируйте carrier через `miimon`, состояние агрегатора и LACP-партнёра, а доступность VLAN и приложений проверяйте отдельным сетевым и сервисным мониторингом.
Как корректно испытать active-backup?
Проведите минимум два разных теста. Сначала физически отключите активный линк, затем при поднятом carrier удалите рабочий VLAN с активного порта коммутатора. Наблюдайте `/proc/net/bonding/bond0`, журнал ядра, ARP и прикладной трафик; измерьте failover и после возврата конфигурации проверьте выбранную политику failback.
Источники
- Linux Ethernet Bonding Driver HOWTO, Linux 6.1 — Официальная документация ядра Linux 6.1: параметры `arp_interval`, `arp_ip_target`, `ns_ip6_target`, `arp_validate`, `arp_all_targets`, `arp_missed_max`, `miimon`, `num_grat_arp`, `primary_reselect`, разделы Link Monitoring и ARP Monitor Operation. https://docs.kernel.org/6.1/networking/bonding.html
- systemd.netdev(5), Debian 12 / systemd 252 — Официальная man-страница пакета systemd 252 в Debian bookworm, секция [Bond]: `ARPIntervalSec=`, `ARPIPTargets=`, `ARPValidate=`, `ARPAllTargets=`, `PrimaryReselectPolicy=`, `GratuitousARP=`. https://manpages.debian.org/bookworm/systemd/systemd.netdev.5.en.html
- systemd.network(5), Debian 12 / systemd 252 — Официальная man-страница пакета systemd 252 в Debian bookworm, параметры `Bond=` и `PrimarySlave=` для физических интерфейсов. https://manpages.debian.org/bookworm/systemd/systemd.network.5.en.html
- systemd.netdev, актуальная документация — Официальная документация systemd с пометками версий: `ARPMissedMax=` добавлен в systemd 256; для `ARPValidate=` перечислены `none`, `active`, `backup` и `all`. https://www.freedesktop.org/software/systemd/man/systemd.netdev.html
- NMSettingBond, NetworkManager 1.42.4 — Официальный исходный код NetworkManager 1.42.4 со списком bond options и проверками `arp_ip_target`, `arp_validate`, `arp_all_targets`; в этой версии отсутствует `arp_missed_max`. https://github.com/NetworkManager/NetworkManager/blob/1.42.4/src/libnm-core-impl/nm-setting-bond.c
- nm-settings-keyfile, NetworkManager 1.42.4 — Официальная документация формата keyfile: каталоги профилей, соответствие секций настройкам, правила прав доступа и перечитывание вручную изменённых файлов. https://networkmanager.dev/docs/api/1.42.4/nm-settings-keyfile.html
- ifenslave 2.13 README.Debian — Официальный исходный пакет Debian bookworm, перечень поддерживаемых директив `bond-*` для `/etc/network/interfaces`, включая правила для нескольких `bond-arp-ip-target`. https://sources.debian.org/src/ifenslave/2.13/debian/README.Debian
- bond_main.c, Linux 6.1 — Официальный исходный код ядра: `bond_verify_device_path()`, `bond_handle_vlan()` и `bond_arp_send_all()` показывают выбор маршрута и добавление VLAN-тегов к ARP-пробам. https://github.com/torvalds/linux/blob/v6.1/drivers/net/bonding/bond_main.c
- bond_options.c, Linux 6.1 — Официальный исходный код ядра со значениями и диапазоном `arp_missed_max`, а также ограничениями режимов для `arp_interval` и `arp_validate`. https://github.com/torvalds/linux/blob/v6.1/drivers/net/bonding/bond_options.c
