feat: Kafka pipeline v0.1.67 (mqtt-bridge→kafka→consumer→postgres)
This commit is contained in:
@@ -1166,3 +1166,235 @@ kubectl rollout restart -n sless deploy/sless-operator deploy/iot-mqtt-bridge
|
||||
8. progress.md: обновлять до и после каждого шага
|
||||
9. Коммит + пуш после каждого завершённого шага
|
||||
10. ВЕРСИЮ ПОДНИМАТЬ перед каждой сборкой!
|
||||
|
||||
|
||||
---
|
||||
|
||||
# ПЛАН: Telemetry Pipeline — Postgres -> REST API -> UI
|
||||
|
||||
> **Автор плана**: GitHub Copilot (Claude Opus 4.6)
|
||||
> **Дата**: 2026-04-05
|
||||
> **Исполнитель**: Claude Sonnet
|
||||
> **Ветка**: iot-pg-telemetry
|
||||
> **Предусловия**: все компоненты до этого этапа РЕАЛИЗОВАНЫ и задеплоены (CRD, controller, EMQX, mqtt-bridge, IoT Console UI)
|
||||
|
||||
---
|
||||
|
||||
## Цель
|
||||
|
||||
Полная цепочка: IoT устройство (или эмулятор в UI) -> MQTT -> INSERT в Postgres -> REST API -> отображение в таблице на вкладке Телеметрия в IoT Console.
|
||||
|
||||
**User story**: юзер входит токеном, регистрирует устройство, запускает эмулятор (рандомные temp/humidity), переходит на вкладку Телеметрия и видит таблицу с данными: время | устройство | payload.
|
||||
|
||||
---
|
||||
|
||||
## Что уже готово (НЕ ТРОГАТЬ без крайней необходимости)
|
||||
|
||||
| Компонент | Файл(ы) | Статус |
|
||||
|-----------|---------|--------|
|
||||
| IoTDevice CRD + types | iot/api/v1alpha1/device_types.go | DONE |
|
||||
| IoTDevice controller | iot/controllers/iotdevice_controller.go | DONE |
|
||||
| MQTT Auth + ACL | internal/api/handler/iot_device_handler.go | DONE |
|
||||
| IoT API CRUD | internal/api/router.go + handler | DONE |
|
||||
| EMQX deploy | deployments/k8s/emqx.yaml | DONE |
|
||||
| mqtt-bridge MQTT->RabbitMQ | iot/cmd/mqtt-bridge/main.go | DONE |
|
||||
| IoT Console UI | internal/api/ui/iot-console.html | DONE |
|
||||
| TLS (HTTPS + WSS) | deployments/k8s/emqx-ws-ingress.yaml | DONE |
|
||||
| Nubes branding | UI CSS | DONE |
|
||||
| Existing Postgres (invocations) | deployments/k8s/postgres.yaml | DONE |
|
||||
|
||||
---
|
||||
|
||||
## Архитектурное решение (принято 2026-04-04)
|
||||
|
||||
Подробности: doc/decisions/iot-telemetry-storage-2026-04-04.md
|
||||
|
||||
- **Один Postgres инстанс** для IoT (отдельный от sless Postgres для invocations)
|
||||
- **Отдельная DATABASE per tenant** (не одна таблица с tenant_id!)
|
||||
- Tenant DB: tenant_{namespace_hash}, User: tenant_{namespace_hash}, Password: UUID в k8s Secret
|
||||
- Таблица: iot_telemetry(id BIGSERIAL, device_id TEXT, ts TIMESTAMPTZ, payload JSONB)
|
||||
- Клиент читает ТОЛЬКО через REST API, не через прямой доступ к Postgres
|
||||
|
||||
---
|
||||
|
||||
## Шаги реализации (порядок критичен!)
|
||||
|
||||
### ШАГ 1: Postgres Deployment для IoT (namespace: sless)
|
||||
|
||||
**Файл**: deployments/k8s/iot-postgres.yaml
|
||||
|
||||
**Почему отдельный от sless postgres**: разные данные, разная нагрузка.
|
||||
**Почему в namespace sless, а НЕ iot**: всё живёт в одном namespace, упрощение.
|
||||
|
||||
**YAML манифест**:
|
||||
|
||||
|
||||
|
||||
**Действие**: kubectl apply -f deployments/k8s/iot-postgres.yaml
|
||||
**Проверка**: kubectl exec -n sless deploy/iot-postgres -- psql -U iot_admin -d iot_platform -c "SELECT 1"
|
||||
|
||||
---
|
||||
|
||||
### ШАГ 2: Go-пакет IoT Postgres storage
|
||||
|
||||
**Файл**: internal/storage/iotpg/iot_telemetry_store.go
|
||||
|
||||
**Структура**:
|
||||
|
||||
{ is a shell keyword
|
||||
|
||||
**Методы (все обязательные)**:
|
||||
|
||||
1. New(adminDSN string, log) (*IoTPostgresStore, error) -- подключение к iot_platform DB
|
||||
2. EnsureTenantDB(ctx, namespace) error -- создать DATABASE + USER + таблицу если не существуют:
|
||||
- SELECT 1 FROM pg_database WHERE datname = tenant_{ns}
|
||||
- Если нет: CREATE USER, CREATE DATABASE, подключиться и CREATE TABLE
|
||||
- Сохранить пароль в tenant_credentials таблице в iot_platform
|
||||
- Таблица: iot_telemetry(id BIGSERIAL PK, device_id TEXT, ts TIMESTAMPTZ DEFAULT now(), payload JSONB)
|
||||
- Индекс: idx_iot_telemetry_device_ts ON iot_telemetry(device_id, ts DESC)
|
||||
3. InsertTelemetry(ctx, namespace, deviceID, payload json.RawMessage) error
|
||||
4. QueryTelemetry(ctx, namespace, deviceID string, limit int) ([]TelemetryRow, error)
|
||||
5. Close() error
|
||||
|
||||
**Tenant DB provisioning**: таблица tenant_credentials в iot_platform:
|
||||
|
||||
|
||||
**Кэширование**: sync.Map для *sql.DB per tenant. Lazy init при первом обращении.
|
||||
|
||||
---
|
||||
|
||||
### ШАГ 3: Модифицировать mqtt-bridge -- добавить INSERT в Postgres
|
||||
|
||||
**Файл**: iot/cmd/mqtt-bridge/main.go
|
||||
|
||||
**Текущее поведение**: MQTT message -> envelope -> RabbitMQ.
|
||||
**Новое поведение**: MQTT message -> INSERT в Postgres (tenant DB) + RabbitMQ (как было).
|
||||
|
||||
**Изменения**:
|
||||
1. Добавить env var IOT_PG_DSN
|
||||
2. Подключиться к IoTPostgresStore при старте
|
||||
3. В buildMQTTMessageHandler:
|
||||
- store.EnsureTenantDB(ctx, namespace) -- идемпотентно
|
||||
- store.InsertTelemetry(ctx, namespace, deviceID, payload)
|
||||
- При ошибке INSERT -- логировать, НЕ блокировать RabbitMQ publish
|
||||
4. RabbitMQ publish остаётся как было
|
||||
|
||||
**YAML**: deployments/k8s/iot-mqtt-bridge.yaml -- добавить env IOT_PG_DSN из iot-postgres-secret
|
||||
|
||||
---
|
||||
|
||||
### ШАГ 4: REST API endpoint для чтения телеметрии
|
||||
|
||||
**Файл**: internal/api/handler/iot_telemetry_handler.go (НОВЫЙ)
|
||||
|
||||
**Endpoint**:
|
||||
|
||||
|
||||
**Параметры**:
|
||||
- device -- фильтр по device_id (опционален)
|
||||
- limit -- максимум записей (default: 50, max: 1000)
|
||||
|
||||
**Response**:
|
||||
|
||||
|
||||
**Сортировка**: ts DESC (новые сверху).
|
||||
|
||||
---
|
||||
|
||||
### ШАГ 5: Инициализация IoTPostgresStore в main.go
|
||||
|
||||
**Файл**: main.go
|
||||
|
||||
1. Добавить поле IoTPG в handler.Handler struct (handler.go)
|
||||
2. В main.go: if IOT_PG_DSN задан -> iotpg.New() -> передать в Handler
|
||||
3. В router.go: зарегистрировать route /namespaces/{ns}/iot/telemetry
|
||||
|
||||
**YAML**: deployments/k8s/operator.yaml -- добавить env IOT_PG_DSN
|
||||
|
||||
---
|
||||
|
||||
### ШАГ 6: Обновить IoT Console UI -- вкладка Телеметрия
|
||||
|
||||
**Файл**: internal/api/ui/iot-console.html
|
||||
|
||||
**Заменить** заглушку coming-soon на реальную таблицу:
|
||||
|
||||
| Время | Устройство | Данные |
|
||||
|-------|-----------|--------|
|
||||
| 2026-04-05 08:15 | sensor-01 | {"temperature": 22.5, "humidity": 65} |
|
||||
|
||||
**JavaScript**:
|
||||
- loadTelemetry() -- fetch GET /v1/.../iot/telemetry -> заполнить tbody
|
||||
- Авто-обновление каждые 5с (чекбокс)
|
||||
- Фильтр по устройству (select из списка devices)
|
||||
- При переключении на вкладку -- автоматический loadTelemetry()
|
||||
|
||||
**CSS**: таблица в стиле Nubes (navy фон, бордеры #0b2d50, текст #e2ecf6)
|
||||
|
||||
---
|
||||
|
||||
### ШАГ 7: Улучшить эмулятор -- рандомные temp/humidity
|
||||
|
||||
**Файл**: internal/api/ui/iot-console.html (секция эмулятора)
|
||||
|
||||
**Новое поведение**:
|
||||
- Чекбокс: "Генерировать случайные данные (temp/humidity)" (по умолчанию ON)
|
||||
- Если ON: при каждой отправке payload = {temperature: random(18-28), humidity: random(40-80), ts: ISO}
|
||||
- Если OFF: используется текстовое поле как сейчас
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Деплой
|
||||
|
||||
deployment.apps/iot-postgres condition met
|
||||
deployment.apps/sless-operator restarted
|
||||
deployment.apps/iot-mqtt-bridge restarted
|
||||
|
||||
---
|
||||
|
||||
## Файлы СОЗДАТЬ
|
||||
|
||||
| Файл | Описание |
|
||||
|------|----------|
|
||||
| deployments/k8s/iot-postgres.yaml | Deployment + Secret + Service |
|
||||
| internal/storage/iotpg/iot_telemetry_store.go | Go: управление tenant DB + CRUD телеметрии |
|
||||
| internal/api/handler/iot_telemetry_handler.go | REST handler GET /v1/.../iot/telemetry |
|
||||
|
||||
## Файлы ИЗМЕНИТЬ
|
||||
|
||||
| Файл | Что менять |
|
||||
|------|-----------|
|
||||
| internal/api/handler/handler.go | Добавить поле IoTPG *iotpg.IoTPostgresStore |
|
||||
| internal/api/router.go | Route /namespaces/{ns}/iot/telemetry |
|
||||
| main.go | Init IoTPostgresStore + передача в Handler |
|
||||
| iot/cmd/mqtt-bridge/main.go | INSERT в Postgres при MQTT message |
|
||||
| deployments/k8s/operator.yaml | env IOT_PG_DSN + версия v0.1.59 |
|
||||
| deployments/k8s/iot-mqtt-bridge.yaml | env IOT_PG_DSN + версия v0.1.59 |
|
||||
| internal/api/ui/iot-console.html | Telemetry tab + emulator random data |
|
||||
|
||||
## Чего НЕ ДЕЛАТЬ
|
||||
|
||||
- НЕ трогать CRD / controller / EMQX / RabbitMQ
|
||||
- НЕ создавать namespace iot -- всё в sless
|
||||
- НЕ делать processing данных -- RAW payload
|
||||
- НЕ добавлять from/to фильтры -- хватит limit
|
||||
- НЕ трогать Terraform provider
|
||||
- НЕ рефакторить существующие файлы
|
||||
- НЕ запускать команды локально -- только SSH
|
||||
|
||||
---
|
||||
|
||||
## Правила для Sonnet
|
||||
|
||||
1. Читай .github/copilot-instructions.md
|
||||
2. Читай doc/decisions/iot-telemetry-storage-2026-04-04.md
|
||||
3. Команды через SSH: ssh -i /home/naeel/remote_dev/common/id_ed25519.txt naeel@5.172.178.213
|
||||
4. Файлы редактировать можно -- sshfs mount
|
||||
5. ПЕРЕД go build -- проверить .gitignore
|
||||
6. Комментарии: дата + назначение + почему
|
||||
7. Thinking log: doc/thinking/2026-04-05.md
|
||||
8. progress.md: обновлять до и после каждого шага
|
||||
9. Коммит + пуш после каждого завершённого шага
|
||||
10. ВЕРСИЮ ПОДНИМАТЬ перед каждой сборкой!
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
# Миграция на новый кластер (тестовые данные)
|
||||
|
||||
> Дата: 2026-04-06
|
||||
> Сценарий: Все данные тестовые и неважны
|
||||
|
||||
---
|
||||
|
||||
## Процесс
|
||||
|
||||
1. **Clone + Build**
|
||||
```bash
|
||||
git clone <repo>
|
||||
make docker-build docker-push IMG=<new-registry>/sless:v1.0
|
||||
```
|
||||
|
||||
2. **Deploy**
|
||||
```bash
|
||||
kubectl create namespace sless
|
||||
|
||||
# Создать Secrets (новые credentials)
|
||||
kubectl create secret generic sless-operator-secret -n sless \
|
||||
--from-literal=POSTGRES_DSN="..." \
|
||||
--from-literal=S3_ACCESS_KEY="..." \
|
||||
--from-literal=S3_SECRET_KEY="..." \
|
||||
--from-literal=SLESS_API_TOKEN="..." \
|
||||
--from-literal=HARBOR_PASS="..."
|
||||
|
||||
# Apply конфиги
|
||||
kubectl apply -f deployments/k8s/rbac.yaml
|
||||
kubectl apply -f deployments/k8s/
|
||||
```
|
||||
|
||||
3. **Done**
|
||||
- БД создадутся новые и пустые
|
||||
- Registry пересоберётся
|
||||
- Готово
|
||||
|
||||
---
|
||||
|
||||
## Что не требуется
|
||||
- ❌ pg_dump / восстановление БД
|
||||
- ❌ Копирование PVC
|
||||
- ❌ Миграция данных
|
||||
|
||||
Всё пересоздаётся с нуля.
|
||||
Reference in New Issue
Block a user