10 KiB
10 KiB
⛔⛔⛔ ЛЕГАСИ (2026-08-16) — СТАРЫЙ план миграции (Node.js). ОТМЕНЁН. НЕ ПРИНИМАТЬ ВО ВНИМАНИЕ. Актуальное: HISTORY/2026-08-16-session-log.md
Миграция IoT из Kubernetes → Managed Services (Nubes)
Сессия 2026-08-13
1. Что изучили — текущая архитектура
Стек
- Язык: Go 1.25
- Паттерн: Kubernetes Operator (controller-runtime)
- Модуль:
gitea.services.ngcloud.ru/Nail/IoT - Docker Hub:
naeel/iot-operator:v0.2.6
Три бинарника в одном образе
| Бинарник | Роль |
|---|---|
cmd/iot-operator |
Controller-manager (CRD IoTDevice) + REST API :9090 |
cmd/mqtt-bridge |
MQTT (EMQX) → shared-SQS (AWS SDK v2) |
cmd/sqs-consumer |
shared-SQS → per-tenant Postgres |
Поток данных (текущий)
IoT Device (MQTT CONNECT, username="{ns}_{deviceId}")
↓
EMQX 5.5.1 (HTTP auth/acl → iot-operator:9090/internal/mqtt/auth)
↓ MQTT PUBLISH "{ns}/telemetry/{deviceId}"
iot-mqtt-bridge (подписка "+/telemetry/+")
↓ AWS SDK SQS SendMessage
shared-SQS (namespace shared-sqs, очередь "iot-telemetry")
↓ long polling (WaitTimeSeconds=20)
iot-sqs-consumer (at-least-once, DeleteMessage после успешной записи)
↓
Managed PostgreSQL 17 (per-tenant DB: tenant_{namespace})
↓
REST API (iot-operator:9090) → Пользователь (UI/Terraform)
Ключевые компоненты k8s
- CRD IoTDevice (
iot.kube5s.ru/v1alpha1) — регистрация устройств - k8s Secret
iot-{deviceId}— хранит MQTT пароль (64 hex, crypto/rand) - OwnerReference — каскадное удаление Secret при удалении IoTDevice
- RBAC — ClusterRole для чтения/записи IoTDevice + Secrets
- Namespace = ID тенанта — изоляция устройств и данных
Мультитенантность
- MQTT topic изоляция:
{ns}/telemetry/{deviceId}— ACL на уровне EMQX - Per-tenant Postgres DB:
tenant_{namespace}(дефисы → подчёркивания) - MQTT username:
{namespace}_{deviceId}— глобально уникален sync.Mapкэш*sql.DBper tenant — lazy init
Наблюдения / потенциальные проблемы
authTestMode = trueвmiddleware/auth.go— тестовый режим включён в проде, JWT подпись не проверяетсяADMIN_STATS_TOKEN = "iot-admin-2026"захардкожен в YAML манифесте вместо Secret- Устаревшие файлы:
deployments/k8s/kafka.yaml,iot-kafka-consumer.yaml(Kafka удалена, манифесты остались) - Bridge clientID
"sless-iot-bridge"захардкожен и в bridge коде и в ACL логике examples/main.tfиhandler.pyописывают устаревший поток (RabbitMQ/serverless)
2. Почему хотим уйти из k8s
- Цель: использовать только managed services облака Nubes
- Nubes предоставляет: Managed Node.js, Flask, Lucee, PostgreSQL
- Никаких VPS, никакого k8s — только managed-платформа
- SQS тоже наш собственный сервис (не сторонний облачный), работает сейчас в k8s, тоже надо вынести
3. Принятые решения
Node.js — выбранный стек
| Задача | Node.js |
|---|---|
| REST API | Express/Fastify |
| MQTT-брокер | aedes (embedded, over WebSocket) |
| MQTT-клиент для publish | mqtt npm |
| SQS клиент | @aws-sdk/client-sqs |
| Postgres | pg npm |
Почему не Flask: сложнее держать persistent MQTT и SQS polling в фоне
Почему не Lucee: не подходит для long-running background workers
Почему Node.js — монолит: bridge и consumer нельзя масштабировать независимо (один MQTT-клиент = одна подписка), смысла разделять нет
EMQX → aedes (embedded в Node.js)
- Managed Node.js открывает только HTTP/HTTPS порты
- MQTT over WebSocket = HTTP upgrade → работает на любой managed-платформе
- Устройства подключаются через
wss://(уже сейчас так, через ingress emqx-ws-ingress.yaml) - aedes — полноценный MQTT-брокер на Node.js, встраивается в Express HTTP-сервер
- Auth/ACL становится обычной функцией внутри того же процесса (быстрее, проще)
Мультитенантность сохраняется полностью
namespace— просто строка в таблицеiot_devicesвместо k8s namespace- Per-tenant Postgres DB остаётся (чистый SQL, без k8s)
- MQTT topic изоляция остаётся (
{ns}/telemetry/{deviceId}) - ACL по топику остаётся — просто функция вместо HTTP endpoint
k8s CRD/Secret → таблица в Postgres
CREATE TABLE iot_devices (
namespace TEXT NOT NULL,
name TEXT NOT NULL,
device_id TEXT NOT NULL,
enabled BOOLEAN NOT NULL DEFAULT true,
mqtt_password TEXT NOT NULL, -- было в k8s Secret
metadata JSONB,
phase TEXT,
created_at TIMESTAMPTZ DEFAULT now(),
PRIMARY KEY (namespace, device_id)
);
4. Итоговая архитектура на Nubes Managed
IoT Device (wss://iot.example.ru/mqtt)
↓
Managed Node.js — IoT сервис
├── aedes MQTT-брокер (over WebSocket, порт :3000/mqtt)
│ auth/acl → функция → таблица iot_devices в PG
│ on publish → SQS SendMessage
├── SQS consumer (long polling, фоновый setInterval/async loop)
│ → per-tenant Postgres (tenant_{namespace})
└── REST API (Express)
GET/POST /v1/namespaces/{ns}/iot/devices
GET /v1/namespaces/{ns}/iot/telemetry
POST /internal/mqtt/auth (совместимость, опционально)
GET /console (embedded HTML)
GET /iot-admin/stats
↓
Managed Node.js — shared-SQS сервис ← другие сервисы тоже
↓
Managed PostgreSQL — IoT данные
Два managed Node.js сервиса
| Сервис | Назначение |
|---|---|
| shared-SQS | AWS SQS-совместимая очередь, multi-tenant, HTTP API |
| iot-service | aedes MQTT + REST API + SQS consumer + PG |
5. Порядок миграции
⚠️ Сначала shared-SQS, потом IoT
Причина: IoT зависит от SQS. SQS независим — мигрирует первым.
Шаг 1: Вынести shared-SQS из k8s → Managed Node.js на Nubes
Шаг 2: Переписать IoT сервис на Node.js (aedes + REST + consumer)
Шаг 3: Деплой IoT на Managed Node.js на Nubes
Шаг 4: Отключить k8s деплой IoT
6. Детали для нового чата — shared-SQS миграция
Что сейчас
- SQS сервис живёт в
namespace: shared-sqsв кластереiot-naeel - Endpoint:
https://qu.kube5s.ru - Multi-tenant: tenant
iot-service(id:t-96afe7e9f781f6ca), очередьiot-telemetry - IoT использует:
SQS_ENDPOINT=https://qu.kube5s.ru,SQS_ACCESS_KEY,SQS_SECRET_KEY - Протокол: AWS SQS-совместимый (SendMessage, ReceiveMessage, DeleteMessage, GetQueueUrl, GetQueueAttributes)
Что нужно от нового SQS сервиса
- AWS SQS-совместимый HTTP API (те же методы что сейчас)
- Multi-tenant (разные access key / secret key для разных тенантов)
- Очереди создаются по имени (
GetQueueUrl+CreateQueue) - Long polling:
ReceiveMessageсWaitTimeSecondsдо 20 - Хранение сообщений: in-memory или Postgres/Redis
Клиенты SQS в IoT коде
- mqtt-bridge (
cmd/mqtt-bridge/main.go):SendMessageпри каждом MQTT сообщении - sqs-consumer (
cmd/sqs-consumer/main.go):ReceiveMessage(polling) +DeleteMessage - iot-admin-stats (
internal/api/handler/iot_admin_stats_handler.go):GetQueueAttributesдля мониторинга
Env vars для IoT → SQS
SQS_ENDPOINT=https://qu.kube5s.ru (поменяется на новый managed URL)
SQS_ACCESS_KEY=...
SQS_SECRET_KEY=...
SQS_QUEUE_NAME=iot-telemetry (default)
SQS_REGION=us-east-1 (default, не важен для self-hosted)
7. Что переписывается в IoT (Node.js)
Соответствие Go → Node.js
| Go файл | Node.js файл |
|---|---|
cmd/iot-operator/main.go |
src/index.js (точка входа) |
internal/api/router.go |
src/routes.js |
internal/api/handler/iot_device_handler.go |
src/handlers/devices.js |
internal/api/handler/iot_telemetry_handler.go |
src/handlers/telemetry.js |
internal/api/handler/iot_admin_stats_handler.go |
src/handlers/admin.js |
internal/api/middleware/auth.go |
src/middleware/auth.js |
internal/storage/iotpg/iot_telemetry_store.go |
src/storage/pg.js |
cmd/mqtt-bridge/main.go |
встроен в src/mqtt.js (aedes) |
cmd/sqs-consumer/main.go |
встроен в src/sqsWorker.js |
controllers/iotdevice_controller.go |
не нужен (заменён CRUD в БД) |
api/v1alpha1/device_types.go |
не нужен (таблица iot_devices) |
npm зависимости
{
"dependencies": {
"express": "^4",
"aedes": "^0.51",
"websocket-stream": "^5",
"@aws-sdk/client-sqs": "^3",
"pg": "^8",
"uuid": "^9"
}
}