11 KiB
Оценка готовности IoT к production (2026-10-05)
Три взгляда на один репозиторий: анализ A, анализ B и моё мнение. Все три — только чтением кода и документации, без сборки и запуска тестов (правила репо: команды — только через SSH на ВМ; сборку/прогоны не выполняли).
1. Анализ A (первый) — итог 5/10
Вердикт: цельный MVP, но по коду не считать production-ready для надёжной multi-tenant эксплуатации.
Блокеры
- Не проверяется принадлежность namespace пользователю. Middleware
проверяет JWT, но не передаёт
subобработчикам для сравнения с namespace из URL. CRUD и выдача пароля устройства берут namespace из запроса. Валидный токен не ограничивает пользователя его tenant-ом:auth.go:20,router.go:54,devices.go:179.expфактически необязателен (auth.go:120). - Путь потери уже принятых сообщений. Flusher заменяет буфер на пустой
ДО записи в PG, при ошибке только логирует. Consumer при этом удаляет
сообщения из SQS после добавления в буфер:
iot_telemetry_store.go:129,:135,consumer.go:81. - Health зелёный при остановившемся pipeline. Ошибки запуска bridge/consumer
только логируются; горутина завершается, а
/healthвсегда отдаётok:main.go:106,router.go:74. - Доставка допускает потери и дубли. Переполненная RAM-очередь bridge
сбрасывает сообщения; исчерпание SQS-ретраев заканчивается drop. При
at-least-once нет идемпотентного ключа против дублей:
sender.go:70,:112.
Что хорошо
Компоненты сведены в понятный единый сервис; есть MQTT-reconnect с повторной подпиской, SQS-ретраи, graceful shutdown, разделение хранения устройств и телеметрии, широкий набор нагрузочных сценариев.
Чего не хватает до уверенного релиза
- Автоматических Go-тестов (
*_test.goне найдено) и CI. - Согласованности доков с кодом: гайд деплоя —
iot-service:v0.1.2, Makefile —v0.1.13; loadtest README описывает уже исправленную блокировку MQTT-обработчика и visibility 30с, код задаёт 120с:deployment-nubes-production.md:3,Makefile:4,README.md:85,config.go:114.
2. Анализ B (второй) — итог ~60–65%
Вердикт: рабочий MVP / технический прототип, не продакшн-готовый сервис.
Что хорошо
- Конвейер устройство → EMQX → мост → SQS → consumer → PG (per-tenant) работает end-to-end; подтверждено HISTORY и нагрузочными прогонами.
- Прогоны: baseline 2390/2390 без потерь/дублей; ACL-нарушения отсекаются; reconnect-storm прошёл чисто.
- Код небольшой (~2,4 тыс. строк в монолите), аккуратный: graceful shutdown, асинхронный SQS-диспатчер с ретраями, batch-insert, backoff, constant-time сравнение паролей.
- Документация подробная: деплой без kubectl, интеграция с SQS, лог ревью и фиксов, 10 нагрузочных сценариев.
- Auth и ACL в EMQX работают напрямую через PostgreSQL, без зависимости от монолита.
Что мешает назвать готовым
- Нет ни одного Go-теста и нет CI. Только Python-нагрузочные скрипты;
в
.github/одни правила. - Изоляция тенантов слабая. JWT проверяет только
subиexp; namespace из URL не сверяется с токеном. БезJWT_HMAC_SECRETподпись не проверяется. Любой «валидный» токен читает устройства и пароли любого namespace. - Пароли устройств в БД открытым текстом и отдаются через GET. EMQX —
password_hash_algorithm = plain. AUTH_TEST_MODE=true(любой Bearer) был включён на проде для диагностики. HISTORY §38 — «вернуть false»; закрытия не видно./internal/mqtt/authи/internal/mqtt/aclоткрыты на публичном порту без авторизации; дублируют PG-auth в EMQX и уже не нужны. ACL выдаёт права моста любому клиенту сclientid, совпадающим с мостовым.- Нерешённая платформенная проблема: плавающий обрыв ответов >~15–64 КБ и WebSocket-обрывы ~150с; стабильного воспроизведения нет, разбор с DevOps идёт. В коде обходится пагинацией.
- Отложено: whitelist тенантских БД (любой namespace создаёт базу), DLQ, soak 24ч.
- Легаси не вычищено:
cmd/iot-operator,controllers/,internal/api/,cmd/mqtt-bridge,cmd/sqs-consumer; вgo.mod12 строк k8s/controller-runtime; бинарники вbin/иws-probe/ws-probe.LEGACY.mdобещает убрать после переписывания хендлеров, а оно сделано. - Дрейф конфигурации:
cookie = "iot-emqx-cookie-mvp"захардкожен вemqx.conf; параметры EMQX задаются только через env в deck-UI. - Нет TLS-верификации к PG (
verify_none). Нет rate limiting и метрик (только/healthи admin-статистика).
Минимум для продакшна
- привязать namespace к JWT, сделать
JWT_HMAC_SECRETобязательным; - убедиться, что
AUTH_TEST_MODEвыключен; - хешировать пароли устройств или не отдавать повторно;
- удалить
/internal/mqtt/*и легаси; - добавить тесты и CI;
- закрыть вопрос обрывов на платформе.
3. Моё мнение (перепроверено по коду)
Оба анализа компетентны и в главном совпадают. Расхождение 5/10 vs 60–65% — не в фактах, а в весе, который они дают одним и тем же находкам. Спорные места я перепроверил сам — оба правы по существу.
Подтверждается кодом (не предположение)
- Изоляция тенантов слабая — верно.
middleware/auth.goпроверяет JWT только наsub+exp;subне сверяется с{namespace}(claims не передаются в обработчики,router.goне извлекаетsub).expнеобязателен (if claims.Exp > 0), безJWT_HMAC_SECRETподпись не проверяется. - Путь потери принятых сообщений — верно.
processTelemetry→InsertTelemetryкладёт в in-memory буфер и возвращает nil, после чего consumer безусловно удаляет сообщение из SQS.flushAllподменяет буфер на пустой ДО записи в PG и при ошибке только логирует. Данные могут молча пропасть. /healthзелёный при упавшем pipeline — верно.main.goошибки bridge/consumer только логирует; горутина завершается,healthHandlerвсегда отдаётok.- Нет Go-тестов и CI — верно.
*_test.goотсутствует,.github/— правила. AUTH_TEST_MODEи пароли plaintext — верно и задокументировано (password_hash_algorithm=plain,AUTH_TEST_MODE=trueдля DevOps, §38).- Дрейф доков — верно: гайд
v0.1.2vs Makefilev0.1.13; loadtest README 30с vs код 120с. Анализ A зафиксировал точнее.
Где анализ B точнее/дополняет
- Явно оговорил, что сборку/тесты не запускал (ограничение правил — SSH).
- Глубже разобрал открытые
/internal/mqtt/*, TLSverify_none, захардкоженныйcookie, невычищенное легаси и k8s-зависимости вgo.mod.
Где я бы поправил обоих
- Проценты условны. Оба дают один вывод «MVP/прототип, не production-ready»; цифры — риторика, а не измерение.
- Приоритет блокеров: главные технические — потеря сообщений и tenant-граница.
Plaintext-пароли и unauthed
/internal/mqtt/*— важные, но второго эшелона (периметр платформы + EMQX уже на PG-auth напрямую снижают остроту).
Итог
Рабочий MVP, пригодный для демо/пилота, но не production-ready. Главные блокеры: слабая tenant-изоляция, гарантированная потеря сообщений при сбое flush, «слепой» health, отсутствие Go-тестов/CI и не закрытая платформенная проблема wss ~150с / MTU. Моя оценка — между A и B, ближе к «функционально готов, эксплуатационно — нет».