IoT Managed Service — Актуальная архитектура (v0.2.3)
Дата: 2026-04-12
Образ: naeel/iot-operator:v0.2.3
Кластер: iot-naeel, namespace: sless
Путь данных (data flow)
Компоненты
iot-operator (cmd/iot-operator)
Единый бинарник: controller-manager + REST API сервер.
| Функция |
Описание |
| IoTDevice Controller |
Reconcile: создание Secret с MQTT credentials, OwnerReference |
| REST API :9090 |
CRUD устройств, телеметрия, MQTT auth/acl, admin stats, UI |
| Health :8081 |
/healthz, /readyz для k8s probes |
iot-mqtt-bridge (cmd/mqtt-bridge)
MQTT subscriber -> SQS producer. Stateless.
| Параметр |
Значение |
| MQTT broker |
tcp://emqx.sless.svc:1883 |
| MQTT username |
iot-bridge-internal (Secret iot-bridge-credentials) |
| MQTT subscription |
+/telemetry/+ |
| SQS endpoint |
https://qu.kube5s.ru |
| SQS queue |
iot-telemetry |
Bridge auth (v0.2.3): operator проверяет BridgeUsername/BridgePassword ДО парсинга namespace_deviceId.
iot-sqs-consumer (cmd/sqs-consumer)
SQS consumer -> Postgres writer. Stateless.
| Параметр |
Значение |
| SQS endpoint |
https://qu.kube5s.ru |
| SQS queue |
iot-telemetry |
| Long polling |
WaitTimeSeconds=20 |
| Postgres |
IOT_PG_DSN из Secret |
| Семантика |
at-least-once (DeleteMessage после INSERT) |
EMQX 5.5.1
MQTT-брокер с HTTP auth backend.
| Параметр |
Значение |
| Образ |
emqx/emqx:5.5.1 |
| Порты |
1883 (MQTT), 8083 (WebSocket), 18083 (Dashboard) |
| Auth |
HTTP POST -> iot-operator:9090/internal/mqtt/auth |
| ACL |
HTTP POST -> iot-operator:9090/internal/mqtt/acl |
| Конфиг |
HOCON emqx.conf через ConfigMap |
CRD: IoTDevice (iot.kube5s.ru/v1alpha1)
Spec
| Поле |
Тип |
Обязательное |
Описание |
| deviceId |
string |
да |
Pattern: ^[a-z0-9][a-z0-9-]*[a-z0-9]$ |
| enabled |
bool |
нет |
default: true |
| metadata |
map[string]string |
нет |
Произвольные метаданные |
Status
| Поле |
Описание |
| phase |
Active / Disabled / Pending / Error |
| mqttUsername |
{namespace}_{deviceId} |
| secretName |
iot-{deviceId} |
| topicPrefix |
{namespace}/ |
| message |
Сообщение об ошибке |
Reconcile logic
- Добавить finalizer iot.kube5s.ru/device-cleanup
- Если Secret iot-{deviceId} не существует:
- crypto/rand 32 bytes -> hex (64 символа) = пароль
- Создать Secret с OwnerReference -> каскадное удаление
- Keys: mqtt-username, mqtt-password, device-id
- Status: phase=Active, mqttUsername={ns}_{deviceId}
- Если enabled=false -> phase=Disabled (Secret НЕ удаляется)
- DELETE: finalizer cleanup -> Secret удаляется каскадно
Аутентификация
REST API (/v1/)
- Middleware: Bearer JWT token
- authTestMode = true (текущий): любой непустой Bearer token принимается
- authTestMode = false (prod): JWT decode -> sub -> SHA256 -> namespace mapping
MQTT Auth (/internal/mqtt/auth)
- Если username == BridgeUsername -> проверить BridgePassword -> allow/deny
- Иначе: парсить username по первому "_" -> namespace + deviceId
- Найти Secret iot-{deviceId} в namespace
- crypto/subtle.ConstantTimeCompare(password, secret.mqtt-password)
- Всегда HTTP 200, body: {"result": "allow"} или {"result": "deny"}
MQTT ACL (/internal/mqtt/acl)
- Bridge (clientid=sless-iot-bridge): allow subscribe +/telemetry/+
- Device: allow pub/sub только {namespace}/telemetry/{deviceId}
- Всё остальное: deny
Хранение данных
PostgreSQL (managed)
| Параметр |
Значение |
| Версия |
PG 17 |
| Host |
postgresqlk8s-master.dc5db45d-f8b4-4fd0-ad33-ec4dd017f2d5.svc.cluster.local |
| User |
super |
| Master DB |
sqsdb |
Per-tenant изоляция: отдельная DATABASE tenant_{namespace}.
Таблица iot_telemetry:
- id SERIAL PRIMARY KEY
- device_id TEXT NOT NULL
- payload JSONB NOT NULL
- received_at TIMESTAMPTZ
- created_at TIMESTAMPTZ DEFAULT NOW()
shared-SQS
| Параметр |
Значение |
| Endpoint |
https://qu.kube5s.ru |
| Tenant |
iot-service (t-96afe7e9f781f6ca) |
| Queue |
iot-telemetry |
| Протокол |
AWS SQS API compatible |
Docker образ
- Registry: Docker Hub naeel/iot-operator
- Базовый: gcr.io/distroless/static:nonroot
- Содержит 3 бинарника: /iot-operator, /mqtt-bridge, /sqs-consumer
- Выбор бинарника через command в Deployment YAML
Сетевая схема
Секреты (namespace sless)
| Secret |
Ключи |
Используется |
| iot-bridge-credentials |
MQTT_USERNAME, MQTT_PASSWORD |
iot-operator, iot-mqtt-bridge |
| iot-sqs-credentials |
SQS_ENDPOINT, SQS_ACCESS_KEY, SQS_SECRET_KEY |
iot-mqtt-bridge, iot-sqs-consumer |
| iot-postgres-secret |
IOT_PG_DSN |
iot-sqs-consumer |
Эволюция архитектуры
| Версия |
Дата |
Message bus |
Postgres |
Кластер |
| v0.1.50 |
2026-04-04 |
RabbitMQ (AMQP) |
emptyDir PVC |
sless (общий) |
| v0.1.68 |
2026-04-06 |
Kafka (segmentio/kafka-go) |
emptyDir PVC |
sless (общий) |
| v0.2.0 |
2026-04-12 |
shared-SQS (AWS SDK) |
Managed PG 17 |
iot-naeel (новый) |
| v0.2.3 |
2026-04-12 |
shared-SQS (AWS SDK) |
Managed PG 17 |
iot-naeel |