Зафиксированы очередь job, семантика метрик, лимиты бандла и исходящих запросов, а также чеклист продакшена для пула соединений. Co-authored-by: Cursor <cursoragent@cursor.com>
14 KiB
Архитектура 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)
- Scheduler (
evobgp-scheduler/ in-process вevobgp-all) ставитtenant_refresh, еслиModuleDueForScheduler: граница окнаrefresh_interval_secсо сдвигомfnv32(module.ID) % interval, чтобы модули с одним интервалом не били внешние API одновременно. - 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с TTLEVOBGP_DOMAIN_CACHE_TTL_SEC(default 300).IP_RANGES— напрямую из записей модуля.
- Снапшот пишется batch (
pgx.CopyFromв PostgreSQL). Совпадениеmodule.input_hashсо снапшотом — O(1) пропуск повторного ingest при render; CRUD entries обнуляет hash. - Render (
RenderTenantRevision):smartAggregatePrefixRows(стек-схлопывание O(n log n), IPv6 безmath/big) → ревизия, если набор префиксов изменился.
Поток: ревизия и бандл для ноды
- Оператор (роль
operatorили выше по политике) изменяет модули и запускает цепочку, приводящую к новой ревизии (часть шагов может быть асинхронной через jobs — см. OpenAPI). - Control plane формирует подписанный бандл для пары спикер + ревизия.
evobgp-node pull-bundleс ключом ролиnodeзапрашиваетGET /v1/speakers/{id}/revisions/latestи затемGET /v1/speakers/{id}/bundle/{revision_id}.- Локально выполняется проверка подписи (публичный ключ выдаётся при старте API) и применение к BIRD (
apply-bundle).
Связанные документы
- quickstart.md — как поднять стек.
- api.md — точки входа HTTP.
- access.md — ключи и роли.