CI / changes (push) Successful in 4s
CI / openapi (push) Successful in 24s
CI / go (push) Successful in 22s
CI / bird2 (push) Successful in 13s
CI / docker-images (deploy/docker/bird2/Dockerfile, evobgp-bird2) (push) Successful in 38s
CI / docker-images (deploy/docker/evobgp-agent/Dockerfile, evobgp-agent) (push) Successful in 1m8s
CI / docker-images (deploy/docker/evobgp-web/Dockerfile, evobgp-web) (push) Successful in 49s
CI / docker-images (evobgp-all, 1, deploy/docker/gobinary/Dockerfile, evobgp-all) (push) Successful in 1m30s
CI / docker-images (evobgp-api, 1, deploy/docker/gobinary/Dockerfile, evobgp-api) (push) Successful in 1m31s
CI / docker-images (evobgp-ingest, 0, deploy/docker/gobinary/Dockerfile, evobgp-ingest) (push) Has been cancelled
CI / docker-images (evobgp-deploy, 0, deploy/docker/gobinary/Dockerfile, evobgp-deploy) (push) Has been cancelled
CI / docker-images (evobgp-node, 0, deploy/docker/gobinary/Dockerfile, evobgp-node) (push) Has been cancelled
CI / docker-images (evobgp-render, 0, deploy/docker/gobinary/Dockerfile, evobgp-render) (push) Has been cancelled
CI / docker-images (evobgp-scheduler, 0, deploy/docker/gobinary/Dockerfile, evobgp-scheduler) (push) Has been cancelled
987 lines
75 KiB
Markdown
987 lines
75 KiB
Markdown
---
|
||
name: EvoBGP архитектура
|
||
overview: Оба профиля по умолчанию на PostgreSQL (один движок, одни миграции). reference — микросервисы + PG + брокер; microVPS — evobgp-all + контейнер PG (жёсткий тюнинг). SQLite — опция только для одного контейнера без PG. REST API проработан в §7 плана (пути /v1, jobs, бандлы нод); OpenAPI — канон при появлении схемы.
|
||
todos:
|
||
- id: schema-db
|
||
content: "Схема БД: PostgreSQL (основной); опционально SQLite (microVPS single-container); модули, ревизии, jobs"
|
||
status: completed
|
||
- id: bird-generator
|
||
content: Формат bird.conf, include-фрагменты, фильтры, birdc configure
|
||
status: completed
|
||
- id: rest-jobs
|
||
content: REST (refresh, apply, preview, rollback, bundle API) и async jobs
|
||
status: completed
|
||
- id: node-agent
|
||
content: evobgp-agent + bird2 в Docker на мастере; evobgp-node + тот же паттерн на реплике (отдельная ВМ)
|
||
status: completed
|
||
- id: observability
|
||
content: Метрики, алерты префиксов и BGP-сессий
|
||
status: completed
|
||
- id: docker-ms
|
||
content: Compose profiles reference + microVPS; сервисы bird2 + evobgp-agent; сеть BGP; лимиты; логи; prune
|
||
status: completed
|
||
- id: go-modules
|
||
content: Monorepo по §1 (дерево репозитория); internal/*, cmd/evobgp-all и cmd/* для reference
|
||
status: completed
|
||
- id: replica-bundle
|
||
content: Подписанный бандл ревизии, API, evobgp-node
|
||
status: completed
|
||
- id: risk-hardening
|
||
content: Двухфазный deploy, LKG, pin BIRD, политика миграций, обязательная подпись бандла на ноде
|
||
status: completed
|
||
- id: test-bird-matrix
|
||
content: Матрица сценариев BIRD + golden-тесты birdfmt + bird -p в CI
|
||
status: completed
|
||
- id: ci-gitea
|
||
content: .gitea/workflows/ci.yaml, документация для act runner
|
||
status: completed
|
||
- id: web-ui
|
||
content: "Web UI (Svelte + shadcn-svelte) в отдельном контейнере: списки, расписание, мониторинг"
|
||
status: completed
|
||
isProject: false
|
||
---
|
||
|
||
# EvoBGP — архитектурный план
|
||
|
||
Control-plane на **Go**, анонс префиксов через **BIRD**, политика и история в **SQL-БД**, управление по **REST**. Репозиторий кода пока пустой — документ задаёт целевую архитектуру.
|
||
|
||
---
|
||
|
||
## Содержание
|
||
|
||
1. [Структура репозитория (файлы и пакеты)](#1-структура-репозитория-файлы-и-пакеты)
|
||
2. [Два эталонных профиля](#2-два-эталонных-профиля-развёртывания)
|
||
3. [Логическая модель и термины](#3-логическая-модель-общая-для-обоих-профилей)
|
||
4. [Сервисы и контейнеры](#4-сервисы-сравнение-профилей)
|
||
- [4.1. Топология Docker Compose](#41-топология-docker-compose)
|
||
5. [База данных, ETL, ER-схема](#5-база-данных-etl-er-схема)
|
||
6. [Модули префиксов и FQDN](#6-модули-префиксов-as-cdn-домены)
|
||
7. [REST API](#7-rest-api)
|
||
- [7.1. Общие соглашения](#71-общие-соглашения)
|
||
- [7.2. Системные и служебные](#72-системные-и-служебные)
|
||
- [7.3. Модули префиксов](#73-модули-префиксов-module)
|
||
- [7.4. DoH-профили](#74-doh-профили-doh_profile)
|
||
- [7.5. BGP community](#75-bgp-community-bgp_community)
|
||
- [7.6. Пиры](#76-пиры-bgp_peer)
|
||
- [7.7. Спикеры BIRD](#77-спикеры-bird-bgp_speaker)
|
||
- [7.8. Ревизии конфигурации](#78-ревизии-конфигурации-config_revision)
|
||
- [7.9. Применение конфигурации](#79-применение-конфигурации-deploy-bird)
|
||
- [7.10. Задачи](#710-задачи-job_audit)
|
||
- [7.11. Реплики и бандлы](#711-реплики-evobgp-node-бандлы)
|
||
- [7.12. Глобальные настройки](#712-глобальные-настройки-опционально)
|
||
- [7.13. Матрица прав](#713-матрица-прав-роли)
|
||
- [7.14. Следующие итерации](#714-следующие-итерации-api)
|
||
- [7.15. Связь API с разделами плана](#715-связь-api-с-разделами-плана)
|
||
8. [Генерация BIRD и ревизии](#8-генерация-bird-и-ревизии)
|
||
9. [Эксплуатация и масштаб пиров](#9-эксплуатация-и-масштаб)
|
||
10. [Реплика evobgp-node](#10-реплика-evobgp-node)
|
||
11. [Выбор СУБД](#11-выбор-субд)
|
||
12. [Профиль microVPS — детализация](#12-профиль-microvps-детализация)
|
||
13. [Снижение рисков (меры и процессы)](#13-снижение-рисков-меры-и-процессы)
|
||
14. [Тестирование BIRD2 и матрица сценариев](#14-тестирование-bird2-и-матрица-сценариев)
|
||
15. [CI/CD (Gitea Actions)](#15-cicd-gitea-actions)
|
||
16. [Риски и этапы внедрения](#16-риски-и-этапы-внедрения)
|
||
17. [Web UI (Svelte, shadcn-svelte)](#17-web-ui-svelte-shadcn-svelte)
|
||
|
||
---
|
||
|
||
## 1. Структура репозитория (файлы и пакеты)
|
||
|
||
Monorepo на **Go**: общая логика в `internal/*`, отдельные бинарники в `cmd/*`. Цель — одна кодовая база для профилей **reference** (несколько процессов) и **microVPS** (`evobgp-all`).
|
||
|
||
**Принципы:**
|
||
|
||
- `**cmd/`** — только точки входа: флаги, переменные окружения, сборка зависимостей (DI), запуск; без доменной логики.
|
||
- `**internal/`** — весь прикладной код; внешние модули Go не могут импортировать эти пакеты (правило компилятора).
|
||
- **Слои:** `domain` (модели и инварианты без I/O) → `repository` и адаптеры к БД/внешним API → пакеты воркеров (оркестрация) → транспорт (`httpapi`, `jobs`, клиент к агенту).
|
||
- **Один код — две упаковки:** микросервисы и `evobgp-all` используют одни и те же пакеты `internal/*`; отличается только набор процессов в `cmd/*`.
|
||
- `**docs/`**, `**scripts/`** — как сейчас в репозитории (контракт API: [docs/openapi.yaml](docs/openapi.yaml), вспомогательные скрипты).
|
||
- `**deploy/**` — Dockerfiles, `docker-compose` с профилями `reference` / `microVPS`, при необходимости entrypoint’ы для **bird2** / **evobgp-agent**; инфраструктура не смешивается с `internal/`.
|
||
|
||
**Целевое дерево каталогов** (ориентир; имена подпакетов можно уточнить при первой итерации кода, **роли** каталогов зафиксированы):
|
||
|
||
```
|
||
EvoBGP/
|
||
├── cmd/
|
||
│ ├── evobgp-api/ # REST + постановка jobs (reference)
|
||
│ ├── evobgp-scheduler/
|
||
│ ├── evobgp-ingest/
|
||
│ ├── evobgp-render/
|
||
│ ├── evobgp-deploy/
|
||
│ ├── evobgp-all/ # microVPS: те же пакеты, один процесс / несколько goroutine
|
||
│ ├── evobgp-agent/ # рядом с bird2: запись конфигов, birdc
|
||
│ └── evobgp-node/ # pull бандла, проверка подписи, apply
|
||
├── internal/
|
||
│ ├── config/ # загрузка конфигурации (ENV, файлы)
|
||
│ ├── platform/ # логирование, метрики, трассировка, health
|
||
│ ├── db/ # пул, транзакции; embed миграций или вызов migrate
|
||
│ ├── repository/ # SQL по сущностям (tenant, module, peer, revision, jobs, …)
|
||
│ ├── domain/ # типы и правила без I/O (префиксы, community, ревизии)
|
||
│ ├── httpapi/ # роуты OpenAPI, middleware, валидация, маппинг в сервисы
|
||
│ ├── jobs/ # задачи: сейчас in-memory Registry в процессе API; PG job_audit / брокер — целевое расширение (см. примечание ниже §2)
|
||
│ ├── scheduler/ # триггеры по расписанию модулей
|
||
│ ├── ingest/ # CDN, DoH, нормализация, запись в БД
|
||
│ ├── render/ # материализация префиксов, ревизия, текст артефактов BIRD
|
||
│ ├── birdfmt/ # шаблоны и сборка include-фрагментов (альтернатива имени: bird/)
|
||
│ ├── deploy/ # доставка на volume, взаимодействие с evobgp-agent / бандлы
|
||
│ ├── bundle/ # упаковка и подпись бандла для evobgp-node
|
||
│ └── signing/ # ключи, проверка подписи на ноде
|
||
├── migrations/ # SQL миграции PostgreSQL (единый набор для обоих профилей)
|
||
├── deploy/
|
||
│ ├── compose/ # docker-compose с профилями
|
||
│ └── docker/ # Dockerfile на бинарь + общие слои
|
||
├── docs/
|
||
├── scripts/
|
||
├── go.mod
|
||
├── go.sum
|
||
└── README.md
|
||
```
|
||
|
||
**Соответствие сервисам плана:**
|
||
|
||
|
||
| Сервис (план) | Код |
|
||
| -------------------- | ------------------------------------------------------------------------------------------------ |
|
||
| **evobgp-api** | `cmd/evobgp-api`, `internal/httpapi`, `internal/jobs`, `internal/repository` |
|
||
| **evobgp-scheduler** | `cmd/evobgp-scheduler`, `internal/scheduler`, `internal/jobs`, `internal/repository` |
|
||
| **evobgp-ingest** | `cmd/evobgp-ingest`, `internal/ingest`, `internal/repository` |
|
||
| **evobgp-render** | `cmd/evobgp-render`, `internal/render`, `internal/birdfmt`, `internal/repository` |
|
||
| **evobgp-deploy** | `cmd/evobgp-deploy`, `internal/deploy`, `internal/bundle`, `internal/repository` |
|
||
| **evobgp-all** | `cmd/evobgp-all` — поднимает HTTP и воркеры, импортируя те же `internal/`* |
|
||
| **evobgp-agent** | `cmd/evobgp-agent`, `internal/deploy` (запись на volume, `birdc`) |
|
||
| **evobgp-node** | `cmd/evobgp-node`, `internal/bundle`, `internal/signing`, `internal/birdfmt` / `internal/deploy` |
|
||
|
||
|
||
**Поток пакетов (упрощённо):**
|
||
|
||
```mermaid
|
||
flowchart TB
|
||
subgraph entry [cmd]
|
||
API[evobgp-api]
|
||
W[scheduler ingest render deploy]
|
||
end
|
||
subgraph internal [internal]
|
||
HTTP[httpapi]
|
||
J[jobs]
|
||
R[repository]
|
||
DBpkg[db]
|
||
REN[render]
|
||
BF[birdfmt]
|
||
DEP[deploy]
|
||
end
|
||
PG[(PostgreSQL)]
|
||
AG[evobgp-agent]
|
||
API --> HTTP
|
||
HTTP --> J
|
||
HTTP --> R
|
||
W --> J
|
||
W --> R
|
||
R --> DBpkg
|
||
DBpkg --> PG
|
||
REN --> BF
|
||
REN --> R
|
||
DEP --> R
|
||
DEP --> AG
|
||
```
|
||
|
||
|
||
|
||
---
|
||
|
||
## 2. Два эталонных профиля развёртывания
|
||
|
||
Один и тот же **код** в дереве `internal/` (все подпакеты), две **упаковки** в Docker Compose.
|
||
|
||
|
||
| Критерий | **reference** (эталон) | **microVPS** |
|
||
| ---------------------------- | -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
|
||
| **Назначение** | Production control-plane без жёсткого лимита RAM; горизонтальное масштабирование воркеров | Один хост: маленькая VPS / вложенная ВМ |
|
||
| **CPU** | 2+ vCPU (рекомендуется) | **1 vCPU** |
|
||
| **RAM** | 4+ ГиБ (ориентир) | **~1 ГиБ** |
|
||
| **Диск под Docker + данные** | по объёму проекта | **7–10 ГиБ** (бюджет; не весь диск ОС) |
|
||
| **ОС** | Linux (в т.ч. Ubuntu 22.04/24.04) | **Ubuntu 24.04 LTS** (референс) |
|
||
| **Контейнеры Go** | 5 образов: `api`, `scheduler`, `ingest`, `render`, `deploy` | **1** образ: `evobgp-all` |
|
||
| **БД** | **PostgreSQL** (отдельный сервис или managed) | **PostgreSQL** в контейнере (**жёсткий** тюнинг под 1 ГиБ) |
|
||
| **Очередь** | **NATS JetStream** или **Redis Streams** (на выбор) | **Нет брокера** — `job_audit` в **PostgreSQL** + `SKIP LOCKED` / малый пул коннектов |
|
||
| **Reverse proxy** | Traefik / Nginx (опционально) | **Нет** — прямой порт API |
|
||
| **Object storage** | MinIO / S3 опционально для артефактов | Только **локальный volume** или небольшие BLOB в БД |
|
||
| **BIRD** | **BIRD 2** в **Docker** (privileged / нужные capabilities) + контейнер `**evobgp-agent`** с общим volume | То же: **bird2** + **evobgp-agent** + **postgres** + **evobgp-all** |
|
||
| **Контейнеры (итого)** | 5×Go + **postgres** + брокер + **bird2** + **evobgp-agent** (+ опц. proxy) | **4:** **evobgp-all** + **postgres** + **bird2** + **evobgp-agent** (+ опц. нода вне VPS) |
|
||
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
subgraph ref [reference]
|
||
R1[5x Go]
|
||
R2[(PostgreSQL)]
|
||
R3[(Broker)]
|
||
R4[bird2]
|
||
R5[evobgp-agent]
|
||
R1 --> R2
|
||
R1 --> R3
|
||
R1 --> R5
|
||
R5 --> R4
|
||
end
|
||
subgraph micro [microVPS]
|
||
M1[evobgp-all]
|
||
M2[(PostgreSQL)]
|
||
M3[bird2]
|
||
M4[evobgp-agent]
|
||
M1 --> M2
|
||
M1 --> M4
|
||
M4 --> M3
|
||
end
|
||
```
|
||
|
||
|
||
|
||
> **Правило:** логика домена **одинакова**; **один движок БД — PostgreSQL** (одинаковые миграции и SQL). Отличаются число процессов Go, наличие брокера и **настройки** PG.
|
||
|
||
> **Примечание (текущий код vs целевая очередь):** исполнение async jobs идёт через **in-memory `jobs.Registry` в процессе `evobgp-api` / `evobgp-all`**; таблица `job_audit` в миграциях заложена под будущую персистенцию и идемпотентность между процессами. В **reference** Compose отдельный `evobgp-scheduler` **не** читает эту очередь, а вызывает `POST .../modules/{id}/refresh` по HTTP. NATS в compose — для будущей интеграции; см. [docs/architecture.md](../../docs/architecture.md).
|
||
|
||
> **Опция `microVPS_sqlite`:** один контейнер `evobgp-all` без PG — только если критичен абсолютный минимум контейнеров; иначе **не рекомендуется** как основной путь.
|
||
|
||
---
|
||
|
||
## 3. Логическая модель (общая для обоих профилей)
|
||
|
||
- **BGP-сервер (ваш)** — процесс **BIRD**, который **анонсирует** префиксы и держит сессии.
|
||
- **Клиенты** — **внешние** роутеры (BGP-пиры), подключающиеся **к вам** и **получающие** маршруты. Это не контейнеры EvoBGP.
|
||
- **Мастер** — control-plane + **BIRD 2** в **контейнере** и контейнер `**evobgp-agent`** (общий volume: сгенерированные `bird.d`, сокет/канал для `birdc`).
|
||
- **EvoBGP-нода (опционально)** — отдельная площадка: **только** pull подписанного **бандла** с мастера + **BIRD 2 в Docker** (тот же паттерн agent + bird2); **своей** полной БД и ingest **нет**.
|
||
|
||
**Быстрый путь данных:** БД → ingest (CDN / DoH / AS; **IP_RANGES** только читаются при render из БД) → render (префиксы + community + ревизия) → deploy → файлы BIRD → `birdc configure` → клиентские сессии.
|
||
|
||
---
|
||
|
||
## 4. Сервисы (сравнение профилей)
|
||
|
||
|
||
| Сервис | Назначение | reference | microVPS |
|
||
| -------------------- | --------------------------------------------------------------- | -------------------------------- | ------------------------ |
|
||
| **evobgp-api** | REST, CRUD, задачи, `/jobs` | отдельный контейнер | goroutine в `evobgp-all` |
|
||
| **evobgp-scheduler** | Интервалы модулей/CDN → события | отдельный контейнер | goroutine в `evobgp-all` |
|
||
| **evobgp-ingest** | CDN, DoH, материализация в БД | отдельный контейнер | goroutine в `evobgp-all` |
|
||
| **evobgp-render** | Итоговые префиксы, ревизия, текст BIRD | отдельный контейнер | goroutine в `evobgp-all` |
|
||
| **evobgp-deploy** | Доставка на мастерский BIRD, публикация бандла | отдельный контейнер | goroutine в `evobgp-all` |
|
||
| **evobgp-agent** | Контейнер рядом с **bird2**: запись конфигов, `birdc configure` | то же (общий volume с **bird2**) | то же |
|
||
| **evobgp-node** | Реплика: pull бандла, локальный BIRD | отдельный хост | **не на microVPS** |
|
||
|
||
|
||
**Инфраструктура reference:** PostgreSQL, NATS или Redis, опционально Traefik, MinIO, **bird2** + **evobgp-agent** в Docker.
|
||
|
||
### Зависимости (профиль reference)
|
||
|
||
```mermaid
|
||
flowchart TB
|
||
subgraph cp [Control plane]
|
||
API[evobgp-api]
|
||
SCH[scheduler]
|
||
ING[ingest]
|
||
REN[render]
|
||
DEP[deploy]
|
||
end
|
||
DB[(PostgreSQL)]
|
||
MQ[(Broker)]
|
||
API --> DB
|
||
API --> MQ
|
||
SCH --> DB
|
||
SCH --> MQ
|
||
ING --> DB
|
||
ING --> MQ
|
||
REN --> DB
|
||
REN --> MQ
|
||
DEP --> DB
|
||
DEP --> MQ
|
||
DEP --> AG[evobgp-agent]
|
||
AG --> BIRD2[bird2]
|
||
```
|
||
|
||
|
||
|
||
### 4.1. Топология Docker Compose
|
||
|
||
Целевая упаковка: **все компоненты мастера в Compose**, включая **BIRD 2** (`bird2`). Пара **bird2 + evobgp-agent** делает **общий именованный volume** (или bind-mount) для каталога конфигурации и точки управления `birdc` (см. образ/entrypoint в репозитории).
|
||
|
||
**Сеть для BGP:** на практике для входящих TCP **179** часто нужны `network_mode: host` (Linux), **macvlan/ipvlan** или публикация портов / отдельный L3-интерфейс — выбор фиксируется в профиле Compose и документации оператора; контейнер **bird2** получает **privileged** и набор **capabilities** (`NET_ADMIN`, и при необходимости `NET_RAW`), плюс `sysctls` под forwarding, если не на host-сети.
|
||
|
||
```mermaid
|
||
flowchart TB
|
||
subgraph refDocker [profile_reference]
|
||
subgraph goRef [Сервисы Go]
|
||
API1[evobgp-api]
|
||
SCH1[scheduler]
|
||
ING1[ingest]
|
||
REN1[render]
|
||
DEP1[deploy]
|
||
end
|
||
PG1[(postgres)]
|
||
BR1[broker]
|
||
subgraph bgpRef [Стек BGP Docker]
|
||
AG1[evobgp-agent]
|
||
B21[bird2]
|
||
end
|
||
VOL1[vol_bird_config]
|
||
API1 --> PG1
|
||
API1 --> BR1
|
||
SCH1 --> PG1
|
||
SCH1 --> BR1
|
||
ING1 --> PG1
|
||
ING1 --> BR1
|
||
REN1 --> PG1
|
||
REN1 --> BR1
|
||
DEP1 --> PG1
|
||
DEP1 --> BR1
|
||
DEP1 --> AG1
|
||
AG1 --> VOL1
|
||
B21 --> VOL1
|
||
AG1 -->|birdc| B21
|
||
end
|
||
subgraph microDocker [profile_microVPS]
|
||
ALL[evobgp-all]
|
||
PG2[(postgres)]
|
||
subgraph bgpMicro [Стек BGP Docker]
|
||
AG2[evobgp-agent]
|
||
B22[bird2]
|
||
end
|
||
VOL2[vol_bird_config]
|
||
ALL --> PG2
|
||
ALL --> AG2
|
||
AG2 --> VOL2
|
||
B22 --> VOL2
|
||
AG2 -->|birdc| B22
|
||
end
|
||
```
|
||
|
||
|
||
|
||
На **реплике** (`evobgp-node`) — тот же паттерн **bird2 + evobgp-agent** в Docker на отдельном хосте; pull бандла и `birdc configure` без полной БД (см. §10).
|
||
|
||
---
|
||
|
||
## 5. База данных, ETL, ER-схема
|
||
|
||
### 5.1. Группы сущностей
|
||
|
||
|
||
| Область | Назначение |
|
||
| ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||
| **module** | Тип `AS_PREFIXES` / `CDN_CIDRS` / `DOMAINS` / `IP_RANGES`, расписание, DoH, приоритет |
|
||
| **module_cdn_source** | URL + `source_kind` (формат тела списка); примеры наброска: `text_cidr_lines`, `json_prefix_list`, `custom`; ETag; свой `refresh_interval` опционально |
|
||
| **doh_profile** | URL DoH, таймауты, секреты по ссылке |
|
||
| **module_domain_entry** / **module_as_entry** / **module_ip_range_entry** | FQDN; или ASN/префикс; или **только CIDR/диапазон** + `community_id` (`IP_RANGES`) |
|
||
| **bgp_community** | Справочник community для BIRD |
|
||
| **bgp_peer** | Клиентский пир; `speaker_id` NULL = все спикеры в бандле |
|
||
| **bgp_speaker** | master / replica, `last_applied_revision_id` |
|
||
| **config_revision** / **revision_materialized_prefix** | История, diff, откат |
|
||
| **job_audit** | Асинхронные задачи; в reference дополняет брокер |
|
||
|
||
|
||
### 5.2. ETL
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
subgraph ex [Extract]
|
||
CDN[CDN fetch]
|
||
DOH[DoH]
|
||
AS[AS из БД]
|
||
IPR[IP ranges из БД]
|
||
end
|
||
subgraph tr [Transform]
|
||
NORM[Нормализация CIDR]
|
||
DEDUP[Дедуп]
|
||
COMM[Community]
|
||
end
|
||
subgraph ld [Load]
|
||
DB[(SQL БД)]
|
||
REV[revision]
|
||
ART[артефакты BIRD]
|
||
end
|
||
CDN --> NORM
|
||
DOH --> NORM
|
||
AS --> NORM
|
||
IPR --> NORM
|
||
NORM --> DEDUP --> COMM
|
||
COMM --> DB
|
||
COMM --> REV
|
||
REV --> ART
|
||
```
|
||
|
||
|
||
|
||
### 5.3. ER (упрощённо)
|
||
|
||
```mermaid
|
||
erDiagram
|
||
tenant ||--o{ module : owns
|
||
tenant ||--o{ bgp_community : owns
|
||
tenant ||--o{ bgp_peer : owns
|
||
tenant ||--o{ bgp_speaker : owns
|
||
doh_profile ||--o{ module : uses
|
||
module ||--o{ module_cdn_source : contains
|
||
module ||--o{ module_domain_entry : contains
|
||
module ||--o{ module_as_entry : contains
|
||
module ||--o{ module_ip_range_entry : contains
|
||
bgp_community ||--o{ module_domain_entry : tags
|
||
bgp_community ||--o{ module_as_entry : tags
|
||
bgp_community ||--o{ module_ip_range_entry : tags
|
||
bgp_community ||--o{ module_cdn_source : tags
|
||
bgp_speaker ||--o{ bgp_peer : scope
|
||
module ||--o{ config_revision : produces
|
||
config_revision ||--o{ revision_materialized_prefix : snapshot
|
||
module ||--o{ job_audit : tasks
|
||
```
|
||
|
||
|
||
|
||
### 5.4. Пояснения к таблицам
|
||
|
||
|
||
| Таблица | Назначение | Ключевые поля | Кто использует |
|
||
| -------------------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ |
|
||
| **tenant** | Multi-tenant | `id`, `name`, `slug` | API, все сервисы |
|
||
| **module** | Блок политики | `type`, `enabled`, `priority`, `doh_profile_id`, `refresh_interval_sec`, `cron_expr`, `default_community_id` | API, scheduler, ingest, render |
|
||
| **doh_profile** | DoH | `url`, таймауты, ссылка на секрет | DOMAINS, ingest |
|
||
| **module_cdn_source** | Строка CDN | `source_kind` (набросок: `text_cidr_lines`, `json_prefix_list`, `custom`), `url`, `etag`, `refresh_interval_sec`, `community_id` | ingest, render |
|
||
| **module_domain_entry** | FQDN | `fqdn`, `community_id`, метаданные резолва | ingest, render |
|
||
| **module_as_entry** | AS/префикс | `asn`, `prefix`, `community_id` | API, render |
|
||
| **module_ip_range_entry** | Статический CIDR | `prefix` (CIDR), `community_id`; без ASN и без внешнего URL — модуль типа `IP_RANGES` | API, render |
|
||
| **bgp_community** | Справочник | `kind`, значения, уникальность в tenant | API, render |
|
||
| **bgp_peer** | Клиентский пир | neighbor, ASN, политики, `bgp_speaker_id` (NULL = все спикеры) | API, render, бандл |
|
||
| **bgp_speaker** | Экземпляр BIRD | `role` master/replica, endpoint, `last_applied_revision_id` | deploy, нода |
|
||
| **config_revision** | История | `hash`, артефакт, `parent_revision_id` | render, deploy, rollback |
|
||
| **revision_materialized_prefix** | Снимок префиксов | `revision_id`, `prefix`, `community_id`, `source` | preview, diff, откат |
|
||
| **job_audit** | Задачи | `kind`, `status`, `idempotency_key` | API, воркеры |
|
||
|
||
|
||
Дополнительно: `**global_settings`** (KV); `**module_cdn_fetch_log`** (опционально, TTL).
|
||
|
||
В **reference** очередь: брокер + `job_audit`; в **microVPS** — только БД и идемпотентность в `job_audit`.
|
||
|
||
---
|
||
|
||
## 6. Модули префиксов (AS / CDN / домены)
|
||
|
||
### Продуктовая трактовка
|
||
|
||
В терминах продукта **модуль** — это один из **четырёх видов источника префиксов**: **AS** (`AS_PREFIXES`), **CDN** (`CDN_CIDRS`), **DOMAINS** (`DOMAINS`), **IP-диапазоны** (`IP_RANGES`). Наполнение задаётся либо **записями в таблице** (ASN или префикс + `community_id`; FQDN + `community_id` для доменов; **CIDR + `community_id`** для `IP_RANGES` без ASN и без внешнего URL), либо для CDN — **источником по URL** с полем `source_kind`, определяющим формат скачанного списка и парсер. Строка сущности `**module`** в БД — это экземпляр модуля выбранного типа (расписание, приоритет, DoH для доменов и т.д.); `**module_id` в API** — идентификатор экземпляра, а не «имя типа». При минимальном развёртывании (один bird2, мало пиров) допустим **один экземпляр на каждый нужный тип** или узкий набор экземпляров — это не противоречит модели.
|
||
|
||
|
||
| Тип | Ввод | Поведение |
|
||
| ---------------- | ---------------------------- | ------------------------------------------------------------------------- |
|
||
| **AS** | ASN / префиксы | Таблица + опционально внешний `PrefixProvider` |
|
||
| **CDN** | URL или список | Fetch, ETag, интервал модуля или строки |
|
||
| **Домены** | FQDN | В BIRD попадают **только** IP-префиксы после DoH-резолва |
|
||
| **IP-диапазоны** | CIDR (IPv4/IPv6) + community | Только таблица `module_ip_range_entry`; без IRR/ASN-семантики и без fetch |
|
||
|
||
|
||
**FQDN:** воркер резолвит через **DoH-профиль** модуля → `/32` / `/128` (или политика) → материализация в БД → генерация static include для BIRD.
|
||
|
||
---
|
||
|
||
## 7. REST API
|
||
|
||
**Статус:** черновик для согласования и реализации; каноничная машиночитаемая форма — **[docs/openapi.yaml](docs/openapi.yaml)** (по мере заполнения). Ниже — **единая проработка** путей и семантики (консолидация с [docs/evobgp-api-sketches.md](docs/evobgp-api-sketches.md)). Базовый префикс: `**/v1`**. Термины: [§3](#3-логическая-модель-общая-для-обоих-профилей), [§5](#5-база-данных-etl-er-схема), [§6](#6-модули-префиксов-as-cdn-домены).
|
||
|
||
### 7.1. Общие соглашения
|
||
|
||
|
||
| Тема | Решение (набросок) |
|
||
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
|
||
| **Аутентификация** | Заголовок `Authorization: Bearer <api_key>` или mTLS на edge; ключи привязаны к tenant и роли. |
|
||
| **Multi-tenant** | Все сущности в скоупе tenant: либо из ключа, либо явный префикс `X-Tenant-Id` (только для супер-ролей). |
|
||
| **Идентификаторы** | UUID v7 или ULID в URL; в JSON — строки. |
|
||
| **Время** | ISO 8601 UTC (`2026-04-03T12:00:00Z`). |
|
||
| **Ошибки** | Тело `application/problem+json` (RFC 9457): `type`, `title`, `status`, `detail`, `instance`, опционально `errors[]` по полям. |
|
||
| **Идемпотентность** | Для мутаций, создающих задачи или побочные эффекты: заголовок `Idempotency-Key` (опционально обязателен для `POST` apply/refresh). |
|
||
| **Пагинация** | `?cursor=<opaque>&limit=50` (cursor-based); ответ: `items`, `next_cursor`, `has_more`. |
|
||
| **Асинхронные операции** | `202 Accepted`, заголовок `Location: /v1/jobs/{job_id}`; тело `{ "job_id", "status": "queued" }`. |
|
||
| **Версионирование** | Несовместимые изменения — новый префикс `/v2`. |
|
||
|
||
|
||
### 7.2. Системные и служебные
|
||
|
||
|
||
| Метод | Путь | Назначение |
|
||
| ----- | ------------- | ------------------------------------------------------------ |
|
||
| `GET` | `/v1/health` | Liveness (процесс жив). |
|
||
| `GET` | `/v1/ready` | Readiness (БД, брокер при reference, и т.д.). |
|
||
| `GET` | `/v1/version` | Версия сборки API и control-plane (`git_sha`, `build_time`). |
|
||
|
||
|
||
### 7.3. Модули префиксов (`module`)
|
||
|
||
**Связь с продуктом.** В продуктовой формулировке **ASN / CDN / DOMAINS / IP-диапазоны** — это **четыре вида модулей**. Поле `**type`** в `POST /v1/modules` выбирает вид: `AS_PREFIXES`, `CDN_CIDRS`, `DOMAINS` или `**IP_RANGES`**. Дочерние ресурсы: `**as-entries`**, `**cdn-sources**`, `**domain-entries**`; `**ip-range-entries**` — статические **CIDR + `community_id`** (без ASN и без URL). `**module_id**` в пути — идентификатор **конкретного экземпляра** модуля, а не имя типа (согласовано с [§6](#6-модули-префиксов-as-cdn-домены)).
|
||
|
||
|
||
| Метод | Путь | Описание |
|
||
| -------- | ------------------------- | ----------------------------------------------------------------- |
|
||
| `GET` | `/v1/modules` | Список модулей tenant (фильтры: `?type=`, `?enabled=`). |
|
||
| `POST` | `/v1/modules` | Создать модуль. |
|
||
| `GET` | `/v1/modules/{module_id}` | Детали модуля. |
|
||
| `PATCH` | `/v1/modules/{module_id}` | Частичное обновление (расписание, DoH, приоритет, `enabled`). |
|
||
| `DELETE` | `/v1/modules/{module_id}` | Мягкое удаление или `enabled=false` — зафиксировать в реализации. |
|
||
|
||
|
||
**CDN-источники модуля**
|
||
|
||
|
||
| Метод | Путь | Описание |
|
||
| -------- | ------------------------------------------------- | --------------------------------------------------------------------- |
|
||
| `GET` | `/v1/modules/{module_id}/cdn-sources` | Список строк CDN. |
|
||
| `POST` | `/v1/modules/{module_id}/cdn-sources` | Добавить источник (URL + `source_kind` + опционально `community_id`). |
|
||
| `PATCH` | `/v1/modules/{module_id}/cdn-sources/{source_id}` | URL, `source_kind`, `community_id`, свой `refresh_interval_sec`. |
|
||
| `DELETE` | `/v1/modules/{module_id}/cdn-sources/{source_id}` | Удалить. |
|
||
|
||
|
||
**Записи AS / домены**
|
||
|
||
|
||
| Метод | Путь | Описание |
|
||
| -------- | --------------------------------------------------- | --------------------- |
|
||
| `GET` | `/v1/modules/{module_id}/as-entries` | Список ASN/префиксов. |
|
||
| `POST` | `/v1/modules/{module_id}/as-entries` | Добавить. |
|
||
| `PATCH` | `/v1/modules/{module_id}/as-entries/{entry_id}` | Обновить. |
|
||
| `DELETE` | `/v1/modules/{module_id}/as-entries/{entry_id}` | Удалить. |
|
||
| `GET` | `/v1/modules/{module_id}/domain-entries` | FQDN + community. |
|
||
| `POST` | `/v1/modules/{module_id}/domain-entries` | Добавить. |
|
||
| `PATCH` | `/v1/modules/{module_id}/domain-entries/{entry_id}` | Обновить. |
|
||
| `DELETE` | `/v1/modules/{module_id}/domain-entries/{entry_id}` | Удалить. |
|
||
|
||
|
||
**Записи IP-диапазонов** (только для `type: IP_RANGES`)
|
||
|
||
|
||
| Метод | Путь | Описание |
|
||
| -------- | ----------------------------------------------------- | ------------------------------------ |
|
||
| `GET` | `/v1/modules/{module_id}/ip-range-entries` | Список CIDR + `community_id`. |
|
||
| `POST` | `/v1/modules/{module_id}/ip-range-entries` | Добавить (`prefix`, `community_id`). |
|
||
| `PATCH` | `/v1/modules/{module_id}/ip-range-entries/{entry_id}` | Обновить. |
|
||
| `DELETE` | `/v1/modules/{module_id}/ip-range-entries/{entry_id}` | Удалить. |
|
||
|
||
|
||
**Refresh (ingest)**
|
||
|
||
|
||
| Метод | Путь | Описание |
|
||
| ------ | --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||
| `POST` | `/v1/modules/{module_id}/refresh` | Запуск ingest для модуля (CDN / DoH / AS по типу). Для `**IP_RANGES`** обычно **не требуется** (данные только в БД); возможен `**204`** / no-op или `**400`**, если тип не поддерживает refresh — зафиксировать в реализации. |
|
||
|
||
|
||
**Пример тела создания модуля (набросок)**
|
||
|
||
```json
|
||
{
|
||
"type": "CDN_CIDRS",
|
||
"name": "edge-v4",
|
||
"enabled": true,
|
||
"priority": 10,
|
||
"doh_profile_id": null,
|
||
"refresh_interval_sec": 3600,
|
||
"cron_expr": null,
|
||
"default_community_id": "550e8400-e29b-41d4-a716-446655440000"
|
||
}
|
||
```
|
||
|
||
Пример модуля `**IP_RANGES**`: `type: "IP_RANGES"`, `doh_profile_id: null`, далее строки через `ip-range-entries` с полями `prefix` (например `203.0.113.0/24`) и `community_id`.
|
||
|
||
### 7.4. DoH-профили (`doh_profile`)
|
||
|
||
|
||
| Метод | Путь | Описание |
|
||
| -------- | ----------------------- | ------------------------------------------------------------------------------------- |
|
||
| `GET` | `/v1/doh-profiles` | Список. |
|
||
| `POST` | `/v1/doh-profiles` | Создать (URL, таймауты; секрет — ссылка на vault id или отдельный `POST .../secret`). |
|
||
| `GET` | `/v1/doh-profiles/{id}` | Детали (без раскрытия секрета). |
|
||
| `PATCH` | `/v1/doh-profiles/{id}` | Обновить. |
|
||
| `DELETE` | `/v1/doh-profiles/{id}` | Удалить, если не используется модулями. |
|
||
|
||
|
||
### 7.5. BGP community (bgp_community)
|
||
|
||
|
||
| Метод | Путь | Описание |
|
||
| -------- | ---------------------- | ------------------------------ |
|
||
| `GET` | `/v1/communities` | Список справочника. |
|
||
| `POST` | `/v1/communities` | Создать. |
|
||
| `GET` | `/v1/communities/{id}` | Детали. |
|
||
| `PATCH` | `/v1/communities/{id}` | Обновить. |
|
||
| `DELETE` | `/v1/communities/{id}` | Удалить при отсутствии ссылок. |
|
||
|
||
|
||
### 7.6. Пиры (`bgp_peer`)
|
||
|
||
|
||
| Метод | Путь | Описание |
|
||
| -------- | ---------------- | ------------------------------------------------------------------------------ |
|
||
| `GET` | `/v1/peers` | Список (`?speaker_id=`, пагинация). |
|
||
| `POST` | `/v1/peers` | Создать пира. |
|
||
| `GET` | `/v1/peers/{id}` | Детали. |
|
||
| `PATCH` | `/v1/peers/{id}` | Политики, neighbor, ASN, привязка к `bgp_speaker_id` или `null` = все спикеры. |
|
||
| `DELETE` | `/v1/peers/{id}` | Удалить / отключить. |
|
||
|
||
|
||
### 7.7. Спикеры BIRD (`bgp_speaker`)
|
||
|
||
|
||
| Метод | Путь | Описание |
|
||
| ------- | ------------------- | ------------------------------------------ |
|
||
| `GET` | `/v1/speakers` | Список (master / replica, endpoint). |
|
||
| `POST` | `/v1/speakers` | Зарегистрировать спикер (реплика, canary). |
|
||
| `GET` | `/v1/speakers/{id}` | Детали + `last_applied_revision_id`. |
|
||
| `PATCH` | `/v1/speakers/{id}` | Метаданные, endpoint. |
|
||
|
||
|
||
### 7.8. Ревизии конфигурации (config_revision)
|
||
|
||
|
||
| Метод | Путь | Описание |
|
||
| ------ | -------------------------------------- | --------------------------------------------------------------- |
|
||
| `GET` | `/v1/revisions` | История ревизий (`?module_id=`, `?limit=`). |
|
||
| `GET` | `/v1/revisions/{revision_id}` | Метаданные: хэш, родитель, время, артефакты. |
|
||
| `GET` | `/v1/revisions/{revision_id}/prefixes` | Материализованный снимок префиксов (пагинация). |
|
||
| `GET` | `/v1/revisions/{revision_id}/preview` | Превью фрагментов BIRD (read-only, без apply). |
|
||
| `POST` | `/v1/revisions/{revision_id}/rollback` | Создать **новую** ревизию с содержимым отката; часто `**202`**. |
|
||
|
||
|
||
**Сравнение ревизий (набросок)**
|
||
|
||
|
||
| Метод | Путь | Описание |
|
||
| ----- | ---------------------------- | ----------------------------------------------------------------------------- |
|
||
| `GET` | `/v1/revisions/{a}/diff/{b}` | Diff префиксов / метаданных (формат зафиксировать: JSON patch или табличный). |
|
||
|
||
|
||
### 7.9. Применение конфигурации (deploy, BIRD)
|
||
|
||
|
||
| Метод | Путь | Описание |
|
||
| ------ | ------------------------- | --------------------------------------------------------------------------------------------- |
|
||
| `POST` | `/v1/apply` | Применить текущую целевую ревизию на всех спикерах (или по политике по умолчанию). `**202`**. |
|
||
| `POST` | `/v1/speakers/{id}/apply` | Применить на одном спикере (canary). `**202`**. |
|
||
| `POST` | `/v1/bird/reload` | Опционально: явный мягкий reload политики (если отделён от apply); иначе часть `apply`. |
|
||
|
||
|
||
**Тело `POST /v1/apply` (набросок, опционально)**
|
||
|
||
```json
|
||
{
|
||
"revision_id": "01JQXYZ...",
|
||
"strategy": "all_speakers",
|
||
"dry_run": false
|
||
}
|
||
```
|
||
|
||
Связь с безопасным применением: [§13](#13-снижение-рисков-меры-и-процессы) (двухфазный deploy, LKG).
|
||
|
||
### 7.10. Задачи (job_audit)
|
||
|
||
|
||
| Метод | Путь | Описание |
|
||
| ------ | -------------------------- | --------------------------------------------- |
|
||
| `GET` | `/v1/jobs` | Список задач (`?status=`, `?kind=`, cursor). |
|
||
| `GET` | `/v1/jobs/{job_id}` | Статус, прогресс, ошибка, связанные сущности. |
|
||
| `POST` | `/v1/jobs/{job_id}/cancel` | Запрос отмены (best-effort). |
|
||
|
||
|
||
**Пример ответа `GET /v1/jobs/{id}`**
|
||
|
||
```json
|
||
{
|
||
"job_id": "01JQXYZ...",
|
||
"kind": "module_refresh",
|
||
"status": "running",
|
||
"idempotency_key": "client-abc-123",
|
||
"created_at": "2026-04-03T10:00:00Z",
|
||
"started_at": "2026-04-03T10:00:01Z",
|
||
"finished_at": null,
|
||
"error": null,
|
||
"meta": { "module_id": "01JQM..." }
|
||
}
|
||
```
|
||
|
||
### 7.11. Реплики evobgp-node: бандлы
|
||
|
||
Вызываются **нодой** с отдельным ключом / mTLS (роль `node`). См. также [§10](#10-реплика-evobgp-node).
|
||
|
||
|
||
| Метод | Путь | Описание |
|
||
| ------ | ------------------------------------------------ | -------------------------------------------------------------------------------------- |
|
||
| `GET` | `/v1/speakers/{speaker_id}/revisions/latest` | Указатель на последнюю опубликованную ревизию для ноды. |
|
||
| `GET` | `/v1/speakers/{speaker_id}/bundle/{revision_id}` | Скачивание подписанного бандла (архив + `manifest.json` + подпись). |
|
||
| `POST` | `/v1/nodes/enroll` | Регистрация ноды (обмен ключами, привязка к `speaker_id`) — детали протокола отдельно. |
|
||
|
||
|
||
Заголовки для бандла: `Content-Type: application/octet-stream` или multipart; целостность по `manifest` (SHA-256) и подписи (например Ed25519) — обязательно на ноде в production ([§13](#13-снижение-рисков-меры-и-процессы)).
|
||
|
||
### 7.12. Глобальные настройки (опционально)
|
||
|
||
|
||
| Метод | Путь | Описание |
|
||
| ------- | -------------- | ------------------------------------------------------- |
|
||
| `GET` | `/v1/settings` | KV вроде `global_settings` (лимиты CDN, feature flags). |
|
||
| `PATCH` | `/v1/settings` | Частичное обновление (только роль operator). |
|
||
|
||
|
||
### 7.13. Матрица прав (роли)
|
||
|
||
|
||
| Ресурс | `viewer` | `editor` | `operator` | `node` |
|
||
| -------------------------- | -------- | -------- | ---------- | ------ |
|
||
| GET модули, ревизии, peers | да | да | да | нет* |
|
||
| PATCH модули, peers | нет | да | да | нет |
|
||
| apply, rollback | нет | нет | да | нет |
|
||
| bundle / enroll | нет | нет | нет | да |
|
||
|
||
|
||
Нода не ходит в общий CRUD; только [§7.11](#711-реплики-evobgp-node-бандлы).
|
||
|
||
### 7.14. Следующие итерации API
|
||
|
||
- Полная **OpenAPI 3.1** в `docs/openapi.yaml` по этому разделу.
|
||
- Webhooks: `POST` на URL клиента по завершении `job` (опционально).
|
||
- SSE/WebSocket для стрима статуса долгих jobs.
|
||
- Rate limits по ключу и по tenant в ответах (`RateLimit-*` заголовки).
|
||
|
||
### 7.15. Связь API с разделами плана
|
||
|
||
|
||
| Тема плана | Подраздел §7 |
|
||
| ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
||
| REST, jobs, идемпотентность | [7.1](#71-общие-соглашения), [7.10](#710-задачи-job_audit) |
|
||
| refresh, apply, rollback, preview, `IP_RANGES` | [7.3](#73-модули-префиксов-module), [7.8](#78-ревизии-конфигурации-config_revision), [7.9](#79-применение-конфигурации-deploy-bird) |
|
||
| peers, speakers, communities, DoH | [7.4](#74-doh-профили-doh_profile)–[7.7](#77-спикеры-bird-bgp_speaker) |
|
||
| bundle API, нода | [7.11](#711-реплики-evobgp-node-бандлы) |
|
||
| Контрактные тесты HTTP | [§14](#14-тестирование-bird2-и-матрица-сценариев) + `openapi.yaml` |
|
||
|
||
|
||
Черновик в репозитории [docs/evobgp-api-sketches.md](docs/evobgp-api-sketches.md) держать **синхронным** с §7 при согласовании изменений (или пометить файл указателем на план как единый источник).
|
||
|
||
---
|
||
|
||
## 8. Генерация BIRD и ревизии
|
||
|
||
- Фрагменты `bird.d/*.conf`, `include`, фильтры, `peers.conf`, community из справочника.
|
||
- Ревизия: хэш набора префиксов, артефакты, откат = новая ревизия со старым содержимым.
|
||
|
||
---
|
||
|
||
## 9. Эксплуатация и масштаб
|
||
|
||
- До **~20+** клиентских пиров — строки `bgp_peer`, не отдельные хосты EvoBGP.
|
||
- Rate-limit CDN, per-module cooldown.
|
||
- Canary: несколько `bgp_speaker` или подмножество пиров по фильтру.
|
||
- Метрики: размер префикс-сета, ошибки DoH/CDN, `birdc show protocols`.
|
||
- Секреты BGP — Vault / K8s secrets, не plaintext в БД.
|
||
- Last-known-good конфиг на **volume** / bind-mount, общий для **bird2** и **evobgp-agent** (или снимок на хосте при bind-mount).
|
||
|
||
---
|
||
|
||
## 10. Реплика evobgp-node
|
||
|
||
1. Render на мастере упаковывает **бандл** (как для мастерского BIRD) + **manifest** (SHA-256) + **подпись** (например Ed25519).
|
||
2. **evobgp-node**: fetch → проверка → распаковка → `birdc configure`.
|
||
3. Общие фильтры/префиксы **идентичны**; `local.conf` на ноде (router id, source) — вне бандла.
|
||
4. Риски: дубли анонсов, отставание ревизий, компрометация без подписи.
|
||
|
||
---
|
||
|
||
## 11. Выбор СУБД
|
||
|
||
> **Решение по умолчанию:** **PostgreSQL** в **обоих** профилях — один тип миграций, один SQL-диалект в коде (`pgx` / `database/sql`), проще сопровождение и перенос с microVPS на reference без смены БД.
|
||
|
||
|
||
| Движок | Роль в плане |
|
||
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
||
| **PostgreSQL** | **Основной** для `reference` и `microVPS` (различаются только ресурсы и `postgresql.conf`). |
|
||
| **SQLite** | **Опция** `microVPS_sqlite`: один контейнер без PG — только при жёстком лимите «ровно один контейнер приложения+БД в одном процессе». |
|
||
| **MySQL / MariaDB** | **Опционально** по требованию заказчика/хостинга; тот же слой DAO через второй драйвер — вне дефолтного пути. |
|
||
| **rqlite** | HA без отдельного DBA Postgres; **+контейнеры**; на 1 ГиБ тесно. |
|
||
|
||
|
||
### Почему PG «не много» и всё же два контейнера на microVPS
|
||
|
||
При **узкой** конфигурации (`shared_buffers` **64–128 МиБ**, `max_connections` **15–30**, **один** пул коннектов в `evobgp-all`, без сотен долгоживущих backend’ов) суммарный вклад PG **сопоставим** с практичным использованием SQLite в том же объёме RAM, зато:
|
||
|
||
- нет отличий DDL/типов между профилями;
|
||
- нормальные **advisory locks** / `SKIP LOCKED` для очереди `job_audit`;
|
||
- проще подключить **внешний** managed Postgres при росте.
|
||
|
||
На **microVPS** отдельные контейнеры `**postgres`** и стек **bird2 + evobgp-agent** — **осознанная плата** за единообразие с **reference** (минимум: `evobgp-all` + `postgres` + `bird2` + `evobgp-agent`).
|
||
|
||
### PostgreSQL: память и CPU (ориентиры)
|
||
|
||
|
||
| Сценарий соединения | RAM на backend |
|
||
| ---------------------- | -------------- |
|
||
| Чистый idle | ~1.5 МиБ |
|
||
| После простых запросов | ~10–11 МиБ |
|
||
| После тяжёлых / temp | ~14.5 МиБ |
|
||
|
||
|
||
Плюс `shared_buffers`, relation cache при огромной схеме, autovacuum. **reference:** при многих процессах Go — **PgBouncer** (transaction pooling). **microVPS:** пулер часто **не нужен**, если суммарно **≤10** реальных коннектов к PG. Таймауты: `idle_in_transaction_session_timeout`, `statement_timeout`, параметры `tcp_keepalives`_*; мониторинг `pg_stat_activity`.
|
||
|
||
### rqlite
|
||
|
||
Raft, HTTP/`gorqlite`; для HA control-plane; не замена дефолтного PG в плане без отдельного решения.
|
||
|
||
---
|
||
|
||
## 12. Профиль microVPS — детализация
|
||
|
||
> **Железо:** 1 vCPU · ~1024 МиБ RAM · **7–10 ГиБ** SSD под Docker + данные + логи · Ubuntu 24.04. Рекомендуется **swap 512 МиБ–1 ГиБ**, если его нет.
|
||
|
||
### Контейнеры
|
||
|
||
|
||
| Вариант | Состав |
|
||
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
||
| **A (целевой)** | **evobgp-all** + **postgres** + **bird2** + **evobgp-agent** (общий volume конфигов BIRD; см. [§4.1](#41-топология-docker-compose)) |
|
||
| **B (опция)** | Без отдельного PG — профиль **microVPS_sqlite** + тот же стек **bird2** + **evobgp-agent** |
|
||
| **C (legacy)** | BIRD только на хосте ОС — **не целевой путь**, только для отладки или жёстких ограничений Docker |
|
||
|
||
|
||
### Бюджет диска 7–10 ГиБ
|
||
|
||
|
||
| Статья | Ориентир |
|
||
| ---------------- | ------------------------------------- |
|
||
| Образ evobgp-all | ~50–150 МиБ |
|
||
| Образ PostgreSQL | ~80–200 МиБ (слои образа) |
|
||
| Данные PG | ~200 МиБ – 2 ГиБ (политика ревизий) |
|
||
| Конфиги / бандлы | ~10–200 МиБ |
|
||
| Логи Docker | **max-size** / **max-file** в compose |
|
||
| Prune | `docker system prune` по расписанию |
|
||
| Резерв ОС/пики | ≥1–2 ГиБ |
|
||
|
||
|
||
### CPU и RAM
|
||
|
||
- Worker pool ingest/render: **1–2**; без агрессивного параллелизма на одном ядре.
|
||
- `deploy.resources`: лимиты на **evobgp-all**, **postgres**, **bird2**, **evobgp-agent** (на PG ориентир **256m–384m**, на пару **bird2+agent** заложить **128m–256m** — уточнить по `docker stats`).
|
||
- Ориентир суммарно: **~400–750 МиБ** простой, **~550–900 МиБ** пик (**PG + Go + bird2 + система**) — на **1 ГиБ** обычно нужны **swap** и жёсткий тюнинг PG/`bird2`.
|
||
|
||
### Tuning PostgreSQL (microVPS)
|
||
|
||
Пример направлений (не копировать слепо — проверить по мониторингу):
|
||
|
||
- `shared_buffers` = **64–128 МиБ**; `max_connections` = **20–40**; `work_mem` умеренно низкий.
|
||
- Отключить или минимизировать `parallel_workers` на 1 vCPU.
|
||
- `effective_cache_size` подсказка планировщику без выделения RAM.
|
||
|
||
### Tuning PostgreSQL (reference)
|
||
|
||
Обычные практики под размер ВМ; **PgBouncer** при многих сервисах Go; резерв под autovacuum и пики ingest.
|
||
|
||
---
|
||
|
||
## 13. Снижение рисков (меры и процессы)
|
||
|
||
Дополняет [§9](#9-эксплуатация-и-масштаб) и [§10](#10-реплика-evobgp-node) конкретными обязательными практиками.
|
||
|
||
### Конфигурация BIRD: безопасное применение
|
||
|
||
- **Двухфазный deploy:** (1) запись новой ревизии во **временный** каталог на общем volume и проверка `**bird -c <path> -p`** (парсинг без запуска демона; см. `bird(8)`); (2) только при нулевом коде выхода — **атомарная** подмена активных файлов (rename) и `**birdc configure`**. Опционально перед фазой (2) — `preview`/diff в control-plane (REST).
|
||
- **Last-known-good (LKG):** хранить на volume **предыдущую** применённую ревизию; при неуспехе `configure` или ненулевом exit **автоматически** восстановить файлы LKG, зафиксировать событие в логах/метриках и **не** оставлять BIRD в полусобранном состоянии.
|
||
- **Canary в production:** перед полным apply — отдельный `bgp_speaker` или подмножество `bgp_peer` (см. [§9](#9-эксплуатация-и-масштаб)); полный выкат только после проверки сессий/префиксов на канареечном спикере.
|
||
|
||
### Данные, очередь, microVPS
|
||
|
||
- **Миграции:** в основной ветке — только **вперёд**; откат схемы — явные down-миграции (если приняты в процессе) или восстановление БД из бэкапа; политика фиксируется в операторской документации.
|
||
- **Jobs:** обязательные `**idempotency_key`** и уникальность в `job_audit` там, где это предотвращает двойной apply/reload.
|
||
- **microVPS:** пороги мониторинга на рост таблиц ревизий/артефактов, **retention** старых ревизий, лимиты ротации логов Docker (см. [§12](#12-профиль-microvps-детализация)) — с **алертами** при приближении к лимиту диска и OOM.
|
||
|
||
### Безопасность
|
||
|
||
- **evobgp-node:** в production **обязательна** проверка **подписи** бандла (отдельно от TLS транспорта); ключ подписи не смешивать с другими ролями.
|
||
- Секреты BGP (пароли, ключи) — только secret store / Docker secrets; **не** логировать полные конфиги с секретами.
|
||
|
||
### Поставка и совместимость
|
||
|
||
- В **Dockerfile** / Compose зафиксировать **версию образа BIRD 2** (тег minor или digest), совпадающую с образом, в котором выполняется `**bird -p`** в CI ([§15](#15-cicd-gitea-actions)).
|
||
- **Статический каркас** (router id, локальные интерфейсы, операторские правки) — в файлах **вне** автогенерируемых фрагментов; сгенерированное — только в согласованных путях `bird.d/` (см. [§8](#8-генерация-bird-и-ревизии), [§10](#10-реплика-evobgp-node)).
|
||
|
||
### Порядок внедрения (уточнение)
|
||
|
||
Перед полным набором микросервисов целесообразен параллельный этап: **контракт OpenAPI + каркас `internal/birdfmt` + golden-тесты + проверка `bird -p` в CI** ([§14](#14-тестирование-bird2-и-матрица-сценариев), [§15](#15-cicd-gitea-actions)), затем миграции PG и один вертикальный сценарий (например `IP_RANGES`).
|
||
|
||
---
|
||
|
||
## 14. Тестирование BIRD2 и матрица сценариев
|
||
|
||
Цель — покрыть **поверхность генератора** EvoBGP, а не весь язык BIRD. Комбинации фиксируются **матрицей** и каталогом сценариев в репозитории (`internal/birdfmt/testdata/scenarios/`).
|
||
|
||
### Матрица покрытия (ориентир)
|
||
|
||
|
||
| Измерение | Варианты для покрытия |
|
||
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||
| Роль спикера | master (полный набор фрагментов); реплика / **бандл** для `evobgp-node` (manifest + подмножество includes + локальный `local.conf`) |
|
||
| Типы модулей (выход render) | `AS_PREFIXES`, `CDN_CIDRS`, `DOMAINS` (материализованные префиксы), `IP_RANGES` — минимум по одному сценарию; комбинация **2+ типов** в одной ревизии |
|
||
| Пиры | при поддержке каркасом — только static; **1× IPv4**, **1× IPv6**, несколько пиров, смешанный v4/v6 |
|
||
| Community | каждый поддерживаемый `kind` в справочнике + вариант без community (default) |
|
||
| Фильтры | экспорт «разрешить анонс»; при генерации — отрицательные кейсы (reject) |
|
||
| Граничные данные | пустой префикс-лист; один префикс; большой список (нагрузка на размер файла) |
|
||
| Шаблоны include | каждый именуемый фрагмент из `internal/birdfmt` участвует хотя бы в одном интеграционном сценарии |
|
||
|
||
|
||
По мере расширения генератора матрица **дополняется**; регрессия — новыми строками в таблице тестов и при необходимости новыми подкаталогами сценариев.
|
||
|
||
### Виды тестов
|
||
|
||
1. **Unit / snapshot (Go):** пакет `internal/birdfmt` — вход из фикстур, выход сравнивается с `*.golden` или `txtar`.
|
||
2. **Синтаксис BIRD в CI:** для каждого сценария с `bird.conf` выполняется `**bird -c … -p`** в контейнере с **той же major/minor версией BIRD 2**, что в production ([§13](#13-снижение-рисков-меры-и-процессы)).
|
||
3. **Позже:** контрактные тесты HTTP по `docs/openapi.yaml` и сценариям из [§7](#7-rest-api) после реализации `internal/httpapi`.
|
||
|
||
---
|
||
|
||
## 15. CI/CD (Gitea Actions)
|
||
|
||
- Workflows: каталог `**.gitea/workflows/`** в корне репозитория; синтаксис совместим с GitHub Actions ([документация Gitea Actions](https://docs.gitea.com/usage/actions/quickstart/)).
|
||
- Нужен зарегистрированный **act runner** с меткой `ubuntu-latest` (или согласованной с инсталляцией) и при job с Docker — доступ **Docker** на runner.
|
||
- Рекомендуемый pipeline: **lint OpenAPI** (`npx @redocly/cli lint docs/openapi.yaml`), `**go vet` / `go test` / `go build ./...`**, **проверка всех `testdata/scenarios/*/bird.conf` через `bird -p`** (см. workflow в репозитории).
|
||
|
||
---
|
||
|
||
## 16. Риски и этапы внедрения
|
||
|
||
### Риски
|
||
|
||
- Домены и устаревшие IP; DoH down; IRR/RIR vs локальные фильтры; community-ошибки; дубли при master+node; **microVPS:** переполнение диска логами/ревизиями, OOM без swap.
|
||
|
||
### Этапы
|
||
|
||
1. Monorepo Go, миграции **PostgreSQL** (основной путь), опционально SQLite для `microVPS_sqlite`, Compose `reference` и `microVPS`.
|
||
2. Один тип модуля end-to-end; render + BIRD.
|
||
3. Расписания CDN/модуля; deploy + agent.
|
||
4. Ревизии, rollback, bundle API.
|
||
5. **evobgp-node** на отдельной ВМ; observability.
|
||
6. Hardening, документация операторская.
|
||
7. **Web UI** в отдельном контейнере ([§17](#17-web-ui-svelte-shadcn-svelte)): полный UX по спискам, расписанию и мониторингу.
|
||
|
||
---
|
||
|
||
## 17. Web UI (Svelte, shadcn-svelte)
|
||
|
||
Цель — **единая операторская поверхность** поверх уже описанного REST ([§7](#7-rest-api)) и observability ([§4](#4-сервисы-сравнение-профилей), этапы в [§16](#16-риски-и-этапы-внедрения)), без дублирования бизнес-логики на фронтенде: UI только вызывает API и визуализирует состояние.
|
||
|
||
### Стек и упаковка
|
||
|
||
- **Svelte** (актуальная ветка проекта, SvelteKit при необходимости SSR/роутинга) и **shadcn-svelte** — доступные компоненты (формы, таблицы, диалоги, навигация), единый визуальный язык.
|
||
- **Отдельный сервис в Docker Compose** (профиль **reference**): образ со статической сборкой или Node-сервером за reverse-proxy; **не** вшивать UI в `evobgp-api`. Контейнер получает только `VITE_*` / публичный **base URL API** и при необходимости URL метрик/health-прокси (см. ниже).
|
||
- Сборка и публикация артефакта UI — отдельный job в CI ([§15](#15-cicd-gitea-actions)) по мере появления кода в репозитории (например `web/` или `ui/`).
|
||
|
||
### Покрытие user experience
|
||
|
||
1. **Списки и сущности** — модули префиксов, ревизии, пиры, community, DoH-профили, спикеры: просмотр, фильтрация, создание/редактирование там, где это отражено в OpenAPI; связь с jobs и аудитом ([§7.10](#710-задачи-job_audit)).
|
||
2. **Расписание** — триггеры и окна обновления модулей/ETL, ручной refresh, очередь задач и статусы без «чёрного ящика» ([§7](#7-rest-api), scheduler в [§1](#1-структура-репозитория-файлы-и-пакеты)).
|
||
3. **Мониторинг системы** — дашборд health API, агентов и нод; интеграция с метриками/алертами (Prometheus/Grafana или встроенные виджеты по публичным эндпоинтам); наглядное состояние BGP-сессий и последних deploy/revision там, где данные доступны через API или безопасный read-only прокси.
|
||
|
||
### Безопасность и эксплуатация
|
||
|
||
- Аутентификация и роли — по [§7.13](#713-матрица-прав-роли); UI не хранит секреты вне согласованного потока (cookie/session или OIDC — на этапе проектирования конкретной инсталляции).
|
||
- Для **microVPS** полноценный отдельный контейнер UI **опционален** (можно тот же образ с `profiles` или отключённый сервис), чтобы не раздувать single-node; приоритет — **reference** как эталон операторской панели.
|
||
|
||
---
|
||
|
||
**Соответствие старым именам:** `full` ≈ `reference`, `target_vps` ≈ `microVPS`. |