101 lines
6.4 KiB
Markdown
101 lines
6.4 KiB
Markdown
# 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`.
|