Files
EvoBGP/docs/architecture.md
T
DenozordecandCursor 240d83fc61 docs(docs): describe durable jobs, telemetry, and bundle limits
Зафиксированы очередь job, семантика метрик, лимиты бандла и исходящих запросов, а также чеклист продакшена для пула соединений.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-11-02 17:24:18 +07:00

14 KiB
Raw Permalink Blame History

Архитектура EvoBGP

Высокоуровневое описание компонентов и потоков. Детальный продуктовый и инфраструктурный чертёж также зафиксирован во внутреннем плане репозитория: .cursor/plans/evobgp_архитектура_0e73ef02.plan.md (удобно для истории решений; пользовательская навигация — через этот раздел и overview.md).

Назначение слоёв

  • Control plane — HTTP API, хранилище состояния (PostgreSQL), фоновые задачи (jobs), подпись артефактов (бандлы), observability.
  • Data plane — демон BIRD, локальные конфиги в /etc/bird, сокет управления birdc, агент evobgp-agent для наблюдения/сопутствующих действий.
  • Edge интеграция — CLI evobgp-node на машине спикера: получение бандла по API, проверка подписи, применение конфигурации.

Компоненты (бинарники cmd/)

Бинарник Роль
evobgp-api Только HTTP API и связанная логика в одном процессе.
evobgp-all Тот же API + in-process scheduler (очередь module_refresh в общем Registry), ingest (prefetch ETag CDN), render (опционально auto-publish), deploy (лог расхождений published/applied).
evobgp-scheduler По refresh_interval_sec ставит tenant refresh. Reference Compose: доверенный DB-worker, общая PostgreSQL queue (EVOBGP_SCHEDULER_MODE=db). Опциональный HTTP-режим ограничен явным tenant; параметры и Compose override описаны в quickstart.
evobgp-ingest Периодический conditional GET по URL CDN-источников и обновление etag в БД.
evobgp-render По умолчанию только heartbeat; при EVOBGP_RENDER_AUTOPUBLISH=1 выставляет всем спикерам tenant последнюю ревизию (упрощение для демо).
evobgp-deploy Периодически логирует drift: last_applied_revision_id vs опубликованная ревизия для ноды.
evobgp-node CLI реплики: pull-bundle, verify-bundle, apply-bundle.
evobgp-agent Локальный агент рядом с BIRD: watch, serve (Panel→Node sync API на реплике).

В Docker Compose профиль reference запускает отдельные контейнеры под evobgp-api и четыре воркера; профиль microvps использует один контейнер evobgp-all. Сервис NATS вынесен в отдельный профиль nats и не требуется для внутренней очереди задач.

Пакеты internal/ (сжатая карта)

