Миграция 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 |
Поток данных (текущий)
Ключевые компоненты 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.DB per 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
4. Итоговая архитектура на Nubes Managed
Два 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 независим — мигрирует первым.
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
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 зависимости