> ⛔⛔⛔ ЛЕГАСИ (2026-08-16) — СТАРЫЙ IoT (k8s-деплой). НЕ ПРИНИМАТЬ ВО ВНИМАНИЕ. Актуальное: HISTORY/2026-08-16-session-log.md # IoT Managed Service — Актуальный деплой (v0.2.3) > Дата: 2026-04-12 > Образ: `naeel/iot-operator:v0.2.3` > Ветка: `feature/replace-kafka-with-sqs` > Кластер: `iot-naeel` > Автор: GitHub Copilot (Claude Opus 4.6) --- ## Обзор Три бинарника из одного Docker-образа `naeel/iot-operator:v0.2.3`: | Бинарник | Deployment | Назначение | |----------|-----------|------------| | `/iot-operator` | iot-operator | Controller-manager (IoTDevice CRD) + REST API :9090 | | `/mqtt-bridge` | iot-mqtt-bridge | MQTT (EMQX) → shared-SQS bridge | | `/sqs-consumer` | iot-sqs-consumer | shared-SQS → Postgres pipeline | Плюс: - EMQX 5.5.1 — MQTT-брокер (отдельный образ `emqx/emqx:5.5.1`) --- ## Кластер | Параметр | Значение | |----------|----------| | Имя | `iot-naeel` | | API | `https://185.247.187.149:6443` | | Ingress IP | `185.247.187.151` | | DNS | `iot.kube5s.ru → 185.247.187.147` | | Namespace | `sless` | | kubeconfig | `~/.kube/config` на ВМ (токен 24ч, обновлять через auth.k8s.ngcloud.ru) | --- ## Секреты (namespace sless) Перед первым деплоем создать 3 секрета: ### 1. iot-bridge-credentials (MQTT bridge → EMQX) ```bash kubectl create secret generic iot-bridge-credentials -n sless \ --from-literal=MQTT_USERNAME="iot-bridge-internal" \ --from-literal=MQTT_PASSWORD="<пароль bridge>" ``` Используется: - **iot-operator** — для проверки bridge auth в `/internal/mqtt/auth` (env: MQTT_BRIDGE_USERNAME, MQTT_BRIDGE_PASSWORD) - **iot-mqtt-bridge** — для подключения к EMQX (envFrom: secretRef) ### 2. iot-sqs-credentials (shared-SQS) ```bash kubectl create secret generic iot-sqs-credentials -n sless \ --from-literal=SQS_ENDPOINT="https://qu.kube5s.ru" \ --from-literal=SQS_ACCESS_KEY="SSAK-a9964f2723bc6d347f48d153" \ --from-literal=SQS_SECRET_KEY="" ``` Используется: - **iot-mqtt-bridge** — для SendMessage - **iot-sqs-consumer** — для ReceiveMessage + DeleteMessage ### 3. iot-postgres-secret (Managed PostgreSQL) ```bash kubectl create secret generic iot-postgres-secret -n sless \ --from-literal=IOT_PG_DSN="postgres://super:@postgresqlk8s-master.dc5db45d-f8b4-4fd0-ad33-ec4dd017f2d5.svc.cluster.local:5432/sqsdb?sslmode=disable" ``` Используется: - **iot-sqs-consumer** — для записи телеметрии в per-tenant DB --- ## Процедура деплоя (пошагово) ### Все команды выполнять на ВМ через SSH ```bash ssh -i ~/.ssh/id_ed25519 naeel@5.172.178.213 cd /home/naeel/terra/IoT ``` ### 0. Сборка образа (если код менялся) ```bash docker build --no-cache -t naeel/iot-operator:v0.2.3 . docker push naeel/iot-operator:v0.2.3 ``` ### 1. CRD (один раз, cluster-wide) ```bash kubectl apply -f config/crd/bases/iot.kube5s.ru_iotdevices.yaml ``` ### 2. EMQX ```bash kubectl apply -f deployments/k8s/emqx.yaml kubectl apply -f deployments/k8s/emqx-ws-ingress.yaml ``` ### 3. Postgres secret (один раз) ```bash kubectl apply -f deployments/k8s/iot-postgres.yaml ``` ### 4. SQS secret (один раз) ```bash kubectl get secret iot-sqs-credentials -n sless # Если нет — создать (см. секцию Секреты выше) ``` ### 5. Bridge credentials (один раз) ```bash kubectl get secret iot-bridge-credentials -n sless # Если нет — создать (см. секцию Секреты выше) ``` ### 6. Operator ```bash kubectl apply -f deployments/k8s/iot-operator.yaml kubectl rollout status deployment/iot-operator -n sless ``` ### 7. MQTT Bridge ```bash kubectl apply -f deployments/k8s/iot-mqtt-bridge.yaml kubectl rollout status deployment/iot-mqtt-bridge -n sless ``` ### 8. SQS Consumer ```bash kubectl apply -f deployments/k8s/iot-sqs-consumer.yaml kubectl rollout status deployment/iot-sqs-consumer -n sless ``` ### 9. Проверка ```bash kubectl get pods -n sless # Ожидаемый результат: # emqx-xxx 1/1 Running # iot-operator-xxx 1/1 Running # iot-mqtt-bridge-xxx 1/1 Running # iot-sqs-consumer-xxx 1/1 Running kubectl logs -n sless deployment/iot-operator --tail=5 kubectl logs -n sless deployment/iot-mqtt-bridge --tail=5 kubectl logs -n sless deployment/iot-sqs-consumer --tail=5 ``` --- ## Обновление образа (rollout) ```bash # 1. Собрать новый образ docker build --no-cache -t naeel/iot-operator:v0.2.4 . docker push naeel/iot-operator:v0.2.4 # 2. Обновить тег во ВСЕХ трёх YAML sed -i 's/v0.2.3/v0.2.4/g' deployments/k8s/iot-operator.yaml \ deployments/k8s/iot-mqtt-bridge.yaml \ deployments/k8s/iot-sqs-consumer.yaml # 3. Применить kubectl apply -f deployments/k8s/iot-operator.yaml kubectl apply -f deployments/k8s/iot-mqtt-bridge.yaml kubectl apply -f deployments/k8s/iot-sqs-consumer.yaml # 4. Проверить kubectl get pods -n sless -w ``` **ВАЖНО:** Если образ с тем же тегом — Kubernetes не скачает новый (кэш на нодах). Всегда менять тег. --- ## Компоненты: env vars ### iot-operator | Переменная | Значение | Источник | |-----------|----------|----------| | MQTT_BRIDGE_USERNAME | iot-bridge-internal | Secret iot-bridge-credentials (key: MQTT_USERNAME) | | MQTT_BRIDGE_PASSWORD | (пароль) | Secret iot-bridge-credentials (key: MQTT_PASSWORD) | ### iot-mqtt-bridge | Переменная | Значение | Источник | |-----------|----------|----------| | MQTT_BROKER_URL | tcp://emqx.sless.svc:1883 | YAML env | | SQS_QUEUE_NAME | iot-telemetry | YAML env | | SQS_REGION | us-east-1 | YAML env | | MQTT_USERNAME | iot-bridge-internal | Secret iot-bridge-credentials | | MQTT_PASSWORD | (пароль) | Secret iot-bridge-credentials | | SQS_ENDPOINT | https://qu.kube5s.ru | Secret iot-sqs-credentials | | SQS_ACCESS_KEY | SSAK-... | Secret iot-sqs-credentials | | SQS_SECRET_KEY | (secret) | Secret iot-sqs-credentials | ### iot-sqs-consumer | Переменная | Значение | Источник | |-----------|----------|----------| | SQS_QUEUE_NAME | iot-telemetry | YAML env | | SQS_REGION | us-east-1 | YAML env | | SQS_ENDPOINT | https://qu.kube5s.ru | Secret iot-sqs-credentials | | SQS_ACCESS_KEY | SSAK-... | Secret iot-sqs-credentials | | SQS_SECRET_KEY | (secret) | Secret iot-sqs-credentials | | IOT_PG_DSN | postgres://... | Secret iot-postgres-secret | --- ## Managed PostgreSQL | Параметр | Значение | |---------|----------| | Версия | PostgreSQL 17 | | Host | postgresqlk8s-master.dc5db45d-f8b4-4fd0-ad33-ec4dd017f2d5.svc.cluster.local | | Port | 5432 | | User | super | | DB | sqsdb | | Тип | Managed (оператор в другом namespace) | Per-tenant изоляция: для каждого namespace создаётся отдельная DATABASE `tenant_{namespace}` с таблицей `iot_telemetry`. --- ## shared-SQS (очередь) | Параметр | Значение | |---------|----------| | Endpoint | https://qu.kube5s.ru | | Tenant | iot-service (ID: t-96afe7e9f781f6ca) | | Очередь | iot-telemetry | | Протокол | AWS SQS API (AWS SDK compatible) | --- ## Версии образов (история) | Версия | Дата | Изменения | |--------|------|-----------| | v0.2.3 | 2026-04-12 | Bridge auth fix (BridgeUsername/BridgePassword), debug logs | | v0.2.2 | 2026-04-12 | Bridge auth (первая попытка, проблема с табами) | | v0.2.1 | 2026-04-12 | Bridge auth (проблема IsSuperuser field) | | v0.2.0 | 2026-04-12 | Kafka->SQS, managed PG, новый кластер iot-naeel | | v0.1.69 | 2026-04-06 | Kafka Async write fix (1000/1000 тест) | | v0.1.68 | 2026-04-06 | Kafka pipeline (bridge->Kafka->consumer->Postgres) | | v0.1.50 | 2026-04-04 | Первый IoT: controller + API + mqtt-bridge (RabbitMQ) | --- ## Известные ошибки и решения (v0.2.x) ### mqtt-bridge: MQTT connect timeout / CrashLoopBackOff **Симптом:** bridge не подключается к EMQX, логи "MQTT connect timeout" **Причина:** EMQX вызывает `/internal/mqtt/auth` при CONNECT. Bridge username `iot-bridge-internal` не содержит `_` -> MQTTAuth парсит namespace_deviceId -> ошибка -> deny -> EMQX возвращает not_authorized -> bridge retry 30с -> выглядит как timeout. **Решение (v0.2.3):** В Handler добавлены поля BridgeUsername/BridgePassword. MQTTAuth проверяет bridge credentials ДО парсинга namespace_deviceId. Если username совпадает — allow. ### Docker image cache на k8s нодах **Симптом:** после `docker push` новый образ не используется, pod стартует со старым кодом. **Причина:** imagePullPolicy=Always работает, но если тег не изменился Kubernetes может использовать кэш ноды. **Решение:** Всегда менять тег при пересборке (v0.2.1 -> v0.2.2 -> v0.2.3 и т.д.) ### EMQX: required_field node.cookie/node.data_dir **Симптом:** EMQX CrashLoopBackOff при первом старте **Причина:** EMQX 5.x требует явных node.cookie и node.data_dir **Решение:** В emqx.conf (ConfigMap) обязательны: ```hocon node { name = "emqx@127.0.0.1", cookie = "...", data_dir = "/opt/emqx/data" } ``` После изменения ConfigMap: `kubectl rollout restart deployment/emqx -n sless`