Files

115 lines
9.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Шаг 2 — итоги: health-check и таблица слотов
**Дата:** 2026-08-16
**Статус:** выполнено, проверено после холодного перезапуска (50 из 50 проверок)
**План:** [STEP2_IMPLEMENTATION_PLAN.md](STEP2_IMPLEMENTATION_PLAN.md)
**Предыдущий шаг:** [STEP1_SUMMARY.md](STEP1_SUMMARY.md)
## Что сделано
- **Демон `hcd`** (Go, `hc/`) внутри контейнера-балансировщика: проверяет
живость членов пула, ведёт их состояние с гистерезисом (`rise=2`,
`fall=3`), пересчитывает раскладку слотов и заливает её в OVS. Стал
основным процессом контейнера.
- **Пробы с уникального адреса узла** (§7.1 дизайна v1): на мосту появились
internal-порты `hcif-p2` (`10.20.0.253`) и `hcif-p3` (`10.30.0.253`) — это
единственные адреса, которые узел держит в ядре. Источник соединения
фиксируется через `net.Dialer.LocalAddr`.
- **Таблица слотов вместо группы `type=select`**: 1024 слота, номер слота
даёт `multipath(symmetric_l4, basis=pool_id, modulo_n)`, раскладка слотов
по членам считается алгоритмом Maglev (§5). Группа удалена.
- **Fail-close**: пока живых членов нет, таблица слотов пуста и трафик на VIP
отбрасывается со счётчиком.
- **Дренаж** (§7.3): `lbctl drain <член>` / `enable <член>` — член исключается
из раскладки, пробы при этом продолжают идти.
- **Наблюдаемость**: `lbctl health` (состояние пула), `lbctl slots`
(раскладка и счётчики), `/metrics` в формате Prometheus, дайджест раскладки
SHA-256 (§5.3), в лог пишутся только переходы состояния.
## Результаты проверок
| Проверка | Результат |
|---|---|
| Пробы идут | оба члена `up` через 4 с после старта, задержка 1–3 мс |
| Источник проб | `tcpdump` на `p2`: SYN с `10.20.0.253`, не с VIP |
| Раскладка | 1024 слота, 512/512, дайджест `2f0ca39d5f33efa6` |
| Детерминизм | после `docker compose down/up` дайджест тот же |
| **Минимальное возмущение** | при выводе be1 переехали 512 его слотов, у be2 — **ни одного** |
| Отказ члена | `pause be1` → `down` за ~6 с, все 1024 слота у be2, трафик без ошибок |
| Восстановление | `unpause` → `up` за ~4 с, дайджест вернулся к исходному |
| Выживание сессии | длинная сессия через `/slow` не порвалась при выводе её члена из пула (14/14 тиков) |
| Fail-close | оба члена мертвы → таблица пуста, клиент получает таймаут, счётчик drop растёт |
| Идемпотентность | `make flows` → раскладка восстановлена в актуальном составе |
## Отклонения от плана
1. **Формат bundle-файла.** Планировались директивы `delete` / `add`; OVS 3.1
принимает их только с указанием типа сообщения: `flow delete` / `flow add`.
Первый вариант отвергался с `Unsupported bundle message type`.
2. **Шаг перестановки Maglev приводится к нечётному.** При числе слотов 1024
(степень двойки) шаг, выбранный как `h2 % (M-1) + 1`, может оказаться
чётным — тогда последовательность `(offset + j*skip) mod M` покрывает лишь
половину слотов и заполнение зацикливается. Классический алгоритм
рассчитан на простое M; здесь сохранено значение 1024 из §5.1 дизайна, а
взаимная простота обеспечена принудительной нечётностью шага.
3. **Форматирование `lbctl health` вынесено в демон** (`/status.txt`) —
иначе в образ балансировщика пришлось бы тянуть `jq` или `python3`.
4. **Проверка выживания сессии добавлена в `verify.sh`** вместе с эндпоинтом
`/slow` у бэкенда — в плане она была только описана.
## Подтверждённое наблюдение о ct и таблице слотов
План фиксировал предположение, что установленные сессии переживают смену
раскладки за счёт conntrack. Проверка подтвердила: сессия, обслуживаемая be2,
не порвалась при выводе be2 из пула — все 14 тиков пришли с того же бэкенда.
Причина в том, что `ct(commit, nat)` закрепляет трансляцию за соединением при
его создании, и последующие пакеты следуют существующей привязке независимо
от того, какого члена выбрала таблица слотов.
Практический вывод: в текущем stateful-датапасе Maglev защищает **размещение
новых соединений**, а не живые сессии — их и без него защищает conntrack.
Ценность таблицы слотов раскроется при переходе на stateless-датапас, где
никакого ct не будет и единственной защитой от переезда сессий останется
именно минимальное возмущение раскладки. Заодно это ровно то поведение,
которое §7.3 дизайна описывает как «дренаж означает прекратить приём, а не
мягко завершить».
## Известные ограничения
Часть перешла с шага 1, часть появилась вместе с health-check:
- **Датапас по-прежнему stateful** — обратную трансляцию делает `ct(nat)`.
Active/Active ECMP в такой схеме не работает: прямой и обратный трафик
обязаны проходить через один узел.
- **Кворума нет.** Вердикт о живости принимает единственный узел; §7.2
(наблюдения в `Member_Observation`, лидер через OVSDB lock, генерации пулов
и сходимость по дайджестам) появится вместе с control plane.
- **Только HTTP и TCP-пробы.** UDP-проб и ICMP echo из §7.1 нет.
- **Пул статичен** — состав задан в `topology.env`, менять его на лету можно
только дренажом. Модели `Load_Balancer → Listener → Pool → Member` в OVSDB
ещё нет.
- **Веса поддержаны в алгоритме, но не в конфигурации** — у всех членов
вес 1.
- **`hash_algo_version` не реализована.** Дайджест раскладки считается и
логируется, но сравнивать его не с кем: узел один.
- **Пул без живых членов не снимает анонс VIP** — альтернатива fail-close из
§7.3 не реализована, BGP на узле нет.
- ICMP-ошибки, фрагменты, IPv6, UDP- и L3-листенеры — как и на шаге 1, вне
рамок.
## Задел на шаг 3
1. **Stateless-датапас**: заменить `ct(nat)` на явный un-DNAT
(`nw_src=member_ip, tp_src=member_port` → `src = VIP`). Таблица слотов уже
на месте, `symmetric_l4` даёт одинаковый слот для обоих направлений — это
и есть предпосылка, ради которой шаг 2 сделан именно так.
2. Проверить детерминизм выбора по корпусу синтетических 5-tuple через
`ofproto/trace` (спайк S0 из §12 дизайна).
3. Control plane: схема OVSDB вместо `topology.env`, REST API и `lbctl`
поверх неё, валидации §8.4.
4. Второй узел LB: кворум наблюдений, генерации пулов, сравнение дайджестов.
5. ICMP-транслятор в userspace для PMTUD и корректных ICMP-ошибок от имени VIP.