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>
148 lines
10 KiB
Markdown
148 lines
10 KiB
Markdown
# Шаг 5 — план внедрения: динамическое изучение MAC/ARP для бэкендов
|
||
|
||
**Дата:** 2026-08-18
|
||
**Предыдущий шаг:** [STEP3_SUMMARY.md](STEP3_SUMMARY.md) (реализован).
|
||
**Не путать с шагом 4** ([STEP4_PUBLIC_STATELESS_ASSESSMENT.md](STEP4_PUBLIC_STATELESS_ASSESSMENT.md))
|
||
— это только оценка перехода публичного пути на схему без conntrack, ещё не
|
||
реализована; данный шаг с ней не пересекается.
|
||
**Итоги:** [STEP5_SUMMARY.md](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 trace` same-segment пути — обновить ожидаемую цепочку (`12 → 21`
|
||
вместо `12 → 25`).
|
||
|
||
### Документация
|
||
|
||
`docs/PUBLIC_LB_DATAFLOW.md`, `docs/PRIVATE_LB_DATAFLOW.md` — таблица 21:
|
||
«статические MAC» → «резолвятся `hcd` через ядро». Снять пункт «MAC бэкендов
|
||
прописаны статически» из ограничений `STEP1_SUMMARY.md`. `README.md`.
|
||
|
||
## Порядок работ
|
||
|
||
1. `docs/STEP5_IMPLEMENTATION_PLAN.md` (этот документ).
|
||
2. ~~Спайк `ip -json neigh`~~ — выполнено, подтверждено на живом стенде.
|
||
3. `lb/entrypoint.sh` — снять статические `ip neigh permanent`; проверить ARP
|
||
вручную.
|
||
4. `hc/neigh.go` + правки `hc/main.go`.
|
||
5. `lb/pipeline.sh` — таблица 21 как единственный источник MAC.
|
||
6. `lb/hc-config.sh`, `lb/topology.env`.
|
||
7. `lb/lbctl.sh` — команда `neigh`.
|
||
8. Ручная проверка, затем `scripts/verify.sh` и `MANUAL_TEST_PLAN.md`.
|
||
9. `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 пути** — требует правки ожидаемых
|
||
трасс в документации.
|