From f34d2edefaeb7a28366b05abd9a2fb246ad19831 Mon Sep 17 00:00:00 2001 From: ayurishchev Date: Tue, 18 Aug 2026 13:10:08 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D1=86=D0=B5=D0=BD=D0=BA=D0=B0=20=D0=BF?= =?UTF-8?q?=D0=B5=D1=80=D0=B5=D1=85=D0=BE=D0=B4=D0=B0=20=D0=BF=D1=83=D0=B1?= =?UTF-8?q?=D0=BB=D0=B8=D1=87=D0=BD=D0=BE=D0=B9=20=D0=B1=D0=B0=D0=BB=D0=B0?= =?UTF-8?q?=D0=BD=D1=81=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=B8=20=D0=BD=D0=B0?= =?UTF-8?q?=20=D1=81=D1=85=D0=B5=D0=BC=D1=83=20=D0=B1=D0=B5=D0=B7=20conntr?= =?UTF-8?q?ack?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Зафиксирован разбор шага 4: осуществимость замены ct(commit,nat) / ct(nat) на публичном пути статическими правилами DNAT/un-DNAT без состояния, при сохранении требования "бэкенд видит реальный IP клиента, клиент получает корректный ответ от VIP". Ключевые находки: - условие периметра — бэкенды не должны быть достижимы клиентами напрямую, иначе ответ на прямой запрос неотличим от ответа на сессию через VIP по заголовкам; - связность be<->be между разными сегментами сохраняется правилом nw_dst=$PUB_NET в un-DNAT; связность внутри одного сегмента (т.24, шаг 3) требует отдельного решения и не входит в объём public-only оценки; - установленные сессии перестают быть защищены ct при смене состава пула — принимаемый компромисс, ранее предсказанный в STEP2_SUMMARY; - доставка до физического сервера по MAC в EVPN-фабрике — отдельный, не связанный с conntrack вопрос модели adjacency (табл. 21). Это оценка осуществимости, не план внедрения и не реализация. Co-Authored-By: Claude Opus 5 --- README.md | 1 + docs/STEP4_PUBLIC_STATELESS_ASSESSMENT.md | 210 ++++++++++++++++++++++ 2 files changed, 211 insertions(+) create mode 100644 docs/STEP4_PUBLIC_STATELESS_ASSESSMENT.md diff --git a/README.md b/README.md index 63c4417..aa05dab 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ | — | Расширение пула до четырёх бэкендов | [план](docs/CHANGE_POOL_4_MEMBERS_PLAN.md) | [итоги](docs/CHANGE_POOL_4_MEMBERS_SUMMARY.md) | | 3 | Листенер в приватном сегменте, коммутация сегментов | [план](docs/STEP3_IMPLEMENTATION_PLAN.md) | [итоги](docs/STEP3_SUMMARY.md) | | — | Разделение документации по потокам данных | [план](docs/CHANGE_SPLIT_DATAFLOW_DOCS_PLAN.md) | [итоги](docs/CHANGE_SPLIT_DATAFLOW_DOCS_SUMMARY.md) | +| 4 | Публичная балансировка без conntrack | — | [оценка осуществимости](docs/STEP4_PUBLIC_STATELESS_ASSESSMENT.md) | Как устроена раскладка слотов — [docs/MAGLEV_SLOT_TABLE.md](docs/MAGLEV_SLOT_TABLE.md) (справка по `hc/maglev.go` для разработки). Что нужно изменить для перевода ноды diff --git a/docs/STEP4_PUBLIC_STATELESS_ASSESSMENT.md b/docs/STEP4_PUBLIC_STATELESS_ASSESSMENT.md new file mode 100644 index 0000000..ba4456a --- /dev/null +++ b/docs/STEP4_PUBLIC_STATELESS_ASSESSMENT.md @@ -0,0 +1,210 @@ +# Шаг 4 — оценка: публичная балансировка без conntrack + +**Дата:** 2026-08-18 +**Статус:** оценка осуществимости (не план внедрения и не реализация) +**Область:** только публичный листенер `192.168.5.21:80` +**Источник:** [PUBLIC_LB_DATAFLOW.md](PUBLIC_LB_DATAFLOW.md), `lb/pipeline.sh` +(таблицы 12, 15, 16) +**Смежные документы:** [PRIVATE_LB_DATAFLOW.md](PRIVATE_LB_DATAFLOW.md), +[MAGLEV_SLOT_TABLE.md](MAGLEV_SLOT_TABLE.md) + +## Задача + +Оценить переход публичного пути с текущей stateful-схемы (`ct(commit,nat)` на +прямом пути, `ct(nat)` на обратном) на схему без conntrack. Главное условие, +заданное на входе: **бэкенд обязан видеть реальный IP клиента** — адрес, с +которым клиент обратился на балансировщик, до всех преобразований. + +Уточнение по ходу обсуждения: клиент **всегда** обращается на адрес +балансировщика (VIP), не на бэкенд напрямую. Он должен получить ответ, который +его TCP-стек и приложение примут как продолжение своего же соединения — то есть +`src` ответа обязан остаться `VIP:80`. + +## Вывод + +Технически осуществимо для нового трафика через публичный листенер, при двух +явных условиях периметра и одном принимаемом изменении поведения: + +1. бэкенды не должны быть достижимы клиентами напрямую, в обход VIP (условие + периметра, не техническое ограничение датапаса); +2. правило обратной трансляции обязано быть сужено по адресу назначения, + иначе оно ломает трафик между бэкендами; +3. установленные сессии перестают быть защищены от смены состава пула — это + принимаемый компромисс, а не дефект. + +## 1. Что меняется в датапасе + +| Таблица | Сейчас | Предлагается | +|---|---|---| +| 12 (прямой DNAT) | `ct(commit, zone=1, nat(dst=member:8080))` | `mod_nw_dst:, mod_tp_dst:` — без `ct` вообще | +| 15 (обратный путь) | одно правило `tcp actions=ct(table=16,zone=1,nat)` на весь трафик | по одному правилу на каждого члена: `tcp,nw_src=,tp_src=,nw_dst=$PUB_NET actions=mod_nw_src:$VIP,mod_tp_src:$VIP_PORT,goto_table:16` | +| 16 | пост-`ct` — счётчики | не нужна для этого пути; сохраняется для транзита, где `ct` ещё используется | + +`src` клиента в т.12 не трогается ни сейчас, ни в новой схеме — сохранение IP +клиента никогда не зависело от `ct`, поэтому по этой части поведение не +меняется вообще. + +Обоснование замены в т.15 — §2.2 дизайна v1: DNAT переписывает адрес назначения +в фиксированный `member_ip:member_port`, а не в случайный порт, как SNAT. +Поэтому пара `(member_ip, member_port, proto)` однозначно определяет +`(VIP, listener_port)`, и обратную трансляцию можно сделать статическим +правилом без какого-либо состояния. + +## 2. Условие периметра: прямой доступ к бэкенду должен быть закрыт + +Таблица 1 отправляет в т.15 весь трафик с `dl_dst = MAC шлюза сегмента`, не +различая, был ли пакет частью балансируемой сессии. Сегодняшний стенд умеет и +поддерживает прямой доступ клиента к бэкенду в обход VIP (`table=10, +priority=100,ip actions=goto_table:20`, проверяется в `verify.sh` секция 5 и +`MANUAL_TEST_PLAN` блок C.2) — ответ на такое обращение и ответ на сессию через +VIP дают на выходе бэкенда идентичные заголовки (`src=member_ip:8080, +dst=client_ip:client_port`), различить их без состояния невозможно. + +При заданном требовании — клиент всегда обращается на VIP — эта коллизия не +относится к реальному клиентскому трафику. Она становится значимой только если +прямой доступ к порту бэкенда остаётся разрешён одновременно со stateless-схемой. +Вывод: **предпосылка корректности схемы — бэкенды недостижимы клиентами +напрямую**, что и так является типовой практикой для облачных LB (изоляция +периметра, security groups), а не новым ограничением, придуманным ради этого +перехода. + +## 3. Связность между бэкендами + +Уточняющий вопрос по ходу оценки: не потеряют ли бэкенды способность общаться +друг с другом. Ответ разделяется по топологии. + +**Бэкенды в разных сегментах.** Ответ `be1 → be2:8080` (транзит между +сегментами) идёт через ту же таблицу 15, что и обратный трафик клиентских +сессий, потому что оба используют один и тот же MAC шлюза сегмента как +классификатор. Правило `nw_dst=$PUB_NET` в предложенном un-DNAT решает это: +адресные пространства клиентов (`192.168.5.0/24`) и бэкендов (приватные +сегменты) не пересекаются, поэтому транзитный трафик просто не попадает под +условие правила и проходит немодифицированным. **Связность между сегментами +сохраняется**, если это условие включено в правило. + +**Бэкенды в одном сегменте.** Это территория приватного пути (таблица 24, +шаг 3), не публичного, и в этой оценке не менялась — она по-прежнему на `ct`. +Если в будущем этот путь тоже переводить на stateless-схему, префиксный +критерий не сработает: в шаге 3 клиент и бэкенды намеренно живут в одной +подсети, поэтому ответ `be3 → be1` и ответ `be3 → реальный клиент cli2` +неотличимы по заголовкам. Решение для этого случая другое — не диапазон +адресов, а явный список исключений (для каждого члена сегмента: «если +назначение — адрес другого известного члена этого же сегмента, не +транслировать»), которые должны иметь приоритет выше общего правила un-DNAT. +Список членов конечен и известен заранее, в отличие от адресов произвольных +клиентов, поэтому это решаемо, но требует отдельного набора правил и не входит +в объём этой оценки (она ограничена публичным путём). + +## 4. Изменение поведения: установленные сессии больше не защищены + +Сейчас `ct(commit)` закрепляет привязку за соединением на весь срок его жизни +независимо от таблицы слотов — это подтверждено в `STEP2_SUMMARY.md`: сессия, +обслуживаемая выведенным из пула членом, не рвётся (`verify.sh` секция 13, +`MANUAL_TEST_PLAN` блок I). + +Без `ct` трансляция каждого пакета определяется исключительно текущим +состоянием таблицы слотов. При выводе члена из пула следующий пакет уже +установленной к нему сессии уйдёт на другого члена — без контекста +TCP-соединения, и сессия оборвётся. Минимальное возмущение Maglev ограничивает +**долю** пострадавших сессий (переезжают только слоты выбывшего члена, у +остальных ничего не меняется), но не защищает сами эти сессии. + +Это ровно тот компромисс, который был предсказан заранее в `STEP2_SUMMARY.md`: +«ценность таблицы слотов раскроется при переходе на stateless-датапас, где +единственной защитой от переезда сессий останется минимальное возмущение +раскладки». Переход на публичном пути — первое место, где это предсказание +становится проверяемым поведением, а не наблюдением. + +Практическое следствие: тест «сессия переживает дренаж» (`verify.sh` секция 13) +для публичного пути перестаёт быть верным и требует пересмотра или явного +исключения из проверок при внедрении. + +## 5. Узкое остаточное ограничение (из дизайна v1) + +Если сам бэкенд когда-либо инициирует исходящее TCP-соединение с src-портом, +равным его собственному порту прослушивания (8080), и адресует его в +клиентскую сеть — такой пакет попадёт под правило un-DNAT и будет неверно +переписан. Маловероятный случай (обычно исходящие соединения используют +эфемерный порт источника), уже документирован в дизайне v1 (§3.2, «остаточное +ограничение», §15.4). Переход на stateless ничего не меняет в этом отношении — +ограничение существовало бы в любой чисто заголовочной схеме un-DNAT. + +## 6. Что выигрывается + +- `ct` и зависимость от `CT_ZONE` уходят из публичного пути целиком — оба + направления становятся чистым `match + rewrite`. +- Меньше recirculation в датапасе: на момент оценки в датапасе стенда + зафиксировано 6 потоков с `ct(...)`, каждый вызывает `recirc`; на публичном + пути они исчезают. +- Снимается ограничение, зафиксированное в `PUBLIC_LB_DATAFLOW.md` (раздел 9): + «прямой и обратный трафик сессии обязаны проходить через один узел». Это + предпосылка, без которой Active/Active ECMP между несколькими узлами LB не + имеет смысла обсуждать — устраняется она именно этим шагом. + +## 7. Что не меняется + +Маршрутизация (таблицы 20/21), таблица слотов (таблица 11), алгоритм Maglev, +детерминизм `multipath` — не затрагиваются вообще. Ограничения по ICMP-ошибкам +и фрагментации сохраняются в прежнем виде — они не были связаны с `ct` и +раньше. + +## 8. Отдельный вопрос: доставка до физического сервера в EVPN-фабрике + +Поднят по ходу обсуждения, зафиксирован здесь как связанный, но не +пересекающийся с темой `ct`. + +Вопрос: если нода HPNN подключена к EVPN-фабрике, и MAC сетевого порта +физического сервера-бэкенда доходит до HPNN через фабрику — будет ли трафик +доставлен до сервера? + +**Ответ: это не зависит от наличия или отсутствия `ct`.** Трансляция адреса +(таблицы 12/15) и доставка кадра до правильного MAC (таблица 21, adjacency) — +независимые механизмы. `ct` в доставке кадра не участвует ни сейчас, ни в +предложенной схеме. + +Реальная зависимость — в существующей модели adjacency: MAC членов пула сейчас +**статически** прописаны в `topology.env` и заливаются в таблицу 21 константами +(`mod_dl_dst:$BE1_MAC`). Это уже задокументированное ограничение — +`STEP1_SUMMARY.md`: «MAC бэкендов прописаны статически. ARP-резолвер next-hop +отсутствует». Дизайн v1 предполагал агента, резолвящего MAC next-hop через +packet-in/packet-out по своей neigh-таблице (§4.3, §8.2), но это не +реализовано ни в стенде, ни где-либо ещё. + +То, что MAC физического порта доходит до HPNN по EVPN-фабрике, — свойство самой +фабрики (leaf-коммутаторы разучивают MAC локально, публикуют по EVPN Type-2 +остальным VTEP); HPNN в этом процессе не участвует, для него это просто кадры +на порту аплинка. Вопрос не в том, дойдёт ли MAC до HPNN (дойдёт), а в том, +знает ли пайплайн HPNN, какой MAC подставить в `mod_dl_dst`, когда решает +отправить пакет этому серверу. При текущей статической модели — знает только +то, что вручную прописано заранее. Если реальный MAC карты сервера не совпадает +с прописанным (замена карты, переключение LACP/бондинга на другой физический +линк, сервер ещё не был известен на момент конфигурации) — таблица 21 либо +отправит кадр не туда, либо, если совпадения нет вовсе, попадёт под +`priority=0 actions=drop`: трафик до сервера не дойдёт, несмотря на то что +фабрика прекрасно знает его настоящий MAC. + +Это существующий пробел архитектуры, не имеющий отношения к переходу на схему +без conntrack — он не блокирует и не усложняет этот шаг. Требует отдельной +оценки: динамический ARP-резолвер next-hop (как и предполагал дизайн v1) либо +интеграция с тем, как EVPN-фабрика публикует MAC-привязки, чтобы control plane +HPNN получал их не вручную, а из фабрики. + +## 9. Итоговая сводка условий и рисков + +| # | Пункт | Тип | Статус | +|---|---|---|---| +| 1 | Бэкенды недостижимы клиентами напрямую, только через VIP | предпосылка периметра | требует явного решения при внедрении | +| 2 | Правило un-DNAT сужено `nw_dst=$PUB_NET` | техническое условие | зафиксировано в предложенной схеме | +| 3 | Связность между сегментами (be↔be, разные сегменты) | проверено | сохраняется при условии (2) | +| 4 | Связность внутри сегмента (be↔be, один сегмент) | вне объёма этой оценки | не тронуто (путь на `ct`); при переносе на stateless требует отдельного набора исключений | +| 5 | Установленные сессии при смене состава пула | изменение поведения | принимаемый компромисс, требует пересмотра теста `verify.sh` §13 | +| 6 | Исходящий трафик бэкенда с src-портом = 8080 в клиентскую сеть | остаточное ограничение | узкое, уже документировано в дизайне v1 | +| 7 | Доставка до физического сервера по MAC (EVPN) | отдельный вопрос | не связан с `ct`; существующий пробел модели adjacency | + +## 10. Что дальше + +Это оценка осуществимости, не план внедрения. Следующий шаг, если решение +принято, — оформить отдельный план и итоги по конвенции репозитория +(`STEP4_IMPLEMENTATION_PLAN.md` / `STEP4_SUMMARY.md`), с явной фиксацией +решений по пунктам 1 и 5 таблицы выше как условий приёмки, и отдельной оценкой +пункта 7 при планировании физического окружения.