Пакет Назначение
httpapi Маршруты REST, аутентификация, CORS, привязка к store и jobs.
store Абстракция бэкенда данных; реализации в памяти и через репозиторий.
repository Доступ к PostgreSQL, сущности и миграции на уровне приложения.
db Подключение к БД и применение миграций.
jobs Durable enqueue/claim, lease/heartbeat/fencing, выполнение задач и deployment reconciliation.
telemetry Ограниченный фоновый collector legacy BGP/health и local BIRD; persistent snapshots и fenced discovery.
bundle Упаковка и проверка бандлов для нод.
signing Криптографическая проверка подписей.
birdfmt Форматирование и фрагменты конфигурации BIRD, вызовы birdc.
birddeploy Логика применения конфигурации к BIRD (используется в цепочке деплоя).
config Переменные окружения EVOBGP_*.
observability Метрики Prometheus, HTTP middleware.
broker Опциональный EVOBGP_BROKER_URL для будущей шины; пакет лишь логирует URL, NATS не участвует в исполнении задач. Текущий Registry дополнен PostgreSQL persistence/claim/reclaim.
pipeline Ingest+render для module_refresh: CDN/AS/IP/DOMAINS → module_prefix_snapshot (batch COPY, per-module lock) → агрегация CIDR O(n log n) → CreateRenderRevision. Fast-path снапшота — module.input_hash; DoH — TTL-кэш domain_resolve_cache; scheduler — jitter границ интервала.
nodedispatch Panel→Node HTTP wake-up (POST /v1/agent/sync) после deploy_apply.
agentserver HTTP API на реплике (serve): sync + health для Traefik; опционально firewall failover (/v1/firewall/*).
firewall Вычисление policy block/accept → плоский CIDR blocklist.

Удалённые спикеры

Реплики на отдельных VPS: remote-speakers.md. CP публикует ревизию и при EVOBGP_NODE_DISPATCH_ENABLED=1 будит agent; agent тянет signed bundle и применяет BIRD. Compose: deploy/compose/docker-compose.remote-speaker.yaml.

flowchart LR
  subgraph clients [Clients]
    WebUI[Web_UI]
    Operator[Operator_API_client]
    NodeCLI[evobgp_node]
  end
  subgraph control [Control_plane]
    API[evobgp_api]
    Sched[evobgp_scheduler]
    Ingest[evobgp_ingest]
    Render[evobgp_render]
    Deploy[evobgp_deploy]
    PG[(PostgreSQL)]
  end
  subgraph data [Data_plane]
    BIRD[BIRD2]
    Agent[evobgp_agent]
  end
  WebUI --> API
  Operator --> API
  NodeCLI --> API
  API --> PG
  Sched -->|optional_single_tenant_HTTP| API
  Sched --> PG
  Ingest --> PG
  Render --> PG
  Deploy --> PG
  Agent --> BIRD

При PostgreSQL очередь использует общий job_audit: enqueue фиксируется до HTTP 202, claim получает owner/lease/fencing token, а heartbeat продлевает владение. List/get/cancel/report читают общее хранилище. Server.StartBackground запускает durable queue и deployment reconciliation; Memory-режим сохраняет задания только в процессе. Подробности и ограничения — durable-jobs.md. NATS не участвует в исполнении задач. Разрыв между сохранением CRUD и автоматическим enqueue пока не закрыт transactional outbox; ошибка постановки явно сообщается клиенту.

Фоновая статистика

Server.StartBackground также запускает telemetry.Collector. Фиксированное число workers получает due targets из store с lease/fencing, опрашивает прежние endpoints агента 2.56.2 и локальный BIRD, затем сохраняет независимые BGP/health результаты. Discovery записывается в той же fenced транзакции. Истёкший владелец не может заменить snapshot или discoveries. Конфигурация speaker проверяется перед фиксацией; смена endpoint/секрета не переносит успешное наблюдение прежней конфигурации на новую.

GET peers/speakers/discoveries читает сохранённое состояние; live=1 не вызывает сетевой fan-out. POST /v1/telemetry/refresh объединяет запросы в пределах 10 секунд и назначает следующий сбор. received_at и настроенный интервал определяют свежесть, expires_at передаёт её срок клиенту; просроченные sessions не становятся текущим Established. При недоступности хранилища snapshots API отвечает 503.

Для local BIRD несколько CP различаются стабильной authority. EVOBGP_TELEMETRY_LOCAL_ID должен идентифицировать именно BIRD-узел; одинаковый ID допустим только при чтении одного экземпляра BIRD. Две одновременно актуальные authority одного master дают неизвестный статус. Устаревшая authority после перезапуска не скрывает единственный свежий снимок. Настройки интервала, workers и локальной идентичности — quickstart.md.

Снимки префиксов модуля

UpdateModulePrefixSnapshot выполняет read-modify-write атомарно. В PostgreSQL transaction advisory lock, чтение текущих строк, замена снимка и module.input_hash используют одну транзакцию и одно соединение. Потеря соединения не позволяет продолжить запись через другое. GET читает заголовок и строки в repeatable-read транзакции; ошибки scan/iteration не превращаются в неполный успешный результат. Memory использует блокировку ключа tenant/module и фиксирует snapshot/hash под общей mutex. Callback преобразует переданный снимок и не вызывает store I/O.

Это сериализует фиксацию снимка, но ещё не гарантирует единую версию всех входов ingest: конфигурация и input hash вычисляются до транзакции, а tenant manifest/version CAS остаётся отдельной работой B04. Контекст передаётся в операции snapshot и lookup; устаревшие вызовы других store-сущностей требуют последующего расширения.

Диаграмма: microvps (evobgp-all)

flowchart LR
  Client[HTTP_clients]
  All[evobgp_all_process]
  PG[(PostgreSQL)]
  BIRD[BIRD2]
  Client --> All
  All --> PG
  All --> BIRD

Внутри evobgp-all все воркеры используют тот же store и jobs.Registry, что и HTTP handlers, поэтому module_refresh выполняется в том же процессе без HTTP.

Профиль Compose microvps-full добавляет к этому стеку Web UI (nginx → evobgp-all) и Prometheus без отдельных контейнеров воркеров (функционально то же, что отдельные scheduler/ingest/… в reference). NATS включается отдельно через профиль nats. Запуск и текущие лимиты — в quickstart.md; пригодность для ~1 ГиБ RAM требует замера под нагрузкой.

Поток pipeline (ingest → render)

  1. Scheduler (evobgp-scheduler / in-process в evobgp-all) ставит tenant_refresh, если ModuleDueForScheduler: граница окна refresh_interval_sec со сдвигом fnv32(module.ID) % interval, чтобы модули с одним интервалом не били внешние API одновременно.
  2. Ingest (RefreshModuleIngest):
    • AS_PREFIXES — RIPEstat prefixes + holder параллельно, кэш asn_prefix_cache; дедуп строк по prefix + community.
    • CDN_CIDRS — единый fetchCDNSourceRows (conditional GET); prefetch уважает RefreshIntervalSec; merge снапшота под LockModuleSnapshot (пропущенные по ошибке источники сохраняют prior-строки при EVOBGP_CDN_PARTIAL_OK).
    • DOMAINS — DoH A+AAAA параллельно; попадания в domain_resolve_cache с TTL EVOBGP_DOMAIN_CACHE_TTL_SEC (default 300).
    • IP_RANGES — напрямую из записей модуля.
  3. Снапшот пишется batch (pgx.CopyFrom в PostgreSQL). Совпадение module.input_hash со снапшотом — O(1) пропуск повторного ingest при render; CRUD entries обнуляет hash.
  4. Render (RenderTenantRevision): smartAggregatePrefixRows (стек-схлопывание O(n log n), IPv6 без math/big) → ревизия, если набор префиксов изменился.

Поток: ревизия и бандл для ноды

  1. Оператор (роль operator или выше по политике) изменяет модули и запускает цепочку, приводящую к новой ревизии (часть шагов может быть асинхронной через jobs — см. OpenAPI).
  2. Control plane формирует подписанный бандл для пары спикер + ревизия.
  3. evobgp-node pull-bundle с ключом роли node запрашивает GET /v1/speakers/{id}/revisions/latest и затем GET /v1/speakers/{id}/bundle/{revision_id}.
  4. Локально выполняется проверка подписи (публичный ключ выдаётся при старте API) и применение к BIRD (apply-bundle).

Связанные документы