Files
IoT/doc/nubes-deploy-checklist.md

101 lines
6.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# IoT на Nubes — чек-лист деплоя (актуально на 2026-10-11)
> Детальная процедура — `deployment-nubes-production.md`. Здесь — конкретные
> шаги текущего стенда. Секреты НЕ хранятся в репо (`*.key` в `.gitignore`);
> значения `<>` берутся из `pg.key`/deck/генератора.
## 0. Готово
- **Образы в Gitea** (`gitea.services.ngcloud.ru/nail`):
- `iot-service:v0.1.13` (+ latest)
- `iot-emqx:v0.2.5` (+ latest), `iot-service:v0.1.13` (+ latest)
⚠️ **Образ обязан иметь РОВНО ОДИН `EXPOSE`** — платформа Nubes отказывает в создании
контейнера с «Не указан applicationUsePort…», если EXPOSE несколько. У `emqx/emqx:5.5.1`
их 7 → образ собирается через `FROM scratch` + `COPY --from=base / /` (EXPOSE не
наследуется), остаётся только `8083`. Параметра `applicationUsePort` у провайдера nubes
3.1.0 нет — обойти на стороне Terraform нельзя, только пересборкой образа.
- **shared-SQS** тенант `demo-service` (`SSAK-b8fb2b3000c178b865091686`, регион `us-east-1`):
- очередь `iot-telemetry` — создана;
- DLQ `iot-telemetry-dlq` — создана.
- публичный endpoint: `https://sqs.sinefo.ru`.
- **PostgreSQL** managed: хост `postgresqlk8s-master.509145c3-ba71-4687-a084-d9dcbc88d3d2.svc.cluster.local:5432` (из `pg.key`).
✅ **создано через Nubes API (test):** пользователь `iotservice` (role `ddl_user` → CREATEDB+CREATEROLE), БД `iotdb` (owner `iotservice`). Пароль `iotservice` — в vault инстанса.
## 1. PostgreSQL — ✅ ГОТОВО
Пользователь `iotservice` (role `ddl_user`, права CREATEDB+CREATEROLE) и БД `iotdb` (owner `iotservice`)
созданы через Nubes API (test): операции `create_user` (svcOperationId 241) и `create_database` (245)
на инстансе `509145c3-…`. ⚠ Гранулярность: имя PG-пользователя подчиняется платформенному regex
`^[A-Za-z0-9]+$` → `iot_service` НЕЛЬЗЯ (только `iotservice`).
Пароль: GET `/instances/509145c3-…/vault/users` (Nubes API, test) → поле `iotservice.password`.
Подключение:
```
IOT_PG_DSN = postgresql://iotservice:<PG_PASS>@postgresqlk8s-master.509145c3-ba71-4687-a084-d9dcbc88d3d2.svc.cluster.local:5432/iotdb?sslmode=require
```
Таблицы (`iot_devices`, per-tenant БД телеметрии) создаются кодом при старте.
## 2. EMQX-контейнер (создаётся ПЕРВЫМ, CPU ≥ 1000m)
Образ: `gitea.services.ngcloud.ru/nail/iot-emqx:v0.2.5`, порт из EXPOSE = `8083`.
Env (все — при создании, потом не меняются):
| Переменная | Значение |
|---|---|
| `EMQX_AUTHENTICATION__1__SERVER` | `postgresqlk8s-master.509145c3-ba71-4687-a084-d9dcbc88d3d2.svc.cluster.local:5432` |
| `EMQX_AUTHENTICATION__1__DATABASE` | `iotdb` |
| `EMQX_AUTHENTICATION__1__USERNAME` | `iotservice` |
| `EMQX_AUTHENTICATION__1__PASSWORD` | `<PG_PASS>` |
| `EMQX_AUTHORIZATION__SOURCES__1__SERVER` | как AUTHENTICATION |
| `EMQX_AUTHORIZATION__SOURCES__1__DATABASE` | `iotdb` |
| `EMQX_AUTHORIZATION__SOURCES__1__USERNAME` | `iotservice` |
| `EMQX_AUTHORIZATION__SOURCES__1__PASSWORD` | `<PG_PASS>` |
| `EMQX_LOG__CONSOLE_HANDLER__LEVEL` | `warning` |
⚠ Не задавать `...__URL` (HTTP-auth старой схемы — уронит под). SSL обязателен.
После создания — записать **UUID namespace EMQX** (нужен в `MQTT_HOST`).
⚠️ Внутренний Service в namespace платформы называется **`containerk8s`**, НЕ `iot-emqx`
(проверено 2026-10-11):
- `containerk8s.<instanceUid>.svc.cluster.local` → резолвится ✅
- `iot-emqx.<instanceUid>.svc.cluster.local` → NXDOMAIN ❌
⚠️ Роль-владелец таблиц: в БД `iotdb` таблицы `iot_devices`/`tenant_credentials` были созданы
ролью `iot_service` (деплой 2026-08-16). Приложению нужен ВЛАДЕЛЕЦ (иначе
`store: init schema: pq: must be owner of table iot_devices (42501)` → под падает, платформа
откатывает установку). Владение передано роли `iotservice` 2026-10-11:
`ALTER TABLE public.iot_devices OWNER TO iotservice;` + `tenant_credentials` + индексы.
## 3. iot-service-контейнер (создаётся ВТОРЫМ)
Образ: `gitea.services.ngcloud.ru/nail/iot-service:v0.1.13`, порт из EXPOSE = `9090`.
Env (обязательные — секреты; необязательные имеют дефолты в образе):
| Переменная | Значение |
|---|---|
| `IOT_PG_DSN` | `postgresql://iotservice:<PG_PASS>@postgresqlk8s-master.509145c3-ba71-4687-a084-d9dcbc88d3d2.svc.cluster.local:5432/iotdb?sslmode=require` |
| `SQS_ACCESS_KEY` | `SSAK-b8fb2b3000c178b865091686` |
| `SQS_SECRET_KEY` | `<из Admin API / лога сервиса>` |
| `MQTT_USERNAME` | `iot-bridge` (device_id строки бриджа) |
| `MQTT_PASSWORD` | `<сгенерировать>` |
| `JWT_HMAC_SECRET` | `<сгенерировать>` |
| `SQS_ENDPOINT` | `https://sqs.sinefo.ru` (не дефолт: shared-sqs НЕ в кластере, а на ВМ 213) |
| `SQS_QUEUE_NAME` | `iot-telemetry` (дефолт) |
| `SQS_DLQ_NAME` | `iot-telemetry-dlq` (дефолт) |
| `MQTT_HOST` | `containerk8s.<UUID namespace EMQX из шага 2>.svc.cluster.local` (см. предупреждение в шаге 2) |
| `MQTT_PORT` | `8083` (дефолт) |
| `AUTH_TEST_MODE` | `false` |
При старте монолит: подключается к PG, создаёт схему, `EnsureBridgeDevice(MQTT_USERNAME,
MQTT_PASSWORD)` → строка `__bridge/iot-bridge` в `iot_devices` для EMQX-auth.
## 4. Проверка
- `/health` и `/ready` (порт 9090) → 200.
- В логе: `bridge: MQTT connected`, `bridge: subscribed`, `consumer: SQS queue resolved`.
- EMQX: `curl https://<emqx-домен>/mqtt` → 400 (норма). wss-рука — клиент с subprotocol `mqtt`.