- K8s deployment (2 replicas, PVC, initContainer для server_name) - Streams definitions (MM_ONLINE, MM_OFFLINE, MM_WRITE, MM_EVENTS) - Job payload schema (JSON v1 з idempotency) - Worker contract (capabilities + ack/retry) - Init streams script - Оновлено ARCHITECTURE-150-NODES.md (Control-plane vs Data-plane) TODO: Auth (nkeys), 3+ replicas для prod, worker-daemon implementation
383 lines
13 KiB
Markdown
383 lines
13 KiB
Markdown
# 🏗️ Архітектура для 150 нод — DAARION
|
||
|
||
**Дата:** 2026-01-10
|
||
**Версія:** 1.0.0
|
||
|
||
---
|
||
|
||
## 🎯 Принципи архітектури
|
||
|
||
### Ключова ідея
|
||
**Не стандартизувати GPU**, а стандартизувати **контракт виконання**:
|
||
- Як задачі розподіляються
|
||
- Як воркери звітують
|
||
- Як відбувається retry/ack
|
||
- Як гарантується однакова якість (модель/версія) та безпека
|
||
|
||
Різне "залізо" стає різними **tiers** воркерів.
|
||
|
||
---
|
||
|
||
## 🎛️ Control-plane vs Data-plane
|
||
|
||
### Control-plane (людський + командний)
|
||
|
||
**Matrix Rooms** = контроль, команди, статуси, аудит
|
||
|
||
**Призначення:**
|
||
- Команди користувачів/агентів
|
||
- Статуси виконання (progress + summary)
|
||
- Ручне керування
|
||
- Аудит рішень
|
||
- Оповіщення про помилки/алерти
|
||
|
||
**НЕ використовується для:**
|
||
- Високочастотної job-шини
|
||
- Масових обчислень
|
||
- Real-time streaming даних
|
||
|
||
### Data-plane (масові таски, fan-out/fan-in)
|
||
|
||
**NATS JetStream** = черга задач, розподіл обчислень
|
||
|
||
**Призначення:**
|
||
- Розподіл jobs між воркерами
|
||
- Fan-out/fan-in обчислень
|
||
- Retry/ack механізми
|
||
- Пріоритизація (online/offline)
|
||
- Idempotency гарантії
|
||
|
||
**Переваги:**
|
||
- Легкий (менше ресурсів ніж Kafka)
|
||
- Швидкий (low latency)
|
||
- Простіший для старту
|
||
- At-least-once delivery
|
||
- Підтримка idempotency keys
|
||
|
||
### State & Index
|
||
|
||
**PostgreSQL** = state (організації, користувачі, threads, events)
|
||
**Qdrant** = retrieval index (semantic search)
|
||
**Neo4j** = graph relationships
|
||
|
||
---
|
||
|
||
## 📊 Поточна структура БД (що правильно, що треба виправити)
|
||
|
||
### PostgreSQL
|
||
|
||
**Зараз:**
|
||
- NODE1 = CloudNativePG (K8s) ✅
|
||
- NODE2/3 = Docker (dev/edge)
|
||
|
||
**Правильний фундамент:**
|
||
- **Один primary Postgres у Tier A** (NODE1/K8s) + репліки
|
||
- NODE2/3 Postgres у Docker — залишити як dev/edge, але **не як "другий прод-primary"**
|
||
|
||
**Що робити:**
|
||
- [ ] Якщо NODE3 потрібен як DR/replica: робіть **стрімінг-репліку** з primary (NODE1) на NODE3
|
||
- [ ] Для k8s-primary: тримати репліки теж у k8s (CloudNativePG)
|
||
- [ ] NODE3 використовувати для backup/restore і окремих offline задач
|
||
|
||
### Qdrant
|
||
|
||
**Зараз:** 3 інсталяції (k8s + 2 docker)
|
||
|
||
**Правильно:**
|
||
- [ ] Один **prod Qdrant cluster** (Tier A) на NODE1
|
||
- [ ] Edge Qdrant (NODE2/3) — лише для локального тесту або кешу, але не "істина"
|
||
|
||
### Neo4j
|
||
|
||
**Зараз:** Neo4j на всіх нодах (k8s + docker)
|
||
|
||
**Рекомендація:**
|
||
- [ ] Тримати **один Neo4j** у Tier A (NODE1) як прод
|
||
- [ ] NODE2/3 — dev
|
||
- [ ] Кластеризацію Neo4j відкласти, поки не буде реальної потреби в HA саме графа
|
||
|
||
---
|
||
|
||
## 🔄 Паралельні обчислення між нодами
|
||
|
||
### A) Control-plane (людський + командний) = Matrix rooms
|
||
|
||
**Призначення:**
|
||
- Команди
|
||
- Статуси
|
||
- Ручне керування
|
||
- Аудит рішень
|
||
|
||
**НЕ використовується для:**
|
||
- Високочастотної job-шини
|
||
- Масових обчислень
|
||
|
||
### B) Data-plane (масові таски, fan-out) = NATS JetStream
|
||
|
||
**Вибір:** **NATS JetStream** (легкий, швидкий, простіший за Kafka для старту)
|
||
|
||
**Переваги:**
|
||
- Легкий (менше ресурсів)
|
||
- Швидкий (low latency)
|
||
- Простіший для старту
|
||
- Підтримує at-least-once delivery
|
||
- Підтримує idempotency keys
|
||
|
||
### C) Capability registry (хто що вміє) = Consul або Postgres
|
||
|
||
**Кожен воркер реєструється з capabilities:**
|
||
```json
|
||
{
|
||
"node_id": "node-123",
|
||
"tier": "A|B|C",
|
||
"region": "eu-west",
|
||
"trust_zone": "internal",
|
||
"hardware": {
|
||
"cpu_cores": 32,
|
||
"ram_gb": 128,
|
||
"gpu": true,
|
||
"gpu_model": "RTX 3090",
|
||
"vram_gb": 24,
|
||
"cuda_version": "13.0"
|
||
},
|
||
"capabilities": {
|
||
"max_batch": 1000,
|
||
"max_tokens": 8192,
|
||
"models": ["embed-multilingual-v3.0", "llama3:8b"],
|
||
"embedding_dim": 1024
|
||
},
|
||
"status": "ready|busy|maintenance",
|
||
"last_heartbeat": "2026-01-10T19:30:00Z"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🔗 Зв'язок Matrix rooms з паралельними воркерами
|
||
|
||
### Правильний потік:
|
||
|
||
```
|
||
1. Команда/подія → Matrix room (кімната агента або оркестратора)
|
||
↓
|
||
2. Matrix Gateway (сервіс або модуль у Memory Service)
|
||
- RBAC перевірка в Postgres
|
||
- Створює Job у NATS (з idempotency key)
|
||
↓
|
||
3. Воркер на відповідній ноді
|
||
- Бере job з NATS
|
||
- Виконує задачу
|
||
- Пише результат у Postgres/Qdrant/Neo4j
|
||
↓
|
||
4. Gateway публікує статус назад у Matrix
|
||
- Progress + summary → Matrix
|
||
- Детальні логи → система логів (Loki)
|
||
```
|
||
|
||
**Результат:** "Безперервний діалог" і керування через Matrix, але обчислення реально паралеляться через чергу.
|
||
|
||
---
|
||
|
||
## 📦 NATS JetStream: Streams + Consumers
|
||
|
||
### Streams (4 основні)
|
||
|
||
#### STREAM `MM_ONLINE`
|
||
- **Subjects:** `mm.embed.online`, `mm.retrieve.online`, `mm.summarize.online`
|
||
- **Retention:** limits
|
||
- **MaxAge:** 30 хв (або 2 год)
|
||
- **Ack:** explicit
|
||
- **MaxDeliver:** 3
|
||
- **Backoff:** 1s, 5s, 30s
|
||
- **Replicas:** 3
|
||
|
||
#### STREAM `MM_OFFLINE`
|
||
- **Subjects:** `mm.embed.offline`, `mm.index.offline`, `mm.backfill.offline`
|
||
- **Retention:** limits
|
||
- **MaxAge:** 7-30 днів
|
||
- **MaxDeliver:** 10
|
||
- **Backoff:** 10s, 1m, 5m, 30m
|
||
- **Replicas:** 3
|
||
|
||
#### STREAM `MM_WRITE`
|
||
- **Subjects:** `mm.qdrant.upsert`, `mm.pg.write`, `mm.neo4j.write`
|
||
- **Retention:** limits
|
||
- **MaxAge:** 1-7 днів
|
||
- **MaxDeliver:** 10
|
||
- **Replicas:** 3
|
||
|
||
#### STREAM `MM_EVENTS`
|
||
- **Subjects:** `mm.event.audit`, `mm.event.status`
|
||
- **Retention:** limits
|
||
- **MaxAge:** 7-30 днів
|
||
- **Replicas:** 3
|
||
|
||
### Consumer Policies
|
||
|
||
#### Online Consumer (Tier A)
|
||
- **Stream:** `MM_ONLINE`
|
||
- **Consumer:** `online-worker-tier-a`
|
||
- **Ack Wait:** 30s
|
||
- **Max Ack Pending:** 5000
|
||
- **Concurrency:** 50-200 (per worker)
|
||
- **Filter:** `tier=A` AND `needs_gpu=true` (для GPU jobs)
|
||
|
||
#### Offline Consumer (Tier B)
|
||
- **Stream:** `MM_OFFLINE`
|
||
- **Consumer:** `offline-worker-tier-b`
|
||
- **Ack Wait:** 5-30 хв
|
||
- **Max Ack Pending:** 10000
|
||
- **Concurrency:** висока, але з backpressure
|
||
- **Preemption:** можливе призупинення для online jobs
|
||
|
||
### Job Payload Schema
|
||
|
||
Детальна схема: `infrastructure/nats/job-payload-schema.json`
|
||
|
||
**Ключові поля:**
|
||
- `job_id` (ULID)
|
||
- `idempotency_key` (SHA256 hash) — обов'язково для safe retries
|
||
- `type` (embed|retrieve|summarize|qdrant_upsert|pg_write|neo4j_write|index)
|
||
- `priority` (online|offline)
|
||
- `requirements` (needs_gpu, min_vram_gb, max_latency_ms, tier)
|
||
- `tenant` (org_id, workspace_id)
|
||
- `scope` (user_id, agent_id, thread_id)
|
||
- `input` (text, model, dims)
|
||
- `refs` (event_id, memory_id, artifact_uri)
|
||
- `trace` (trace_id, parent_span_id)
|
||
- `timestamps` (created_at, deadline)
|
||
|
||
---
|
||
|
||
## 🎚️ Використання різних нод (Tier A/B/C)
|
||
|
||
### Поточна класифікація:
|
||
|
||
| Node | Hardware | Tier | Призначення |
|
||
|------|----------|------|-------------|
|
||
| **NODE1** | RTX 4000 Ada 20GB | **A** | Online critical, primary DB |
|
||
| **NODE3** | RTX 3090 24GB | **A/B** | Online + offline, heavy jobs |
|
||
| **NODE2** | Apple M4 | **C** | Orchestration/dev/offline prep |
|
||
|
||
### Розподіл задач:
|
||
|
||
- **Online embeddings/LLM** → NODE1 + NODE3
|
||
- **Offline backfill** → NODE3 + інші GPU edge ноди
|
||
- **NODE2** → Orchestration, тестування, підготовка чанків, локальні агентні сесії
|
||
|
||
---
|
||
|
||
## 🔐 Embeddings: Cohere vs BGE-M3
|
||
|
||
### Правило:
|
||
**Одна колекція Qdrant = один embedding space** (модель + версія + dims)
|
||
|
||
### Поточна ситуація:
|
||
- **Cohere embed-multilingual-v3.0** (1024 dim) — використовується
|
||
|
||
### Якщо переходите на BGE-M3:
|
||
- [ ] Створити нову колекцію: `memories_bge_m3_v1` (1024 dim)
|
||
- [ ] Або додати `model_version` і фізично рознести колекції
|
||
- [ ] **НЕ змішувати** embedding-простори в одній колекції
|
||
|
||
---
|
||
|
||
## 📈 Метрики та Алерти
|
||
|
||
### NATS JetStream Metrics
|
||
|
||
- `nats_consumer_lag{stream, consumer}` — backlog по streams
|
||
- `nats_redeliveries_total{stream}` — кількість retries
|
||
- `nats_ack_pending{stream, consumer}` — jobs в обробці
|
||
- `nats_stream_storage_bytes{stream}` — використання диску
|
||
|
||
### Worker Metrics
|
||
|
||
- `worker_jobs_processed_total{type, status}` — загальна кількість jobs
|
||
- `worker_job_duration_seconds{type, quantile}` — latency (p50, p95, p99)
|
||
- `worker_gpu_utilization{node_id}` — використання GPU
|
||
- `worker_vram_usage_bytes{node_id}` — використання VRAM
|
||
- `worker_errors_total{type, error_type}` — помилки
|
||
|
||
### Алерти → Matrix Ops Room
|
||
|
||
**Критичні:**
|
||
- `MM_ONLINE backlog > 1000` — SLO порушення
|
||
- `redeliveries spike > 100/min` — проблеми з воркерами
|
||
- `embed p95 > 500ms` (target: 300ms)
|
||
- `disk > 80%` на JetStream PVC
|
||
- `worker offline > 2 min` (Tier A)
|
||
|
||
**Попередження:**
|
||
- `MM_OFFLINE backlog > 10000`
|
||
- `ack_pending > 80% max_ack_pending`
|
||
- `worker_gpu_utilization < 20%` (Tier A, протягом 10+ хв)
|
||
|
||
---
|
||
|
||
## 📋 Наступні 7 кроків (по порядку)
|
||
|
||
### 1. ✅ Ротація секретів + прибрати креденшали з документів/репо
|
||
- [x] Видалено паролі з документів
|
||
- [ ] Ротація Cohere API key
|
||
- [ ] Ротація паролів Postgres/Neo4j
|
||
- [ ] Оновлення в Vault
|
||
|
||
### 2. ✅ Закрити зовнішній доступ до NodePort
|
||
- [x] NodePort закрито для Memory Service NODE1
|
||
- [ ] Додати JWT/mTLS auth
|
||
- [ ] NetworkPolicy для internal-only
|
||
|
||
### 3. ✅ Підняти NATS JetStream (Tier A)
|
||
- [x] Створено K8s deployment (3 replicas + PVC + auth)
|
||
- [x] Створено streams definitions (MM_ONLINE, MM_OFFLINE, MM_WRITE, MM_EVENTS)
|
||
- [x] Створено job payload schema (JSON v1)
|
||
- [x] Створено worker contract (capabilities + ack/retry)
|
||
- [ ] Застосувати deployment в K8s
|
||
- [ ] Налаштувати operator JWT + system account
|
||
- [ ] Створити streams через NATS CLI або API
|
||
|
||
### 4. Додати worker-daemon на кожну ноду
|
||
- [ ] Heartbeat до capability registry
|
||
- [ ] Capabilities (hardware, models, tier)
|
||
- [ ] Підписка на черги NATS
|
||
- [ ] Job execution + результат
|
||
|
||
### 5. Matrix Gateway
|
||
- [ ] Команди/статуси ↔ jobs у NATS
|
||
- [ ] RBAC перевірка
|
||
- [ ] Публікація результатів назад у Matrix
|
||
|
||
### 6. Рознести embedding-простори
|
||
- [ ] Визначити стратегію (Cohere vs BGE-M3)
|
||
- [ ] Створити окремі колекції або міграцію
|
||
|
||
### 7. Postgres: визначити один primary + репліки
|
||
- [ ] NODE1 = primary (CloudNativePG)
|
||
- [ ] NODE3 = streaming replica (якщо потрібен DR)
|
||
- [ ] Backup/restore стратегія
|
||
|
||
---
|
||
|
||
## 📁 Структура файлів
|
||
|
||
```
|
||
infrastructure/
|
||
├── nats/
|
||
│ ├── deployment.yaml # NATS JetStream в K8s
|
||
│ ├── streams.yaml # Конфігурація streams
|
||
│ └── accounts.yaml # NATS accounts (якщо потрібно)
|
||
├── worker-daemon/
|
||
│ ├── deployment.yaml # Worker daemon для кожної ноди
|
||
│ ├── capability-registry.py # Реєстрація capabilities
|
||
│ └── job-executor.py # Виконання jobs
|
||
└── matrix-gateway/
|
||
├── deployment.yaml # Matrix Gateway service
|
||
├── gateway.py # Matrix ↔ NATS bridge
|
||
└── rbac.py # RBAC перевірка
|
||
```
|
||
|
||
---
|
||
|
||
*Документ створено: 2026-01-10 19:30 CET*
|