Update EvoBGP architecture plan with new overview and todos. Refined descriptions for control-plane and data-plane components, clarified roles of services, and added details on client interactions and replica nodes.
This commit is contained in:
@@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
name: EvoBGP архитектура
|
name: EvoBGP архитектура
|
||||||
overview: "Control-plane на Go в Docker: микросервисы (API, планировщик, ingest, генерация BIRD, доставка на узлы), MySQL, брокер очередей; префиксы из AS/CDN/доменов, расписания, DoH, community-справочник, REST, ревизии и откат; data-plane — BIRD и агент на узлах."
|
overview: Control-plane на мастере; BIRD на мастере и опционально на EvoBGP-нодах, стягивающих подписанный бандл (префиксы+пиры+фильтры) для одинаковых правил. Клиенты — внешние BGP-пиры. MySQL/SQLite, Go, REST, ревизии.
|
||||||
todos:
|
todos:
|
||||||
- id: schema-mysql
|
- id: schema-mysql
|
||||||
content: "Схема MySQL: модули, расписания, CDN-источники, DoH-профили, справочник community, привязки, пиры, узлы, ревизии, jobs"
|
content: "Схема БД (MySQL prod + SQLite edge): модули, CDN, DoH, community, пиры, ревизии, jobs"
|
||||||
status: pending
|
status: pending
|
||||||
- id: bird-generator
|
- id: bird-generator
|
||||||
content: Определить формат bird.conf фрагментов, фильтры и точки reload/configure
|
content: Определить формат bird.conf фрагментов, фильтры и точки reload/configure
|
||||||
@@ -12,17 +12,20 @@ todos:
|
|||||||
content: Спецификация REST (refresh модуля, apply, preview, rollback) и async jobs
|
content: Спецификация REST (refresh модуля, apply, preview, rollback) и async jobs
|
||||||
status: pending
|
status: pending
|
||||||
- id: node-agent
|
- id: node-agent
|
||||||
content: Протокол доставки конфига на до 20 узлов (агент + версии + canary)
|
content: evobgp-agent на мастере; опционально evobgp-node (pull бандла + BIRD на реплике)
|
||||||
status: pending
|
status: pending
|
||||||
- id: observability
|
- id: observability
|
||||||
content: Метрики, алерты на дрейф префиксов, статус пиров
|
content: Метрики, алерты на дрейф префиксов, статус пиров
|
||||||
status: pending
|
status: pending
|
||||||
- id: docker-ms
|
- id: docker-ms
|
||||||
content: Dockerfile сервисов, compose (dev), сети/volumes, healthchecks
|
content: Dockerfile, compose profiles full vs edge_1g, сети/volumes, healthchecks
|
||||||
status: pending
|
status: pending
|
||||||
- id: go-modules
|
- id: go-modules
|
||||||
content: Структура Go-модулей, общие пакеты (db, models, bird templating)
|
content: Структура Go-модулей, общие пакеты (db, models, bird templating)
|
||||||
status: pending
|
status: pending
|
||||||
|
- id: replica-bundle
|
||||||
|
content: Формат бандла ревизии, подпись, API выдачи, evobgp-node pull и локальный BIRD
|
||||||
|
status: pending
|
||||||
isProject: false
|
isProject: false
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -32,17 +35,20 @@ isProject: false
|
|||||||
|
|
||||||
## Целевая картина (логическая)
|
## Целевая картина (логическая)
|
||||||
|
|
||||||
- **BIRD** — источник истины на уровне маршрутизации; **MySQL** — для политики, ревизий и материализованных префиксов.
|
- **BIRD** на стороне **вашего сервера** — процесс, который **анонсирует** префиксы (и ведёт сессии с соседями). **Клиенты** — это **удалённые BGP-пиры** (до ~20 и более), которые **подключаются к этому серверу** и **получают** объявления; у них **свой** стек (не evobgp, не ваш Docker).
|
||||||
- **Быстрота:** очередь задач, идempotent-воркеры, при необходимости `birdc configure` после атомарной подмены include-файлов.
|
- **MySQL/SQLite** — политика: какие префиксы, какие community, **какие пиры** в `protocol bgp` и кому что экспортировать.
|
||||||
|
- **Быстрота:** очередь задач, идempotent-воркеры, `birdc configure` после подмены include на **хосте BIRD**.
|
||||||
|
|
||||||
### Диаграмма: микросервисы и Docker (control-plane + data-plane)
|
**Терминология:** **клиенты** — внешние BGP-пиры (их роутеры), не контейнеры EvoBGP. **Мастер** — control-plane + первичный BIRD (или только control-plane, если BIRD только на границе). **Опционально** — одна или несколько **EvoBGP-нод**: только стягивание готового бандла с мастера и локальный BIRD с **теми же** префиксами/фильтрами/правилами пиров (см. раздел «Реплика-нода»).
|
||||||
|
|
||||||
|
### Диаграмма: микросервисы и Docker (control-plane + BGP-сервер)
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TB
|
flowchart TB
|
||||||
subgraph edge [Периметр]
|
subgraph edge [Периметр]
|
||||||
LB[Traefik или Nginx]
|
LB[Traefik или Nginx]
|
||||||
end
|
end
|
||||||
subgraph docker [Docker host или кластер]
|
subgraph docker [Docker host control-plane]
|
||||||
API[evobgp-api Go]
|
API[evobgp-api Go]
|
||||||
SCH[evobgp-scheduler Go]
|
SCH[evobgp-scheduler Go]
|
||||||
ING[evobgp-ingest Go]
|
ING[evobgp-ingest Go]
|
||||||
@@ -51,11 +57,23 @@ flowchart TB
|
|||||||
MQ[(NATS или Redis Streams)]
|
MQ[(NATS или Redis Streams)]
|
||||||
DB[(MySQL)]
|
DB[(MySQL)]
|
||||||
end
|
end
|
||||||
subgraph nodes [До 20 узлов]
|
subgraph speaker [Хост BGP сервера]
|
||||||
AG1[evobgp-agent Go]
|
AG1[evobgp-agent опционально]
|
||||||
BR1[BIRD]
|
BR1[BIRD анонсирует префиксы]
|
||||||
AG1 --> BR1
|
AG1 --> BR1
|
||||||
end
|
end
|
||||||
|
subgraph clients [Клиенты BGP пиры]
|
||||||
|
C1[BGP клиент 1]
|
||||||
|
C2[BGP клиент N]
|
||||||
|
end
|
||||||
|
subgraph replicaOpt [Опционально реплика]
|
||||||
|
NODE[evobgp-node]
|
||||||
|
BR2[BIRD реплика]
|
||||||
|
NODE --> BR2
|
||||||
|
CR[Клиенты к реплике]
|
||||||
|
BR2 <-->|BGP| CR
|
||||||
|
end
|
||||||
|
API -->|bundle mTLS| NODE
|
||||||
LB --> API
|
LB --> API
|
||||||
API --> DB
|
API --> DB
|
||||||
API --> MQ
|
API --> MQ
|
||||||
@@ -67,7 +85,9 @@ flowchart TB
|
|||||||
REN --> DB
|
REN --> DB
|
||||||
DEP --> MQ
|
DEP --> MQ
|
||||||
DEP --> DB
|
DEP --> DB
|
||||||
DEP -->|mTLS pull или push| AG1
|
DEP -->|конфиг на хост BIRD| AG1
|
||||||
|
BR1 <-->|BGP сессии| C1
|
||||||
|
BR1 <-->|BGP сессии| C2
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@@ -75,17 +95,18 @@ flowchart TB
|
|||||||
Назначение сервисов (можно объединять на раннем MVP, границы — контракты между пакетами):
|
Назначение сервисов (можно объединять на раннем MVP, границы — контракты между пакетами):
|
||||||
|
|
||||||
|
|
||||||
| Сервис | Роль |
|
| Сервис | Роль |
|
||||||
| -------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| **evobgp-api** | REST, аутентификация, CRUD сущностей, постановка задач (`refresh`, `apply`, `rollback`), `GET /jobs`. |
|
| **evobgp-api** | REST, аутентификация, CRUD сущностей, постановка задач (`refresh`, `apply`, `rollback`), `GET /jobs`. |
|
||||||
| **evobgp-scheduler** | Читает интервалы модулей и CDN-строк из MySQL, публикует события «пора обновить модуль/источник» в очередь. |
|
| **evobgp-scheduler** | Читает интервалы модулей и CDN-строк из MySQL, публикует события «пора обновить модуль/источник» в очередь. |
|
||||||
| **evobgp-ingest** | Fetch CDN, DoH-резолв доменов, загрузка AS/префиксов; пишет материализованные строки и сырые метаданные в MySQL. |
|
| **evobgp-ingest** | Fetch CDN, DoH-резолв доменов, загрузка AS/префиксов; пишет материализованные строки и сырые метаданные в MySQL. |
|
||||||
| **evobgp-render** | Собирает итоговый набор префиксов + community, создаёт ревизию, генерирует артефакты BIRD (текст конфигов). |
|
| **evobgp-render** | Собирает итоговый набор префиксов + community, создаёт ревизию, генерирует артефакты BIRD (текст конфигов). |
|
||||||
| **evobgp-deploy** | Доставка артефактов на узлы, учёт `node_config_version`, canary. |
|
| **evobgp-deploy** | Доставка конфига **на мастерский** BIRD через `evobgp-agent`; **публикация** подписанного **бандла** для `evobgp-node` (HTTP или объектное хранилище). |
|
||||||
| **evobgp-agent** | Отдельный образ для узла: получение конфига, запись в volume, вызов `birdc`, отчёт о версии. |
|
| **evobgp-agent** | На **мастерском** хосте BIRD: применить файлы от `evobgp-deploy`, `birdc`, отчитаться о ревизии. |
|
||||||
|
| **evobgp-node** | **Опционально** на удалённой площадке: периодически **скачивает** с мастера **подписанный бандл** ревизии (все include префиксов, фильтры, `peers.conf`), проверяет подпись/хэши, раскладывает на диск, вызывает локальный `birdc`. **Без** своей MySQL и без ingest — только **consumer** артефакта. |
|
||||||
|
|
||||||
|
|
||||||
Инфраструктурные контейнеры: **MySQL**, **брокер очередей** (NATS JetStream или Redis), опционально **Valkey/Redis** для кэша и rate-limit по модулям.
|
**Инфраструктурные контейнеры:** **MySQL**, **брокер очередей** (NATS JetStream или Redis), опционально **Valkey/Redis** для кэша и rate-limit по модулям.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -94,19 +115,19 @@ flowchart TB
|
|||||||
Рекомендуемые группы таблиц:
|
Рекомендуемые группы таблиц:
|
||||||
|
|
||||||
|
|
||||||
| Область | Назначение |
|
| Область | Назначение |
|
||||||
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||||
| **Модули** | Тип: `AS_PREFIXES` / `CDN_CIDRS` / `DOMAINS`; включён/выключен; приоритет; ссылки на расписание и (для доменов) DoH-профиль. |
|
| **Модули** | Тип: `AS_PREFIXES` / `CDN_CIDRS` / `DOMAINS`; включён/выключен; приоритет; ссылки на расписание и (для доменов) DoH-профиль. |
|
||||||
| **Расписания обновления** | Базовый интервал на **модуль** (`refresh_interval_sec`, cron или interval); см. ниже про переопределение на CDN. |
|
| **Расписания обновления** | Базовый интервал на **модуль** (`refresh_interval_sec`, cron или interval); см. ниже про переопределение на CDN. |
|
||||||
| **Источники CDN внутри модуля** | Для типа `CDN_CIDRS`: несколько записей «URL/статический список» на модуль; у **каждой** записи свой опциональный `refresh_interval_sec` (если NULL — брать интервал модуля). |
|
| **Источники CDN внутри модуля** | Для типа `CDN_CIDRS`: несколько записей «URL/статический список» на модуль; у **каждой** записи свой опциональный `refresh_interval_sec` (если NULL — брать интервал модуля). |
|
||||||
| **Профили DoH** | URL HTTPS DoH (`https://…/dns-query`), опционально имя для SNI, таймауты, доверие к сертификату (политика); привязка к модулям `DOMAINS` или глобальный default. |
|
| **Профили DoH** | URL HTTPS DoH (`https://…/dns-query`), опционально имя для SNI, таймауты, доверие к сертификату (политика); привязка к модулям `DOMAINS` или глобальный default. |
|
||||||
| **Содержимое модуля** | AS и префиксы; CDN-строки; FQDN; у каждой сущности — **привязка к community** (FK). |
|
| **Содержимое модуля** | AS и префиксы; CDN-строки; FQDN; у каждой сущности — **привязка к community** (FK). |
|
||||||
| **Справочник BGP community** | Канонические записи: `standard` (65535:123), `large` (x:y:z) при необходимости, человекочитаемое имя, описание, `tenant_id`. |
|
| **Справочник BGP community** | Канонические записи: `standard` (65535:123), `large` (x:y:z) при необходимости, человекочитаемое имя, описание, `tenant_id`. |
|
||||||
| **Привязки community** | Связь «сущность → community»: для **домена**, **ASN**, **префикса/CIDR** (в т.ч. из CDN-листа) — `community_id`; при генерации BIRD маршруты/фильтры получают соответствующий `bgp_community.add()`. |
|
| **Привязки community** | Связь «сущность → community»: для **домена**, **ASN**, **префикса/CIDR** (в т.ч. из CDN-листа) — `community_id`; при генерации BIRD маршруты/фильтры получают соответствующий `bgp_community.add()`. |
|
||||||
| **Пиры** | neighbor IP, ASN, пароли/ключи (лучше ссылка на секреты), BGP параметры, привязка к группе узлов. |
|
| **Пиры (клиенты)** | Удалённый BGP-сосед, который **получает** анонсы с вашего BIRD: neighbor IP, remote ASN, секреты, `export`/`import` политика, теги (группы клиентов). Одна строка ≈ одна сессия к одному клиентскому роутеру. |
|
||||||
| **Узлы** | Идентификатор узла (hostname), роль, теги для «каким пирам/политикам подчиняться». |
|
| **Экземпляр BIRD (`bgp_speaker`)** | Мастерский и/или репликовый BIRD: `role` (`master` / `replica`), hostname, `last_applied_revision_id`, для реплики — URL мастера / токен ноды (или вне секретов). Реплика **не** ведёт ingest; только pull бандла. |
|
||||||
| **История (append-only)** | Снимок состояния или дифф после каждого успешного применения; `revision_id`, автор (API key/user), timestamp. |
|
| **История (append-only)** | Снимок состояния или дифф после каждого успешного применения; `revision_id`, автор (API key/user), timestamp. |
|
||||||
| **Журнал заданий** | Очередь «пересобрать модуль X», «откатить на revision Y», статус, ошибки. |
|
| **Журнал заданий** | Очередь «пересобрать модуль X», «откатить на revision Y», статус, ошибки. |
|
||||||
|
|
||||||
|
|
||||||
### Расписание: модуль и отдельно каждый CDN-источник
|
### Расписание: модуль и отдельно каждый CDN-источник
|
||||||
@@ -134,7 +155,7 @@ flowchart TB
|
|||||||
|
|
||||||
**Откат:** не переписывать текущее состояние «вручную», а хранить **ревизии** (например JSON-снимок или нормализованные строки в history-таблицах). Операция rollback = `INSERT` новой ревизии с содержимым выбранной старой + триггер перегенерации. Так история остаётся линейной и аудируемой.
|
**Откат:** не переписывать текущее состояние «вручную», а хранить **ревизии** (например JSON-снимок или нормализованные строки в history-таблицах). Операция rollback = `INSERT` новой ревизии с содержимым выбранной старой + триггер перегенерации. Так история остаётся линейной и аудируемой.
|
||||||
|
|
||||||
**Дополнительно:** мягкие блокировки (`SELECT ... FOR UPDATE` на уровне модуля/узла при применении), чтобы два REST-вызова не портили друг друга.
|
**Дополнительно:** мягкие блокировки (`SELECT ... FOR UPDATE` на уровне модуля или экземпляра `bgp_speaker` при применении), чтобы два REST-вызова не портили друг друга.
|
||||||
|
|
||||||
### Диаграмма ETL (от источников до BIRD и ревизий)
|
### Диаграмма ETL (от источников до BIRD и ревизий)
|
||||||
|
|
||||||
@@ -156,7 +177,7 @@ flowchart LR
|
|||||||
ART[Артефакты BIRD]
|
ART[Артефакты BIRD]
|
||||||
end
|
end
|
||||||
subgraph out [Выход]
|
subgraph out [Выход]
|
||||||
BIRD[BIRD на узлах]
|
BIRD[BIRD сервер анонсов]
|
||||||
end
|
end
|
||||||
CDN --> NORM
|
CDN --> NORM
|
||||||
DOH --> NORM
|
DOH --> NORM
|
||||||
@@ -182,6 +203,7 @@ erDiagram
|
|||||||
tenant ||--o{ module : owns
|
tenant ||--o{ module : owns
|
||||||
tenant ||--o{ bgp_community : owns
|
tenant ||--o{ bgp_community : owns
|
||||||
tenant ||--o{ bgp_peer : owns
|
tenant ||--o{ bgp_peer : owns
|
||||||
|
tenant ||--o{ bgp_speaker : owns
|
||||||
doh_profile ||--o{ module : uses
|
doh_profile ||--o{ module : uses
|
||||||
module ||--o{ module_cdn_source : contains
|
module ||--o{ module_cdn_source : contains
|
||||||
module ||--o{ module_domain_entry : contains
|
module ||--o{ module_domain_entry : contains
|
||||||
@@ -189,8 +211,7 @@ erDiagram
|
|||||||
bgp_community ||--o{ module_domain_entry : tags
|
bgp_community ||--o{ module_domain_entry : tags
|
||||||
bgp_community ||--o{ module_as_entry : tags
|
bgp_community ||--o{ module_as_entry : tags
|
||||||
bgp_community ||--o{ module_cdn_source : tags
|
bgp_community ||--o{ module_cdn_source : tags
|
||||||
bgp_node ||--o{ node_peer_binding : has
|
bgp_speaker ||--o{ bgp_peer : optional_scope
|
||||||
bgp_peer ||--o{ node_peer_binding : has
|
|
||||||
module ||--o{ config_revision : produces
|
module ||--o{ config_revision : produces
|
||||||
config_revision ||--o{ revision_materialized_prefix : snapshot
|
config_revision ||--o{ revision_materialized_prefix : snapshot
|
||||||
module ||--o{ job_audit : async_tasks
|
module ||--o{ job_audit : async_tasks
|
||||||
@@ -212,9 +233,8 @@ erDiagram
|
|||||||
| **module_domain_entry** | Одна строка FQDN в модуле `DOMAINS`. | `module_id`, `fqdn`, `community_id`, опционально переопределение интервала; материализованные поля после резолва можно хранить в отдельной таблице или здесь (`last_resolved_at`, хэш ответа). | Ingest (DoH), render. |
|
| **module_domain_entry** | Одна строка FQDN в модуле `DOMAINS`. | `module_id`, `fqdn`, `community_id`, опционально переопределение интервала; материализованные поля после резолва можно хранить в отдельной таблице или здесь (`last_resolved_at`, хэш ответа). | Ingest (DoH), render. |
|
||||||
| **module_as_entry** | Одна строка в модуле `AS_PREFIXES`: ASN и/или явный префикс. | `module_id`, `asn`, `prefix` (nullable если задаётся только ASN), `community_id`. | API, render (если данные не из внешнего IRR — тогда расширить провайдером). |
|
| **module_as_entry** | Одна строка в модуле `AS_PREFIXES`: ASN и/или явный префикс. | `module_id`, `asn`, `prefix` (nullable если задаётся только ASN), `community_id`. | API, render (если данные не из внешнего IRR — тогда расширить провайдером). |
|
||||||
| **bgp_community** | Справочник BGP community для экспорта в BIRD. | `tenant_id`, `name`, `kind` (standard/large/extended), числовые поля значения, уникальность в рамках tenant. | API, render (генерация `filter` / `define`). |
|
| **bgp_community** | Справочник BGP community для экспорта в BIRD. | `tenant_id`, `name`, `kind` (standard/large/extended), числовые поля значения, уникальность в рамках tenant. | API, render (генерация `filter` / `define`). |
|
||||||
| **bgp_peer** | Описание BGP-соседа (логический пир). | `tenant_id`, neighbor IP, remote ASN, локальные политики, **ссылка на секрет** (MD5/TC), `group_name`/`tags` для выбора на узлах. | API, render (`peers.conf`), deploy. |
|
| **bgp_peer** | Клиентский BGP-пир. | Как выше; `bgp_speaker_id`: **NULL** — включить в **бандлы всех** спикеров (зеркало); иначе только выбранный мастер/реплика. | API, render, упаковка бандла. |
|
||||||
| **bgp_node** | Узел сети, где крутится BIRD и агент. | `tenant_id`, `hostname`, `api_endpoint` или идентификатор для mTLS, `tags`, `last_applied_revision_id`. | deploy, API (статус), мониторинг. |
|
| **bgp_speaker** | Экземпляр BIRD: **master** или **replica**. | `role`, endpoint для agent/deploy, `last_applied_revision_id`; для replica — учётные данные к API бандла (вне БД). | deploy, бандлы, нода. |
|
||||||
| **node_peer_binding** | Какие пиры подняты на каком узле (many-to-many). | `bgp_node_id`, `bgp_peer_id`, возможно переопределение при необходимости. | API, render (генерация только релевантных сессий на узел). |
|
|
||||||
| **config_revision** | Неизменяемая точка истории после успешного применения политики. | `id`, `module_id` или `NULL` (глобальная ревизия), `created_at`, `author`, `hash` префикс-сета, ссылка на артефакт (путь/URL в object storage), `parent_revision_id` (для отката как «новая ревизия со старым содержимым»). | render, deploy, API (rollback, audit). |
|
| **config_revision** | Неизменяемая точка истории после успешного применения политики. | `id`, `module_id` или `NULL` (глобальная ревизия), `created_at`, `author`, `hash` префикс-сета, ссылка на артефакт (путь/URL в object storage), `parent_revision_id` (для отката как «новая ревизия со старым содержимым»). | render, deploy, API (rollback, audit). |
|
||||||
| **revision_materialized_prefix** | Снимок итоговых префиксов для ревизии (для быстрого diff и отката без пересчёта из сырья). | `revision_id`, `prefix`, `cidr_len`, `community_id`, `source` (модуль/тип). | render (запись), API (preview, diff), откат. |
|
| **revision_materialized_prefix** | Снимок итоговых префиксов для ревизии (для быстрого diff и отката без пересчёта из сырья). | `revision_id`, `prefix`, `cidr_len`, `community_id`, `source` (модуль/тип). | render (запись), API (preview, diff), откат. |
|
||||||
| **job_audit** | Журнал асинхронных операций (дополняет брокер, не заменяет его). | `id`, `kind` (refresh / apply / rollback), `module_id`, `status`, `error`, `idempotency_key`, `created_at`. | API, операторы, ретраи. |
|
| **job_audit** | Журнал асинхронных операций (дополняет брокер, не заменяет его). | `id`, `kind` (refresh / apply / rollback), `module_id`, `status`, `error`, `idempotency_key`, `created_at`. | API, операторы, ретраи. |
|
||||||
@@ -258,7 +278,7 @@ flowchart TB
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
**Зависимости по данным:** все мутирующие сервисы согласуются через **MySQL** и **очередь**; агент не ходит в MySQL напрямую, только к API/deploy или к артефакт-хранилищу (S3/minio + подпись), в зависимости от выбранной реализации `evobgp-deploy`.
|
**Зависимости по данным:** все мутирующие сервисы согласуются через **MySQL** и **очередь**; **evobgp-agent** на хосте BIRD не ходит в MySQL напрямую — получает артефакты от `evobgp-deploy` (pull/mTLS/SSH). **Клиентские роутеры** в БД не фигурируют как хосты EvoBGP — только как записи `bgp_peer`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -294,10 +314,11 @@ flowchart TB
|
|||||||
Минимальный набор эндпоинтов:
|
Минимальный набор эндпоинтов:
|
||||||
|
|
||||||
- `POST /modules/{id}/refresh` — пересобрать только этот модуль (CDN fetch / DNS refresh / перечитать AS-данные).
|
- `POST /modules/{id}/refresh` — пересобрать только этот модуль (CDN fetch / DNS refresh / перечитать AS-данные).
|
||||||
- `POST /apply` или `POST /nodes/{id}/apply` — сгенерировать конфиг и применить (см. раздел про узлы).
|
- `POST /apply` или `POST /speakers/{id}/apply` (или `/bird/apply` при одном сервере) — сгенерировать конфиг и применить на **хосте BIRD-сервера**; клиентские пиры подтянут изменения после перезагрузки сессии/политики export в BIRD.
|
||||||
- `GET /revisions`, `POST /revisions/{id}/rollback`.
|
- `GET /revisions`, `POST /revisions/{id}/rollback`.
|
||||||
- `POST /peers` / `PATCH /peers/{id}` — добавление/изменение пира; опционально `POST /peers/{id}/apply`.
|
- `POST /peers` / `PATCH /peers/{id}` — добавление/изменение пира; опционально `POST /peers/{id}/apply`.
|
||||||
- CRUD для **профилей DoH**, **справочника community**, **расписаний** (если вынесены из PATCH модуля) — по необходимости UI/автоматизации.
|
- CRUD для **профилей DoH**, **справочника community**, **расписаний** (если вынесены из PATCH модуля) — по необходимости UI/автоматизации.
|
||||||
|
- Для реплик: `**GET /v1/speakers/{id}/bundle/{revision}`**, `**GET .../revisions/latest`**, опционально enrollment нод.
|
||||||
|
|
||||||
Ответы — **202 Accepted** + `job_id`, если работа асинхронная; **GET /jobs/{id}** для статуса.
|
Ответы — **202 Accepted** + `job_id`, если работа асинхронная; **GET /jobs/{id}** для статуса.
|
||||||
|
|
||||||
@@ -324,18 +345,54 @@ flowchart TB
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. Масштаб до ~20 узлов: предложения по улучшению
|
## 6. Масштаб: до ~20 клиентских BGP-пиров и один (или несколько) BIRD-сервер
|
||||||
|
|
||||||
1. **Единый control-plane, много data-plane:** один API+воркер (или небольшой кластер API за балансировщиком), на каждом узле — **агент** (лёгкий daemon), который тянет готовый конфиг/дифф по **mTLS** или получает push через message queue. Так не нужен SSH с центра на 20 хостов.
|
1. **Control-plane на мастере:** Docker с API/воркерами; **мастерский BIRD** + `evobgp-agent`. **Опционально** дополнительные хосты только с `**evobgp-node` + BIRD** (реплики), без своей БД. Клиенты — внешние пиры к мастеру и/или к репликам.
|
||||||
2. **Идентичность конфигурации:** таблица `node_config_version`; после деплоя агент репортит `applied_revision`. Дашборд «какой узел отстаёт».
|
2. **Версия конфига:** `bgp_speaker.last_applied_revision_id` (или файл-маркер на хосте BIRD); агент репортит применённую ревизию.
|
||||||
3. **Canary / поэтапный rollout:** сначала 1–2 узла, затем остальные — снижает риск массового bad announce.
|
3. **Canary:** при **нескольких** `bgp_speaker` — сначала обновить один POP; при одном сервере — canary через **группу пиров** / отдельный `export` filter для подмножества `bgp_peer`, затем полный rollout.
|
||||||
4. **Очередь и rate-limit:** массовый refresh всех CDN-модулей не должен DDOSить внешние списки; **per-module cooldown** в воркере.
|
4. **Очередь и rate-limit:** массовый refresh CDN не должен DDOSить внешние списки; **per-module cooldown** в воркере.
|
||||||
5. **Наблюдаемость:** метрики (Prometheus): время генерации, размер префикс-сета, ошибки DNS/CDN, статус BIRD-сессий (через экспортер или scrape `birdc`). Алерты на **аномальный рост/падение** числа префиксов.
|
5. **Наблюдаемость:** метрики: размер префикс-сета, ошибки DNS/CDN, **состояние BGP-сессий с клиентами** (`birdc show protocols`). Алерты на скачок/пропадание префиксов и на **Down** сессий к критичным клиентам.
|
||||||
6. **Консистентность БД:** транзакции при записи ревизии + смене «текущего» указателя; миграции через Flyway/Liquibase или аналог.
|
6. **Консистентность БД:** транзакции при ревизиях; миграции `goose`/`golang-migrate`.
|
||||||
7. **Секреты:** пароли BGP не в открытом виде в MySQL — **Vault**, Kubernetes secrets, или зашифрованные поля с KMS.
|
7. **Секреты:** MD5/password пиров не в открытом виде — Vault/K8s secrets.
|
||||||
8. **Multi-tenant (если нужно):** `tenant_id` на модулях и пирах с самого начала — дешевле, чем латеральный рефакторинг.
|
8. **Multi-tenant:** `tenant_id` на модулях и пирах при необходимости.
|
||||||
9. **Dry-run:** `POST .../preview` возвращает diff префиксов и фрагмент BIRD без применения — обязателен для операций с 20 узлами.
|
9. **Dry-run:** `POST .../preview` перед apply на BIRD-сервер — обязателен при рискованных изменениях.
|
||||||
10. **Резервный путь:** локальный last-known-good конфиг на узле, если центр недоступен (только чтение, без изменения политики до восстановления связи).
|
10. **Резервный путь:** last-known-good конфиг **на хосте BIRD**, если control-plane недоступен.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Опциональная EvoBGP-нода: стянуть конфиг с мастера и поднять BIRD
|
||||||
|
|
||||||
|
**Цель:** на отдельной площадке запустить **второй (или N-й) BIRD**, чтобы клиенты могли строить сессии **и к мастеру, и к ноде**, получая **одинаковые** наборы префиксов, community и **те же** правила `export`/`import` (как в сгенерированном конфиге мастера). Control-plane и MySQL на ноде **не нужны**.
|
||||||
|
|
||||||
|
### Поток данных
|
||||||
|
|
||||||
|
1. На **мастере** после `evobgp-render` формируется **бандл ревизии**: каталог файлов, идентичный тому, что уходит на мастерский BIRD (`bird.conf` + includes: префиксы, фильтры, `peers.conf`, при необходимости отдельный фрагмент только для «общей» политики).
|
||||||
|
2. Добавляется **manifest** (JSON): список путей, SHA-256 каждого файла, `revision_id`, `speaker_id` или маркер «полное зеркало политики».
|
||||||
|
3. Бандл **подписывается** ключом мастера (например Ed25519); нода хранит **доверенный публичный** ключ / цепочку.
|
||||||
|
4. `**evobgp-node`** по расписанию или webhook: `GET` (или pull из S3/MinIO с тем же manifest) → проверка подписи и хэшей → атомарная распаковка в каталог BIRD → `birdc configure`.
|
||||||
|
5. Нода репортит на мастер опционально: `POST /nodes/{id}/applied` с `revision_id` (для дашборда «реплика отстаёт»).
|
||||||
|
|
||||||
|
### Одинаковые правила для клиентов на мастере и на ноде
|
||||||
|
|
||||||
|
- **Общая часть бандла** (префикс-листы, `filter`, `define` community) — **байт-в-байт** одинакова на мастере и реплике.
|
||||||
|
- **Пиры:** если в БД у `bgp_peer` задано `speaker_id IS NULL` — пир попадает в бандлы **всех** спикеров с режимом зеркала; если указан конкретный `bgp_speaker_id` — только в бандл этого спикера (мастер vs реплика с разным набором соседей).
|
||||||
|
- **Локальные отличия только на ноде:** через небольшой `**local.conf`** (не из бандла): `router id`, при необходимости `source address` для BGP multihop, локальный `listen` — подставляется `evobgp-node` из env/volume **до** или **после** include общих файлов, без изменения семантики фильтров.
|
||||||
|
|
||||||
|
### API мастера (минимум)
|
||||||
|
|
||||||
|
- `GET /v1/speakers/{id}/revisions/latest` — метаданные и URL/тело бандла.
|
||||||
|
- `GET /v1/speakers/{id}/bundle/{revision_id}` — архив (например `tar.zst`) + заголовок или sidecar с подписью.
|
||||||
|
- Аутентификация ноды: **mTLS** или **Bearer** (токен выдачи при enrollment ноды).
|
||||||
|
|
||||||
|
### Когда полный клон пиров возможен
|
||||||
|
|
||||||
|
Клиенты «одинаково» подключаются, если с их стороны допустимы **две независимые сессии** (к мастеру и к реплике) с **теми же** параметрами политики; **neighbor** в BIRD — IP клиента на стороне реплики/мастера. Если у клиента **разные** source IP к разным серверам — в БД это либо **две** записи `bgp_peer`, либо одна запись с учётом того, как BIRD видит remote (уточняется при внедрении).
|
||||||
|
|
||||||
|
### Риски
|
||||||
|
|
||||||
|
- **Дублирование анонсов** в одну и ту же сеть от двух BIRD с разным `router id` — согласовать с дизайном AS/апстримами (могут быть допустимы как anycast/резерв, могут требовать политики).
|
||||||
|
- **Расхождение ревизий:** мастер обновился, нода отстала — мониторинг `applied_revision` на ноде.
|
||||||
|
- **Компрометация бандла** без подписи недопустима — только проверенные артефакты.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -344,9 +401,85 @@ flowchart TB
|
|||||||
- **Backend:** **Go** (1.22+): REST на `chi` / `echo` / `fiber`; драйвер MySQL — `database/sql` + `sqlc` или GORM по согласованию команды; DNS DoH — HTTP-клиент с проверкой TLS.
|
- **Backend:** **Go** (1.22+): REST на `chi` / `echo` / `fiber`; драйвер MySQL — `database/sql` + `sqlc` или GORM по согласованию команды; DNS DoH — HTTP-клиент с проверкой TLS.
|
||||||
- **Миграции:** `golang-migrate` или `goose`, SQL в репозитории.
|
- **Миграции:** `golang-migrate` или `goose`, SQL в репозитории.
|
||||||
- **Контейнеры:** отдельный **multi-stage Dockerfile** на сервис (минимальный образ `distroless` или `alpine`); `docker compose.yaml` для локальной среды: `mysql`, `nats` или `redis`, сервисы `api`, `scheduler`, `ingest`, `render`, `deploy`, опционально `minio` для артефактов.
|
- **Контейнеры:** отдельный **multi-stage Dockerfile** на сервис (минимальный образ `distroless` или `alpine`); `docker compose.yaml` для локальной среды: `mysql`, `nats` или `redis`, сервисы `api`, `scheduler`, `ingest`, `render`, `deploy`, опционально `minio` для артефактов.
|
||||||
- **BIRD и агент:** BIRD обычно на хосте или в **privileged** контейнере с `CAP_NET_ADMIN` и доступом к сетевому стеку; образ `evobgp-agent` монтирует volume с конфигом и взаимодействует с сокетом `birdc` (монтирование `bird.ctl`).
|
- **BIRD и агент:** BIRD на **мастере** и опционально на **нодах**; `evobgp-agent` — у мастерского BIRD; `**evobgp-node`** — лёгкий образ (pull бандла + локальный BIRD) **без** MySQL. Клиентские роутеры в стек EvoBGP **не входят**.
|
||||||
- **Наблюдаемость:** OpenTelemetry / Prometheus metrics в каждом Go-сервисе; единый `health` endpoint для оркестратора.
|
- **Наблюдаемость:** OpenTelemetry / Prometheus metrics в каждом Go-сервисе; единый `health` endpoint для оркестратора.
|
||||||
|
|
||||||
|
### Хранилище: MySQL и более лёгкие альтернативы
|
||||||
|
|
||||||
|
**MySQL** остаётся разумным выбором для **полного** стека: привычные репликации, бэкапы, несколько инстансов API/воркеров, нормальная конкуренция писателей.
|
||||||
|
|
||||||
|
Для **минимального потребления RAM** и профиля **edge** рассмотреть:
|
||||||
|
|
||||||
|
|
||||||
|
| Вариант | RAM / эксплуатация | Плюсы | Минусы |
|
||||||
|
| ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||||
|
| **SQLite** (файл на volume, драйвер `modernc.org/sqlite` или CGO) | Почти **нет отдельного сервера** — память в процессе приложения; типично **десятки МиБ** на БД при умеренном объёме данных | Один контейнер `evobgp-all` без `mysql`; проще бэкап (копия файла + опционально **Litestream**); SQL и миграции те же с поправкой на диалект | Один писатель в классической модели — **нормально** для одного `evobgp-all`; несколько реплик приложения — нужна осторожность (WAL, или только чтение с реплик + один writer). Не замена кластерному MySQL без доп. продуктов. |
|
||||||
|
| **PostgreSQL** | Обычно **не легче** MySQL на малых инсталляциях | Богатый SQL | Для 1 ГиБ edge — не упрощение. |
|
||||||
|
| **Встраиваемые KV (badger, bbolt)** | Очень мало | Полный контроль | Нет SQL, сложнее отчёты/миграции/операторский доступ — обычно **не окупается**, если уже есть реляционная модель. |
|
||||||
|
|
||||||
|
|
||||||
|
**Рекомендация по коду:** слой repository/DAO с интерфейсами; реализация **MySQL** для `full`, **SQLite** для профиля `edge_sqlite` — миграции через тот же `goose` с **двумя диалектами** (или отдельные папки SQL с условной сборкой). Типы запросов в плане (ревизии, job-очередь, модули) **хорошо укладываются в SQLite** при одном процессе записи.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Профиль развёртывания ~1 ГиБ RAM (edge / маленький VPS)
|
||||||
|
|
||||||
|
Цель — **весь docker-compose на одной ВМ с ≈1 ГиБ RAM** без OOM. Полноценный микросервисный разнобой для этого профиля **отключается**: та же логика, другой **способ упаковки**.
|
||||||
|
|
||||||
|
### Архитектурные решения
|
||||||
|
|
||||||
|
|
||||||
|
| Было (полный стек) | Для 1 ГиБ RAM |
|
||||||
|
| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| 5 отдельных контейнеров Go | **Один** бинарь / **один** образ `evobgp-all`: внутри goroutines — HTTP API, планировщик, ingest, render, deploy-клиент (как подпроцессы логики, не отдельные ОС-процессы). |
|
||||||
|
| NATS / Redis | **Нет брокера:** очередь задач только в **MySQL** (`job_audit` + `SELECT … FOR UPDATE SKIP LOCKED` или аналог). |
|
||||||
|
| Traefik / Nginx | **Прямой** проброс порта на `evobgp-all` (или один `nginx` только если критично — тогда ещё −50–80 МиБ). |
|
||||||
|
| MinIO | Артефакты BIRD на **локальный volume** или в БД (BLOB/текст ограниченно), без object storage. |
|
||||||
|
| Prometheus на том же хосте | **Не крутить** на этой же машине; метрики — опционально pull снаружи или отключить в профиле `edge`. |
|
||||||
|
| MySQL | Опционально заменить на **SQLite** в том же процессе — см. раздел «Хранилище»; тогда **нет контейнера** с СУБД. |
|
||||||
|
|
||||||
|
|
||||||
|
### Контейнеры в compose (минимум)
|
||||||
|
|
||||||
|
**Вариант A (как раньше):** `mysql` + `evobgp-all` — **2 контейнера**.
|
||||||
|
|
||||||
|
**Вариант B (максимально лёгкий):** только `**evobgp-all`**, SQLite-файл на **volume** — **1 контейнер**, минимальный RAM (см. ниже про SQLite).
|
||||||
|
|
||||||
|
### Tuning MySQL под малую память
|
||||||
|
|
||||||
|
Задать через `command` или `my.cnf` (ориентиры, подобрать по замерам):
|
||||||
|
|
||||||
|
- `innodb_buffer_pool_size` — **96–128 МиБ** (не дефолтные сотни МиБ).
|
||||||
|
- `max_connections` — **20–50** (достаточно для одного приложения).
|
||||||
|
- `table_open_cache` / `performance_schema` — снизить или отключить `performance_schema`, если допустимо.
|
||||||
|
- **Не** включать тяжёлые плагины; по возможности **одна** БД без реплики на этом же хосте.
|
||||||
|
|
||||||
|
Ожидаемый RSS MySQL после тюнинга: **~250–400 МиБ** вместо 600+ МиБ «из коробки».
|
||||||
|
|
||||||
|
### Бюджет памяти (порядок)
|
||||||
|
|
||||||
|
**Почему Go не «съедает много» сам по себе:** у статически слинкованного Go-процесса в простое типичный **RSS десятки МиБ** (рантайм, GC, стеки goroutine). Он **существенно легче** типичного JVM/.NET для аналогичной роли. В прошлой оценке диапазон по Go получился завышенно пессимистичным, если читать его как «всегда 120–250 МиБ».
|
||||||
|
|
||||||
|
**Где растёт память у Go в этом приложении:** не сам рантайм, а **работа** — буферы HTTP при скачивании больших CDN-листов, тысячи параллельных DoH-ответов, большие слайсы префиксов при рендере, подключения к MySQL. Пик кратковременно поднимает heap до сотен МиБ, пока GC не соберёт; это можно сдерживать **лимитом concurrency** ingest и `GOGC` (и не держать гигантские строки в памяти целиком).
|
||||||
|
|
||||||
|
|
||||||
|
| Компонент | Простой (baseline) | Пик (тяжёлый ingest / большой diff) |
|
||||||
|
| ------------------------------ | ------------------ | ------------------------------------- |
|
||||||
|
| MySQL (после тюнинга) | **250–400 МиБ** | чуть выше при большом InnoDB workload |
|
||||||
|
| `evobgp-all` (один процесс Go) | **~30–80 МиБ** | **~80–180 МиБ** при нагрузке |
|
||||||
|
| Docker / ядро / page cache | **120–220 МиБ** | **150–250 МиБ** |
|
||||||
|
|
||||||
|
|
||||||
|
**Сумма ориентировочно:** **~400–700 МиБ** в типичном простое, **~500–850 МиБ** в пике — реалистично для **1 ГиБ** с MySQL и ограничением параллелизма.
|
||||||
|
|
||||||
|
**С SQLite (вариант B):** вычитается **~250–400 МиБ** сервера MySQL; остаётся **~150–450 МиБ** в простое и **~250–550 МиБ** в пике — **заметный запас** под 512 МиБ-VM невозможен без ещё более жёстких лимитов, но **1 ГиБ** становится комфортнее. Для **512 МиБ RAM** хоста целиться **только в SQLite** + один бинарь + жёсткий `mem_limit` на контейнер.
|
||||||
|
|
||||||
|
**Риски:** при большом числе модулей/CDN-параллелизма или огромных таблицах ревизий возможен **OOM** — в профиле `edge` задать **лимиты** `mem_limit` в compose и **очередь** тяжёлых задач (строго последовательно или 1–2 воркера).
|
||||||
|
|
||||||
|
### Связь с полным стеком
|
||||||
|
|
||||||
|
Код организовать так, чтобы **те же пакеты** `internal/scheduler`, `internal/ingest` и т.д. собирались в `cmd/evobgp-all` и отдельно в `cmd/evobgp-api`, … для production; **compose profile** `full` vs `edge` выбирает количество контейнеров.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Риски и границы
|
## Риски и границы
|
||||||
@@ -355,14 +488,16 @@ flowchart TB
|
|||||||
- **DoH:** недоступность выбранного резолвера блокирует обновление доменного модуля; иметь **fallback** (второй профиль или кратковременный отказ в смене префиксов с алертом) — по политике эксплуатации.
|
- **DoH:** недоступность выбранного резолвера блокирует обновление доменного модуля; иметь **fallback** (второй профиль или кратковременный отказ в смене префиксов с алертом) — по политике эксплуатации.
|
||||||
- **Согласование «что анонсировать»** с регистрацией в RIR/IRR — отдельная дисциплина; система может лишь **не выходить за заданные в БД границы** (prefix filters).
|
- **Согласование «что анонсировать»** с регистрацией в RIR/IRR — отдельная дисциплина; система может лишь **не выходить за заданные в БД границы** (prefix filters).
|
||||||
- **Community:** ошибка в справочнике или привязке ведёт к неверной маркировке трафика у апстримов; обязательны preview/diff перед apply и аудит изменений справочника.
|
- **Community:** ошибка в справочнике или привязке ведёт к неверной маркировке трафика у апстримов; обязательны preview/diff перед apply и аудит изменений справочника.
|
||||||
|
- **Несколько BIRD с одной политикой (мастер + ноды):** возможны лишние/дублирующие анонсы в зависимости от топологии; проектировать совместно с маршрутизацией в AS.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Предлагаемые этапы внедрения
|
## Предлагаемые этапы внедрения
|
||||||
|
|
||||||
1. Репозиторий Go (monorepo `cmd/<service>` + `internal/`), MySQL-миграции, `docker compose` с MySQL и брокером.
|
1. Репозиторий Go (monorepo `cmd/evobgp-all` + `cmd/<service>` + `internal/`), миграции под **MySQL и SQLite**, `docker compose` с профилями `full`, `edge_1g` (MySQL + при необходимости брокер в `full`), опционально `edge_sqlite` (один контейнер).
|
||||||
2. Сервис `evobgp-api` + `evobgp-ingest` (один тип модуля) + очередь; затем `evobgp-render` и генерация BIRD.
|
2. Сервис `evobgp-api` + `evobgp-ingest` (один тип модуля) + очередь; затем `evobgp-render` и генерация BIRD.
|
||||||
3. `evobgp-scheduler` и политики интервалов (модуль + CDN-строка).
|
3. `evobgp-scheduler` и политики интервалов (модуль + CDN-строка).
|
||||||
4. `evobgp-deploy` + `evobgp-agent`, ревизии и rollback в БД.
|
4. `evobgp-deploy` + `evobgp-agent` на **мастерском** BIRD; публикация бандла; ревизии и rollback в БД.
|
||||||
5. Наблюдаемость, hardening контейнеров (non-root где возможно, read-only root), пилот на 2–3 узлах, затем шаблон для остальных.
|
5. Опционально `**evobgp-node`**: pull бандла, подпись, второй BIRD, пилот с клиентами к мастеру и к ноде.
|
||||||
|
6. Наблюдаемость, hardening.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user