MAC каждого члена пула больше не статическая константа в topology.env, а резолвится демоном hcd через обычный ARP ядра — в реальном окружении MAC бэкенда заранее не известен (сервер ещё не подключён, NIC может замениться), топология не описывается статически, в отличие от стенда. hcif-порты (единственные адреса узла в ядре) уже были настоящими L3-интерфейсами в тех же сегментах, что и бэкенды — единственное, что мешало обычному ARP, это permanent-записи ip neigh в entrypoint.sh. Убрав их и добавив hc/neigh.go (читает ip -json neigh show, точечно заливает бандл в таблицу 21 на том же тикере, что и health-пробы), получили резолвер без нового OpenFlow-контроллера. Таблица 21 стала единственным источником MAC для обоих путей — маршрутизируемого и коммутируемого (шаг 3): таблица 12 больше не дублирует MAC инлайново, ct(commit) ведёт сразу в таблицу 21. Исправлен попутно найденный баг: после apply.sh (replace-flows) таблица 21 не восстанавливалась, поскольку syncNeighbors сравнивал MAC с памятью демона, а не с датапасом. Добавлен force-режим по аналогии с Agent.apply(), плюс регрессионная проверка в verify.sh. Проверено на живом стенде: MAC всех четырёх членов резолвлен и совпадает с реальными интерфейсами; смена MAC "железа" обнаружена и применена без вмешательства за счёт штатного старения ARP ядра. Регрессия: 94 из 94 проверок. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
10 KiB
Шаг 5 — план внедрения: динамическое изучение MAC/ARP для бэкендов
Дата: 2026-08-18 Предыдущий шаг: STEP3_SUMMARY.md (реализован). Не путать с шагом 4 (STEP4_PUBLIC_STATELESS_ASSESSMENT.md) — это только оценка перехода публичного пути на схему без conntrack, ещё не реализована; данный шаг с ней не пересекается. Итоги: STEP5_SUMMARY.md
Задача
Сейчас MAC каждого члена пула — статическая константа (BE1_MAC…BE4_MAC в
lb/topology.env), продублированная в четырёх независимых местах: правило
mod_dl_dst в таблице 21 (routed-путь), инлайновый mod_dl_dst в таблице 12
(same-segment/коммутируемый путь шага 3), permanent-запись ip neigh в ядре
узла (lb/entrypoint.sh, для health-проб) и реальный MAC интерфейса ВМ
(scripts/attach-segment.sh). Все четыре обязаны совпадать вручную.
В реальном окружении MAC бэкенда заранее не известен: топология сети, в
отличие от стенда, не описывается статически. Уже задокументированный пробел
(STEP1_SUMMARY.md, известные ограничения: «MAC бэкендов прописаны статически.
ARP-резолвер next-hop отсутствует»), отдельно всплывший в оценке шага 4 (раздел
8, доставка до физического сервера в EVPN-фабрике).
Нужен компонент, изучающий MAC членов пула динамически, без предварительного описания в конфигурации.
Объём: изучаются MAC только для адресов, уже известных из конфигурации
(member IP объявлен в hc-config.sh); обнаружение новых, необъявленных хостов
— вне рамок (отдельная задача control plane, уже отложена в MULTITENANCY.md).
Логика живёт в демоне hcd — расширение существующего единственного
control-plane процесса на ноде.
Ключевая находка
Internal-порты hcif-p2/hcif-p3 — единственные адреса узла, живущие в ядре
Linux, — уже настоящие L3-интерфейсы в том же сегменте, что и бэкенды.
Ядро уже способно резолвить их MAC обычным ARP; единственное препятствие —
статические ip neigh ... nud permanent записи в lb/entrypoint.sh:99,
которые ARP для этих адресов отключают навсегда. Подтверждено на живом стенде:
ip -json neigh show внутри lb-router сейчас отдаёт четыре записи с
"state":["PERMANENT"] — ровно то, что предстоит заменить на реальный ARP.
Дополнительно: segment_ingress()/l3_learn() в lb/pipeline.sh уже заливает
действием learn записи приоритета 90 в таблицу 21 для любого IP-трафика с
портов сегмента, включая ответы бэкендов на health-пробы — на живом стенде эти
записи присутствуют в dump-flows table=21, но перекрыты статическими
правилами приоритета 100. Задача не строит резолвер с нуля, а: (1) снимает
блокировку ARP ядра, (2) делает hcd явным и наблюдаемым источником MAC вместо
неявного поведения learn, (3) убирает дублирование MAC между таблицами 12 и
21.
Разделение ответственности
| Кто | Адрес | Механизм |
|---|---|---|
| Клиенты | заранее неизвестен, произвольный | как сейчас — learn (таблица 21, приоритет 90), без изменений |
| Члены пула | известен из конфигурации, MAC — нет | новое: hcd резолвит через ядро (ARP на hcif-портах), заливает таблицу 21 приоритетом 100 |
Изменения по файлам
lb/entrypoint.sh
В hc_port() убрать ip neigh replace ... nud permanent для адресов членов
пула (строка 99, вызовы 106-109). MAC самого internal-порта (строка 93)
остаётся статичным — собственный адрес узла, не next-hop.
hc/neigh.go (новый файл)
Работает на тикере health-проб (cfg.interval), без отдельной горутины:
- по
Sourceчлена и новому полюifaceвыполняетip -json neigh show dev <iface> to <member_ip>; - MAC резолвлен при состоянии
REACHABLE/STALE/DELAY/PROBE/PERMANENT; - при изменении MAC — точечный бандл
ovs-ofctl bundle:flow delete table=21,ip,nw_dst=<ip>+flow add table=21,priority=100,ip,nw_dst=<ip> actions=mod_dl_dst:<mac>,output:<port>; - API: поле в
/status, командаlbctl neigh.
lb/pipeline.sh
- Убрать статические
table=21,priority=100,ip,nw_dst=$BE{1..4}_IP actions=mod_dl_dst:$BE{n}_MAC,output:...(строки 333-336). Записи дляHC2_IP/HC3_IPостаются статичными. - Таблица 12, ветка same-segment (приоритет 200): убрать инлайновый
mod_dl_dst:$mmac(строка 284),ct(commit,...)направить вtable=21вместоtable=25. Таблица 21 не декрементирует TTL — свойство «TTL не уменьшается для соседей по сегменту» сохраняется, дублирование MAC между таблицами уходит.
lb/topology.env, lb/hc-config.sh
BE1_MAC…BE4_MAC перестают быть обязательными. Добавляется поле iface на
член пула.
lb/lbctl.sh
Команда neigh: «член / адрес / MAC / состояние / возраст».
scripts/verify.sh, docs/MANUAL_TEST_PLAN.md
- Таблица 21 заполняется динамически в течение нескольких секунд после старта.
lbctl neighпоказывает MAC, совпадающий с реальным MAC интерфейса ВМ.- Смена MAC у
be1обнаруживаетсяhcdбез разрыва доставки. - Регрессия: все существующие проверки (оба листенера, транзит, health-check,
fail-close, дренаж, идемпотентность
apply.sh) без изменений в поведении. lbctl tracesame-segment пути — обновить ожидаемую цепочку (12 → 21вместо12 → 25).
Документация
docs/PUBLIC_LB_DATAFLOW.md, docs/PRIVATE_LB_DATAFLOW.md — таблица 21:
«статические MAC» → «резолвятся hcd через ядро». Снять пункт «MAC бэкендов
прописаны статически» из ограничений STEP1_SUMMARY.md. README.md.
Порядок работ
docs/STEP5_IMPLEMENTATION_PLAN.md(этот документ).Спайк— выполнено, подтверждено на живом стенде.ip -json neighlb/entrypoint.sh— снять статическиеip neigh permanent; проверить ARP вручную.hc/neigh.go+ правкиhc/main.go.lb/pipeline.sh— таблица 21 как единственный источник MAC.lb/hc-config.sh,lb/topology.env.lb/lbctl.sh— командаneigh.- Ручная проверка, затем
scripts/verify.shиMANUAL_TEST_PLAN.md. docs/STEP5_SUMMARY.md, README, диаграммы потоков.
Критерии приёмки
lbctl neighпоказывает реальный MAC каждого члена, совпадающий сip link show eth0внутри соответствующего контейнера;- в
topology.envнет обязательныхBE*_MAC; - смена MAC у бэкенда обнаруживается и трафик продолжает доставляться без ручного вмешательства;
- все существующие проверки (
make verify) проходят без изменений в наблюдаемом поведении.
Риски
- Окно на холодном старте таблицы 21 для члена, пока
hcdне сделал первый резолв — по порядку величины совпадает с существующим fail-close таблицы слотов, новой категории риска не вводит. - Обнаружение новых, необъявленных хостов вне объёма — осознанно отложено
к control plane (
MULTITENANCY.md). - Изменение цепочки таблиц same-segment пути — требует правки ожидаемых трасс в документации.