# Диаграммы потоков данных Три диаграммы, поясняющие устройство системы на уровне потоков данных — дополнение к [API.md](API.md) (протокол) и [USAGE.md](USAGE.md) (работа оператора). Диаграммы — в формате Mermaid, рендерятся нативно на GitHub/GitLab и в большинстве современных Markdown-просмотрщиков. Презентационная версия этих же двух разрезов (control plane и data plane) под микросервисный (docker-compose) деплой, с топологией по хостам и профилям `COMPOSE_PROFILES` — [CONTROL_DATA_PLANE.html](CONTROL_DATA_PLANE.html) (самодостаточный HTML-файл, открыть в браузере). ## Как читать диаграммы Единое условное обозначение стрелок для всех диаграмм: | Обозначение | Значение | |---|---| | `-->` тонкая сплошная | Управляющий вызов / API-запрос | | `-.->` пунктирная | Ответ, уведомление, асинхронный результат | | `==>` жирная сплошная | Реальный сетевой трафик проверки (data-plane) — именно то, что валидируется, а не служебный вызов | ## Содержание - [1. Control plane сервиса](#1-control-plane-сервиса) - [2. Поток данных: от валидатора к целевому серверу (egress-проверка)](#2-поток-данных-от-валидатора-к-целевому-серверу-egress-проверка) - [3. Поток данных телеметрии](#3-поток-данных-телеметрии) --- ## 1. Control plane сервиса Кто кем управляет и через какой канал: конфигурация оператора, HTTP API control-api, фоновый оркестратор, база данных, вызовы к OpenStack и опрос со стороны validator-agent/prober. ```mermaid flowchart TB subgraph OP["Оператор"] CFG["control-api.yaml
(bootstrap пустой БД:
validators, sites, targets,
check_types, ip_addresses)"] ENV["control-api.env
(OS_AUTH_URL, OS_TOKEN, ...)"] ADMIN["curl /api/v1/admin/*
(status/ips/validators,
ips submit/cancel,
auto-cycle start/stop,
config CRUD)"] end subgraph CAPI["control-api (управляющая машина, 1 экземпляр)"] HTTP["HTTP API
/api/v1/agents/*
/api/v1/probers/*
/api/v1/admin/*
/healthz"] ORCH["Оркестратор: Tick раз в
poll_interval_seconds
claim → associate FIP →
ожидание self-check →
checking → aggregate → release
+ lease sweep + heartbeat sweep
+ автоцикл (если включён):
очистка → скан FIP → ожидание →
пауза interval_seconds"] DB[("SQLite
validators / ip_queue / sites /
target_groups / check_types /
checks / events / auto_cycle")] OSCLIENT["OpenStack-клиент
(mode: mock | real)"] end OS["OpenStack Neutron
Floating IP API"] VA["validator-agent ×N
(на каждой ВМ-валидаторе)"] PR["prober ×3
(на каждой внешней площадке)"] CFG -->|"читается только один раз,
на пустых таблицах (bootstrap)"| DB ENV -->|"переменные окружения процесса"| OSCLIENT ADMIN --> HTTP HTTP -->|"config/queue CRUD:
источник истины после
первого изменения"| DB HTTP --> ORCH ORCH <--> DB ORCH --> OSCLIENT OSCLIENT -->|"associate / disassociate
floating ip"| OS VA <-->|"register, heartbeat,
GET assignment,
POST self-check/events/
results/complete"| HTTP PR <-->|"register,
GET assignments,
POST results"| HTTP ``` **Пояснение.** `control-api` — единственный компонент с состоянием и единственная точка принятия решений (какой IP кому назначить, когда считать проверку завершённой). `control-api.yaml` используется только как одноразовый bootstrap для четырёх секций (`validators`, `sites`, `targets`, `check_types`) — читается лишь пока соответствующая таблица в БД пуста; `ip_addresses` — отдельный, всегда аддитивный путь постановки в очередь при каждом старте. После bootstrap все изменения этих сущностей, включая состав очереди и принудительные повтор/остановку проверки, идут через `/api/v1/admin/*` — «на лету», без `systemctl restart control-api` (см. [API.md](API.md#управление-очередью-и-конфигурацией)). Оркестратор работает по таймеру независимо от HTTP-запросов — назначение IP валидаторам и агрегация результатов не привязаны к конкретному входящему запросу, читая актуальную конфигурацию из БД на каждом проходе, а не единожды при старте. Опциональный автоцикл — часть того же оркестратора: на каждом тике он читает из таблицы `auto_cycle` флаг `enabled`, интервал и фазу (`idle`/`running`/`waiting`), поэтому включение, выключение и смена интервала действуют без перезапуска, а состояние переживает рестарт (см. [USAGE.md](USAGE.md#автоматический-цикл-проверок)). `validator-agent` и `prober` — активная сторона: они сами инициируют все HTTP-запросы к control-api (pull-модель), сам control-api к ним не обращается. --- ## 2. Поток данных: от валидатора к целевому серверу (egress-проверка) Как валидатор проверяет, что через назначенный ему публичный адрес реально работает исходящий доступ в интернет. ```mermaid flowchart LR subgraph VM["ВМ-валидатор (сервисный проект OpenStack)"] AGENT["validator-agent"] end FIP(["Floating IP
203.0.113.10
(адрес под проверкой,
привязан оркестратором заранее)"]) IPECHO["Внешний IP-echo сервис
(api.ipify.org и т.п.,
вне облака)"] CAPI["control-api"] subgraph TARGETS["Целевые серверы (targets из конфига)"] T1["hub.docker.com"] T2["github.com"] T3["packages.ubuntu.com"] end AGENT ==>|"1 self-check: исходящий трафик
ВМ идёт через FIP (SNAT облака)"| FIP FIP ==>|"1 GET"| IPECHO IPECHO -.->|"2 наблюдаемый исходящий IP"| AGENT AGENT -->|"3 POST self-check {success}"| CAPI AGENT ==>|"4 проверки: тоже через FIP"| FIP FIP ==>|"4 HTTPS GET"| T1 FIP ==>|"4 HTTPS GET"| T2 FIP ==>|"4 ICMP echo"| T3 ``` **Пояснение.** Шаги 1–2 — самопроверка (self-check): агент сам (без участия control-api) обращается к внешнему IP-echo сервису и сравнивает адрес, с которого пришёл его собственный запрос, с адресом, который ему назначен. Ресурс для сравнения обязан быть вне облака: OpenStack применяет SNAT через Floating IP только к трафику, уходящему через внешнюю сеть — обращение к чему-либо внутри проекта (включая сам control-api, если он в той же внутренней сети) показало бы приватный адрес валидатора независимо от корректности привязки FIP. Итог самопроверки агент затем сообщает control-api отдельным вызовом (шаг 3) — это уже управляющий, а не проверяемый трафик. Только после успешного self-check агент переходит к шагу 4 и выполняет проверки из `check_config` (HTTPS/ICMP/опционально SSH) против целей из конфига — тем же путём, через тот же FIP. Каждый результат отправляется обратно в control-api сразу после выполнения (см. диаграмму телеметрии ниже) — сам этот data-plane трафик (шаги 1 и 4) в control-api не проходит и им не наблюдается напрямую, control-api видит только заявленный агентом результат. ### Дополнительно: обратное направление (пробер к валидатору) Тот же Floating IP одновременно проверяется и «снаружи» — с трёх внешних площадок, независимо от исходящих проверок агента: ```mermaid flowchart LR subgraph SITES["3 внешние площадки"] P1["prober site-1"] P2["prober site-2"] P3["prober site-3"] end FIP(["тот же Floating IP
203.0.113.10"]) VM["ВМ-валидатор
(слушает 22/80/443/8080)"] P1 ==>|"TCP connect + ICMP echo"| FIP P2 ==>|"TCP connect + ICMP echo"| FIP P3 ==>|"TCP connect + ICMP echo"| FIP FIP -.-> VM ``` Именно сочетание двух направлений (egress от валидатора и inbound от трёх площадок) и даёт полную картину: адрес может нормально работать «наружу», но быть заблокирован для конкретных внешних сетей — это увидит только inbound-проверка, и наоборот. --- ## 3. Поток данных телеметрии Как результаты проверок и события аудита от validator-agent и трёх проберов попадают в базу данных control-api, агрегируются в итоговый результат и становятся видны оператору. ```mermaid flowchart TB subgraph SOURCES["Источники телеметрии"] VA["validator-agent
(egress-проверки + self-check)"] P1["prober site-1"] P2["prober site-2"] P3["prober site-3"] end subgraph API["HTTP API control-api"] EP1["POST /agents/{id}/events"] EP2["POST /agents/{id}/self-check"] EP3["POST /agents/{id}/results"] EP4["POST /probers/{site_id}/results"] end subgraph DB["SQLite (control-api)"] EVENTS[("events
журнал аудита")] CHECKS[("checks
результаты проверок,
source=egress|inbound-site-N")] IPQ[("ip_queue
state, *_complete,
overall_result")] end AGG["Агрегация (оркестратор):
по достижении всех *_complete
или по таймауту checking_window_seconds"] ADMIN["Оператор:
GET /api/v1/admin/status
GET /api/v1/admin/ips
GET /api/v1/admin/ips/{ip}"] VA -->|"config_received,
self_check_result, ..."| EP1 --> EVENTS VA -->|"success, detected_egress_ip"| EP2 --> EVENTS EP2 -.->|"успех → egress_complete"| IPQ VA -->|"check_type, target,
success, latency_ms"| EP3 --> CHECKS P1 & P2 & P3 -->|"check_type, success,
latency_ms, complete"| EP4 --> CHECKS EP4 -.->|"complete=true → siteN_complete"| IPQ CHECKS --> AGG IPQ --> AGG AGG -->|"overall_result:
pass / partial / fail"| IPQ EVENTS --> ADMIN CHECKS --> ADMIN IPQ --> ADMIN ``` **Пояснение.** Телеметрия стекается в БД из четырёх независимых потоков (один от агента-валидатора, три — по одному с каждой площадки), записываясь в две таблицы: `events` — сырой журнал аудита (что и когда произошло), `checks` — результат каждой отдельной проверки с указанием источника (`source`: `egress` для валидатора, `inbound-site-1/2/3` для площадок). Отдельно, по мере поступления данных, выставляются флаги завершения (`egress_complete`, `site1/2/3_complete`) в `ip_queue`. Как только все ожидаемые флаги выставлены — либо истекло время ожидания (`checking_window_seconds`) — фоновая агрегация суммирует все строки `checks` по текущей попытке и записывает итог (`pass`/`partial`/`fail`) обратно в `ip_queue`. Оператор в любой момент читает уже накопленные данные через административные `GET`-методы, не дожидаясь завершения проверки — подробнее о значениях полей см. [USAGE.md](USAGE.md#значения-полей-ip). > На диаграмме показан пример с тремя площадками — это лишь иллюстрация, > не ограничение. Площадки опциональны и их число не ограничено: сколько > их ожидать, определяется списком `sites` в конфиге control-api (пустой > список — ни одного потока `inbound-site-N`, агрегация ждёт только > `egress_complete`; N площадок — N параллельных потоков телеметрии). См. > [USAGE.md](USAGE.md#управление-площадками-проберами).