# TASK_PHASE_NODE_SELF_HEALING_v1 ## Проєкт DAARION.city — Nodes / Node Cabinet / DAGI Router ## Фаза Self-healing нод (автоматична реєстрація, відновлення та синхронізація) ## Статус ✅ **COMPLETED** --- ## Мета Зробити так, щоб: 1. Ноди **ніколи не "зникали"** з Node Directory, якщо фізично існують і шлють heartbeat 2. Реєстрація/оновлення нод виконувалась **агентами ноди**, а не ручними діями 3. Node Directory → Node Cabinet → Node Metrics → DAGI Router були повністю узгоджені --- ## Problem Statement ### Симптом - `/nodes` (Node Directory) показує: - «Знайдено нод: 0» - «Помилка завантаження нод» - Хоча: - насправді NODE1/NODE2 є в `node_cache` - метрики, DAGI Router, агенти ноди працюють ### Причини - Node Directory фронт дивився на іншу структуру даних - Реєстрація ноди не відпрацьовувала після деплою - Немає самовідновлюваної логіки на рівні нод --- ## Рішення ### 1. Node Registry — єдине джерело істини **Таблиця:** `node_registry` ```sql CREATE TABLE node_registry ( id text PRIMARY KEY, -- node_id name text NOT NULL, -- Людська назва hostname text, -- Hostname environment text NOT NULL, -- production/development/staging roles text[] NOT NULL DEFAULT '{}', -- ['gpu', 'ai_runtime', ...] description text, is_active boolean NOT NULL DEFAULT true, registered_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), last_self_registration timestamptz, -- Остання самореєстрація self_registration_count integer DEFAULT 0 ); ``` **View для Node Directory:** ```sql CREATE VIEW v_nodes_directory AS SELECT r.*, c.cpu_model, c.gpu_model, c.ram_total, ... c.last_heartbeat, c.agent_count_router, c.agent_count_system, CASE WHEN c.last_heartbeat < NOW() - INTERVAL '10 minutes' THEN 'stale' ELSE 'online' END AS connection_status FROM node_registry r LEFT JOIN node_cache c ON c.node_id = r.id WHERE r.is_active = true; ``` ### 2. Self-Registration API | Endpoint | Метод | Опис | |----------|-------|------| | `/internal/nodes/register-or-update` | POST | Самореєстрація ноди | | `/internal/node/{node_id}/heartbeat` | POST | Heartbeat з метриками | | `/internal/node/{node_id}/directory-check` | GET | Перевірка видимості | | `/internal/node/{node_id}/self-healing/status` | GET | Статус self-healing | | `/internal/node/{node_id}/self-healing/trigger` | POST | Тригер self-healing | | `/internal/nodes/needing-healing` | GET | Список нод для healing | ### 3. Node Bootstrap Script **Файл:** `scripts/node-bootstrap.sh` ```bash # Використання при старті ноди NODE_ID=node-2-macbook-m4max \ NODE_NAME="MacBook Pro M4 Max" \ NODE_ENVIRONMENT=development \ NODE_ROLES=gpu,ai_runtime,development \ ./scripts/node-bootstrap.sh ``` **Що робить:** 1. Відправляє POST на `/internal/nodes/register-or-update` 2. При успіху — відправляє початковий heartbeat 3. При помилці — retry до 5 разів ### 4. Node Guardian Self-Healing Loop **Файл:** `scripts/node-guardian-loop.py` ```bash # Запуск як фоновий процес NODE_ID=node-2-macbook-m4max \ NODE_NAME="NODE2" \ python scripts/node-guardian-loop.py --interval 60 # Одноразова перевірка python scripts/node-guardian-loop.py --node-id node-2-macbook-m4max --once ``` **Що перевіряє:** 1. Чи нода видима в Node Directory 2. Чи є heartbeat 3. Чи є Guardian/Steward агенти 4. Чи є агенти в router **Self-healing дії:** 1. Якщо не видима — виконує self-registration 2. Якщо heartbeat старий — відправляє новий 3. Якщо статус error — тригерить healing через API --- ## Файли | Файл | Опис | |------|------| | `migrations/039_node_registry_self_healing.sql` | Міграція для node_registry | | `services/city-service/repo_city.py` | Функції для self-healing | | `services/city-service/routes_city.py` | API endpoints | | `scripts/node-bootstrap.sh` | Bootstrap скрипт | | `scripts/node-guardian-loop.py` | Self-healing loop | --- ## Інваріанти Self-Healing | Умова | Дія | |-------|-----| | Нода не в node_registry | → self_register() | | heartbeat > 10 хв | → send_heartbeat() | | agent_count_router = 0 | → alert + try reinstall | | guardian_agent_id = NULL | → alert | | self_healing_status = error | → trigger_healing() | --- ## Використання ### При першому деплої ноди ```bash # 1. Запустити міграцію psql -d daarion < migrations/039_node_registry_self_healing.sql # 2. Запустити bootstrap NODE_ID=node-2-macbook-m4max \ NODE_NAME="MacBook Pro M4 Max" \ NODE_ENVIRONMENT=development \ ./scripts/node-bootstrap.sh ``` ### Запуск Guardian Loop ```bash # Через systemd [Unit] Description=DAARION Node Guardian After=network.target [Service] Environment=NODE_ID=node-2-macbook-m4max Environment=NODE_NAME=NODE2 Environment=CITY_SERVICE_URL=http://localhost:7001 ExecStart=/usr/bin/python3 /path/to/scripts/node-guardian-loop.py Restart=always [Install] WantedBy=multi-user.target ``` ### Через Docker Compose ```yaml services: node-guardian: image: python:3.11-slim environment: - NODE_ID=node-2-macbook-m4max - NODE_NAME=NODE2 - CITY_SERVICE_URL=http://city-service:7001 command: python /app/scripts/node-guardian-loop.py volumes: - ./scripts:/app/scripts depends_on: - city-service ``` --- ## Self-Healing сценарії ### Сценарій 1: Нода зникла з Directory після деплою ``` 1. Node Guardian запускається 2. check_visibility() → false 3. self_register() → успіх 4. check_visibility() → true 5. ✅ Нода знову в Directory ``` ### Сценарій 2: Heartbeat застарів ``` 1. Node Guardian перевіряє статус 2. self_healing_status = "stale_heartbeat" 3. send_heartbeat() → успіх 4. ✅ Heartbeat оновлено ``` ### Сценарій 3: Agent count = 0 ``` 1. Node Guardian бачить agent_count_router = 0 2. Логує попередження 3. (Опційно) trigger_healing() для перевірки DAGI Router 4. ⚠️ Потребує уваги адміністратора ``` --- ## Acceptance Criteria | Критерій | Статус | |----------|--------| | node_registry таблиця створена | ✅ | | API self-registration працює | ✅ | | node-bootstrap.sh виконує реєстрацію | ✅ | | node-guardian-loop.py запускається | ✅ | | Ноди видимі в /nodes після реєстрації | ✅ | | Self-healing при зникненні | ✅ | | Heartbeat оновлює статус | ✅ | --- ## Наступні кроки 1. **Автоматичний DAGI Router reinstall** при `agent_count_router = 0` 2. **NATS events** для node healing (`node.selfhealing.*`) 3. **Prometheus metrics** для self-healing 4. **Alert rules** для критичних станів 5. **Node Federation** — з'єднання нод між собою