Реализует HLD-дизайн (hld.md) в виде развёртываемого Docker Compose окружения: 5 FRR-роутеров (A1-A3 anycast 10.200.200.1/32 на AS 65001, B1/B2 на AS 65002/65003) + 6 клиентских контейнеров, ECMP-балансировка трафика клиентов площадки B к anycast-адресу площадки A. - docker-compose.yml, frr/*, entrypoints/* — топология и конфиги FRR - scripts/verify.sh, scripts/traffic-test.sh — проверка BGP/ECMP и трафика - hld.md — HLD-дизайн + Mermaid-диаграмма топологии - README.md — запуск, структура, команды проверки на роутерах и клиентах - docs/ — планы внедрения и summary по каждому изменению Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Bc3HkMiDwooSyLBLoMKNyF
5.4 KiB
5.4 KiB
Summary: контейнерный POC anycast-топологии FRR
Дата: 2026-09-03 План: 2026-09-03-anycast-lab-plan.md
Что сделано
Топология из hld.md полностью развёрнута в Docker Compose (docker-compose.yml): 5 FRR-контейнеров (a1/a2/a3 — AS 65001, площадка A; b1/b2 — AS 65002/65003, площадка B) + 6 клиентских контейнеров (nicolaka/netshoot) на площадке B. 8 Docker-сетей воспроизводят все линки HLD (6 межплощадочных + 2 клиентских сегмента).
Созданные файлы: docker-compose.yml, frr/{a1,a2,a3,b1,b2}/{daemons,frr.conf,vtysh.conf}, entrypoints/{a-entrypoint.sh,b-entrypoint.sh,client-entrypoint.sh}, scripts/{verify.sh,traffic-test.sh}, README.md.
Отклонения от первоначального плана (обнаружены и устранены в процессе)
/30→/29на межплощадочных линках. Docker всегда резервирует первый usable-адрес подсети под gateway моста, даже приinternal: true. При буквальном/30(только 2 usable-адреса) это конфликтовало с адресом.1, который нужен был роутеру A (Error: Address already in use). Решение — расширить линковые подсети до/29и явно вынести gateway на неиспользуемый адрес (.6); адресация роутеров (.1/.2) осталась как в hld.md. Аналогично для клиентских/24-сетей gateway вынесен на.254, чтобы не конфликтовать с адресом B-роутера (.1).sysctl -wв entrypoint не работал (Read-only file system) —/proc/sysвнутри контейнера доступен на запись только для sysctl'ов, заданных декларативно через Docker (sysctls:в compose), а не вызовомsysctlиз процесса внутри контейнера. Перенеслиnet.ipv4.ip_forward=1вdocker-compose.yml.cap_sys_adminтребуется FRR для namespace-операций — сcap_add: [NET_ADMIN, NET_RAW]zebra/bgpd не стартовали (privs_init: initial cap_set_proc failed). ДобавилиSYS_ADMINвcap_add.- ECMP не реагировал на src-port. С дефолтной политикой ядра
net.ipv4.fib_multipath_hash_policy=0next-hop хешируется только по src/dst IP — один клиент всегда получал один и тот же anycast-узел, что противоречило описанной в hld.md 5-tuple-логике. Установилиnet.ipv4.fib_multipath_hash_policy=1на b1/b2. - Убраны
interface ethNиз frr.conf. Раз Docker Compose уже назначает статические IP черезipv4_address, а zebra сам подхватывает существующие адреса интерфейсов, ручное дублирование адресов в frr.conf было источником риска (несовпадение имени интерфейсаethNс ожидаемым линком). Оставлен толькоdummy0(единственный интерфейс, которого нет "из коробки" у Docker).
Результаты проверки
- BGP: все 10 eBGP-сессий (a1/a2/a3 × b1/b2) в состоянии Established.
- ECMP: у b1 и у b2 — по 3 равнозначных пути (weight 1) до
10.200.200.1/32, через a1/a2/a3 соответственно. - Связность клиент → anycast:
ping/traceroute/curlс любого клиента (c1…c6) до10.200.200.1проходят. - Распределение трафика (
scripts/traffic-test.sh): серии запросов с варьированием src-port показывают ответы от всех трёх anycast-узлов в примерно равной пропорции, например сc4(30 запросов):a1: 13, a2: 2, a3: 15; сc2(24 запроса):a1: 10, a2: 7, a3: 7. Ответ каждого узла содержит его hostname (мини HTTP-responder на dummy0), что визуально подтверждает, какой именно anycast-узел обслужил запрос. - Идемпотентность:
docker compose down && docker compose up -dподнимает окружение с нуля без ручного вмешательства, BGP сходится за секунды.
Как повторить
docker compose up -d
./scripts/verify.sh
./scripts/traffic-test.sh c1 30
docker compose down
Дальнейшие возможные шаги (не выполнялись, вне рамок задачи)
- Симуляция отказа одного из A-узлов (
docker compose stop a2) для проверки конвергенции ECMP на оставшихся 2 путях. - Нагрузочный тест через
iperf3(уже есть в образеnicolaka/netshoot) вместо HTTP-responder.