Добавлены потоки данных
This commit is contained in:
1 parent
10b2247251
commit
afd4f057be
2 files changed
+230
No files matched your search
@@ -15,6 +15,9 @@
|
||||
| 2 | Health-check и таблица слотов | [план](docs/STEP2_IMPLEMENTATION_PLAN.md) | [итоги](docs/STEP2_SUMMARY.md) |
|
||||
| — | Расширение пула до четырёх бэкендов | [план](docs/CHANGE_POOL_4_MEMBERS_PLAN.md) | [итоги](docs/CHANGE_POOL_4_MEMBERS_SUMMARY.md) |
|
||||
|
||||
Диаграммы потоков данных подсистемы маршрутизации —
|
||||
[docs/ROUTING_DATAFLOW.md](docs/ROUTING_DATAFLOW.md).
|
||||
|
||||
## Топология
|
||||
|
||||
```
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
# Потоки данных подсистемы L3-маршрутизации
|
||||
|
||||
**Дата:** 2026-08-17
|
||||
**Область:** транзитная маршрутизация узла `hpnn-lb` — всё, что проходит через
|
||||
таблицы 20 (LPM) и 21 (adjacency). Балансировка (таблицы 10–12) показана только
|
||||
как точка ответвления и подробно описана в [STEP2_SUMMARY.md](STEP2_SUMMARY.md).
|
||||
**Источник:** `lb/pipeline.sh`, `lb/topology.env` (состояние на 4 члена пула).
|
||||
|
||||
Ключевое свойство: сетевой стек ядра контейнера в транзите не участвует. В ядре
|
||||
живут только два адреса — `10.20.0.253` и `10.30.0.253` на internal-портах для
|
||||
health-проб. Все решения о пересылке принимает OpenFlow.
|
||||
|
||||
## 1. Топология портов моста `br-lb`
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
CL["Клиент<br/>192.168.5.0/24"]
|
||||
|
||||
subgraph BR["br-lb — OpenFlow datapath"]
|
||||
direction TB
|
||||
PUB["pub0 — ofport 1<br/>macvlan @ enp3s0<br/>02:42:c0:a8:05:14<br/><i>шлюз 192.168.5.20, VIP .21</i>"]
|
||||
P2["p2 — ofport 2<br/>02:42:0a:14:00:01<br/><i>шлюз 10.20.0.1</i>"]
|
||||
P3["p3 — ofport 3<br/>02:42:0a:1e:00:01<br/><i>шлюз 10.30.0.1</i>"]
|
||||
HC2["hcif-p2 — ofport 4<br/>10.20.0.253 <b>в ядре</b>"]
|
||||
HC3["hcif-p3 — ofport 5<br/>10.30.0.253 <b>в ядре</b>"]
|
||||
end
|
||||
|
||||
BE1["be1 10.20.0.2"]
|
||||
BE3["be3 10.20.0.3"]
|
||||
BE2["be2 10.30.0.2"]
|
||||
BE4["be4 10.30.0.3"]
|
||||
HCD["hcd — демон<br/>health-check"]
|
||||
|
||||
CL <--> PUB
|
||||
P2 <--> BE1
|
||||
P2 <--> BE3
|
||||
P3 <--> BE2
|
||||
P3 <--> BE4
|
||||
HCD <--> HC2
|
||||
HCD <--> HC3
|
||||
```
|
||||
|
||||
## 2. Конвейер таблиц: путь маршрутизируемого пакета
|
||||
|
||||
Красным помечены точки отбрасывания, пунктиром — ответвление в балансировку.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
IN(["Пакет на порту моста"]) --> T0
|
||||
|
||||
T0{"<b>Таблица 0</b><br/>классификация по in_port<br/>и типу трафика"}
|
||||
|
||||
T0 -->|"arp"| T5["<b>5</b> ARP-респондер<br/>для .20/.21/10.20.0.1/<br/>10.30.0.1/.253"]
|
||||
T0 -->|"icmp echo<br/>на адреса узла"| T6["<b>6</b> ICMP echo-респондер"]
|
||||
T0 -->|"in_port=1 (pub)<br/>+ dst ∈ {VIP, узел,<br/>10.20/24, 10.30/24}"| LEARN["<b>learn</b> → запись<br/>adjacency клиента<br/>в таблицу 21<br/>(priority 90, 300 c)"]
|
||||
T0 -->|"in_port=2,3 (priv)"| T15["<b>15</b> обратный путь"]
|
||||
T0 -->|"in_port=4,5 (hcif)"| T20
|
||||
T0 -->|"иначе"| DROP0(["drop"])
|
||||
|
||||
LEARN --> T10{"<b>Таблица 10</b><br/>листенеры"}
|
||||
T10 -.->|"tcp dst=VIP:80"| LB["<b>11/12</b> слот → член пула,<br/>DNAT ct(commit,nat)"]
|
||||
T10 -->|"прочий IP<br/>(прямое обращение<br/>к бэкенду)"| T20
|
||||
T10 -->|"IP на VIP<br/>без листенера"| DROPV(["drop"])
|
||||
LB --> T20
|
||||
|
||||
T15 -->|"tcp"| CT["ct(zone=1, nat)<br/>снятие DNAT,<br/>если сессия известна"] --> T16["<b>16</b> пост-ct"] --> T20
|
||||
T15 -->|"прочий IP"| T20
|
||||
|
||||
T20{"<b>Таблица 20</b> — LPM<br/>приоритет = длина префикса"}
|
||||
T20 -->|"/32 10.20.0.253<br/>/32 10.30.0.253<br/><i>адрес узла, TTL не трогаем</i>"| T21
|
||||
T20 -->|"/24 10.20.0.0<br/>dec_ttl, src MAC = p2"| T21
|
||||
T20 -->|"/24 10.30.0.0<br/>dec_ttl, src MAC = p3"| T21
|
||||
T20 -->|"/24 192.168.5.0<br/>dec_ttl, src MAC = pub0"| T21
|
||||
T20 -->|"нет маршрута<br/><i>default отсутствует</i>"| DROP20(["drop"])
|
||||
|
||||
T21{"<b>Таблица 21</b> — adjacency<br/>next-hop MAC + порт"}
|
||||
T21 -->|"prio 100: be1..be4,<br/>hcif — статически"| OUT(["output: 2 / 3 / 4 / 5"])
|
||||
T21 -->|"prio 90: клиент,<br/>из действия learn"| OUTC(["output: 1"])
|
||||
T21 -->|"MAC неизвестен"| DROP21(["drop"])
|
||||
|
||||
classDef d fill:#7f1d1d,stroke:#ef4444,color:#fff
|
||||
class DROP0,DROPV,DROP20,DROP21 d
|
||||
```
|
||||
|
||||
Почему нумерация именно такая: `goto_table` в OpenFlow разрешает переход только
|
||||
вперёд, поэтому обратный путь (15/16) обязан стоять **до** общей маршрутизации
|
||||
(20), а не после неё.
|
||||
|
||||
## 3. Четыре потока маршрутизации
|
||||
|
||||
### R1. Клиент → бэкенд напрямую (публичный → приватный сегмент)
|
||||
|
||||
Балансировка не участвует: клиент обращается на `10.20.0.2:8080`, имея маршрут
|
||||
через `192.168.5.20`.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant C as Клиент 192.168.5.13
|
||||
participant P as pub0 (1)
|
||||
participant OF as Таблицы OpenFlow
|
||||
participant Q as p2 (2)
|
||||
participant B as be1 10.20.0.2
|
||||
|
||||
C->>P: ARP who-has 192.168.5.20
|
||||
P->>OF: таблица 5 — ARP-респондер
|
||||
OF-->>C: is-at 02:42:c0:a8:05:14
|
||||
C->>P: IP src=192.168.5.13 dst=10.20.0.2, TTL=64
|
||||
P->>OF: t0 learn (adj клиента в t21) → t10
|
||||
Note over OF: t10: не VIP → goto 20
|
||||
Note over OF: t20: LPM /24 10.20.0.0<br/>dec_ttl → 63, src MAC = p2
|
||||
Note over OF: t21: dst MAC = 02:42:0a:14:00:02, порт 2
|
||||
OF->>Q: пересылка
|
||||
Q->>B: пакет, src остался клиентским
|
||||
B->>Q: ответ src=10.20.0.2 dst=192.168.5.13
|
||||
Q->>OF: t0 → t15 → ct(nat) сессии нет → t16 → t20
|
||||
Note over OF: t20: LPM /24 192.168.5.0<br/>dec_ttl, src MAC = pub0
|
||||
Note over OF: t21: prio 90 — запись, созданная learn
|
||||
OF->>P: порт 1
|
||||
P->>C: ответ
|
||||
```
|
||||
|
||||
### R2. Транзит между приватными сегментами (be1 → be2)
|
||||
|
||||
Единственный поток, где узел работает как чистый маршрутизатор в обе стороны.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
BE1["be1<br/>10.20.0.2"] -->|"dst=10.30.0.2:8080<br/>TTL 64"| A["p2 (2)"]
|
||||
A --> B["t0: in_port=2 → t15"]
|
||||
B --> C["t15: tcp → ct(zone=1,nat)<br/>сессии в таблице нет —<br/>пакет не меняется"]
|
||||
C --> D["t16 → t20"]
|
||||
D --> E["t20: /24 10.30.0.0<br/>dec_ttl → 63<br/>src MAC = 02:42:0a:1e:00:01"]
|
||||
E --> F["t21: dst MAC =<br/>02:42:0a:1e:00:02<br/>output:3"]
|
||||
F --> G["p3 (3)"] --> BE2["be2<br/>10.30.0.2<br/><i>client=10.20.0.2</i>"]
|
||||
```
|
||||
|
||||
Обратный путь `be2 → be1` симметричен: `t0 → t15 → t16 → t20 (/24 10.20.0.0)
|
||||
→ t21 → p2`. Никакой трансляции ни в одном направлении.
|
||||
|
||||
### R3. Обратный трафик балансируемой сессии (бэкенд → клиент)
|
||||
|
||||
Тот же путь, что и R1-ответ, но с обязательным проходом через `ct`: именно там
|
||||
`src` возвращается с адреса бэкенда на VIP.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
BE["be1 10.20.0.2:8080"] -->|"src=10.20.0.2<br/>dst=192.168.5.13"| A["p2 (2)"]
|
||||
A --> B["t0: in_port=2 → t15"]
|
||||
B --> C["t15: ct(zone=1, nat)<br/><b>un-DNAT</b>:<br/>src → 192.168.5.21:80"]
|
||||
C --> D["t16 → t20"]
|
||||
D --> E["t20: /24 192.168.5.0<br/>dec_ttl, src MAC = pub0"]
|
||||
E --> F["t21 prio 90:<br/>MAC клиента из learn<br/>output:1"]
|
||||
F --> CL["Клиент<br/>видит ответ от VIP"]
|
||||
```
|
||||
|
||||
Почему обратный трафик вообще приходит на узел: у бэкендов профиль A — маршрут
|
||||
на клиентский префикс `192.168.5.0/24` направлен на шлюз сегмента
|
||||
(`10.20.0.1` / `10.30.0.1`), а `default` остаётся на шлюзе Docker. Собственный
|
||||
исходящий трафик бэкенда через балансировщик не идёт.
|
||||
|
||||
### R4. Health-пробы — единственный трафик, порождённый узлом
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant H as hcd (ядро контейнера)
|
||||
participant I as hcif-p2 (4)
|
||||
participant OF as OpenFlow
|
||||
participant Q as p2 (2)
|
||||
participant B as be1
|
||||
|
||||
Note over H: Dialer.LocalAddr = 10.20.0.253
|
||||
H->>I: SYN 10.20.0.253 → 10.20.0.2:8080
|
||||
I->>OF: t0: in_port=4 → <b>сразу t20</b><br/>без learn и без ct
|
||||
Note over OF: t20: /24 10.20.0.0, t21: output:2
|
||||
OF->>Q: →
|
||||
Q->>B: проба GET /healthz
|
||||
B->>Q: SYN-ACK 10.20.0.2 → 10.20.0.253
|
||||
Q->>OF: t0 → t15 → t16 → t20
|
||||
Note over OF: t20: <b>/32 10.20.0.253</b> prio 32<br/>TTL не уменьшается — пакет<br/>адресован самому узлу
|
||||
Note over OF: t21: output:4
|
||||
OF->>I: →
|
||||
I->>H: ответ, член up
|
||||
```
|
||||
|
||||
Пробы идут с `.253`, а не с VIP, намеренно: иначе ответ бэкенда попал бы в
|
||||
логику обратной трансляции (`t15`) и до пробера не дошёл.
|
||||
|
||||
## 4. Таблица решений
|
||||
|
||||
| Таблица | Матч | Действие | Комментарий |
|
||||
|---|---|---|---|
|
||||
| 0 | `in_port=1`, dst ∈ {VIP, .20, 10.20/24, 10.30/24} | `learn` → t10 | обучение сужено, чтобы в t21 не попадал broadcast-шум LAN |
|
||||
| 0 | `in_port=1`, прочий IP | drop | |
|
||||
| 0 | `in_port=2,3` | → t15 | обратный путь и транзит |
|
||||
| 0 | `in_port=4,5` | → t20 | трафик стека узла, минуя ct и learn |
|
||||
| 10 | `ip`, не VIP | → t20 | точка входа маршрутизации из публичного сегмента |
|
||||
| 15 | `tcp` | `ct(zone=1, nat)` → t16 | un-DNAT для балансируемых сессий, no-op для транзита |
|
||||
| 20 | `/32` адреса `.253` | prio 32, → t21 | TTL не уменьшается |
|
||||
| 20 | `/24` три префикса | prio 24, `dec_ttl` + `mod_dl_src` | LPM через приоритет |
|
||||
| 20 | остальное | prio 0, drop | **дефолтного маршрута нет** |
|
||||
| 21 | `nw_dst` = be1..be4, hcif | prio 100, `mod_dl_dst` + `output` | статические MAC из `docker-compose.yml` |
|
||||
| 21 | `nw_dst` = клиент | prio 90, из `learn` | hard_timeout 300 с |
|
||||
|
||||
## 5. Где поток обрывается
|
||||
|
||||
| Точка | Причина | Как увидеть |
|
||||
|---|---|---|
|
||||
| t0 prio 0 | не-IP и не-ARP: IPv6, DHCP, broadcast | `lbctl flows 0` |
|
||||
| t0 prio 100 (pub) | IP из публичного сегмента не стенду | `lbctl flows 0` |
|
||||
| t10 prio 150 | IP на VIP без листенера | `lbctl flows 10` |
|
||||
| t20 prio 0 | назначение без маршрута | `lbctl flows 20 \| grep priority=0` |
|
||||
| t21 prio 0 | next-hop MAC неизвестен (запись `learn` истекла) | `lbctl flows 21` |
|
||||
|
||||
## 6. Чего в этих потоках нет
|
||||
|
||||
- **ICMP-ошибок узел не генерирует** — ни `TTL exceeded`, ни `fragmentation
|
||||
needed`. Пакет с TTL=1 просто исчезает, `traceroute` через стенд узел не
|
||||
покажет, PMTUD не работает.
|
||||
- **Фрагменты IP не обрабатываются**: правила матчат L4-заголовок, которого во
|
||||
втором и последующих фрагментах нет.
|
||||
- **ARP-резолвера next-hop нет.** MAC бэкендов статичны, MAC клиентов узнаются
|
||||
действием `learn` — то есть только для тех, кто первым обратился к стенду.
|
||||
- **Дефолтного маршрута нет** по построению: узел маршрутизирует только три
|
||||
известных префикса.
|
||||
- **Только IPv4.**
|
||||
Reference in new issue
Block a user