- 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
13 KiB
🏗️ Архітектура для 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:
{
"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=AANDneeds_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 retriestype(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 по streamsnats_redeliveries_total{stream}— кількість retriesnats_ack_pending{stream, consumer}— jobs в обробціnats_stream_storage_bytes{stream}— використання диску
Worker Metrics
worker_jobs_processed_total{type, status}— загальна кількість jobsworker_job_duration_seconds{type, quantile}— latency (p50, p95, p99)worker_gpu_utilization{node_id}— використання GPUworker_vram_usage_bytes{node_id}— використання VRAMworker_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 PVCworker offline > 2 min(Tier A)
Попередження:
MM_OFFLINE backlog > 10000ack_pending > 80% max_ack_pendingworker_gpu_utilization < 20%(Tier A, протягом 10+ хв)
📋 Наступні 7 кроків (по порядку)
1. ✅ Ротація секретів + прибрати креденшали з документів/репо
- Видалено паролі з документів
- Ротація Cohere API key
- Ротація паролів Postgres/Neo4j
- Оновлення в Vault
2. ✅ Закрити зовнішній доступ до NodePort
- NodePort закрито для Memory Service NODE1
- Додати JWT/mTLS auth
- NetworkPolicy для internal-only
3. ✅ Підняти NATS JetStream (Tier A)
- Створено K8s deployment (3 replicas + PVC + auth)
- Створено streams definitions (MM_ONLINE, MM_OFFLINE, MM_WRITE, MM_EVENTS)
- Створено job payload schema (JSON v1)
- Створено 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