Files
IoT/doc/deployment-v0.2.3.md
T

298 lines
9.9 KiB
Markdown
Raw 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.
> ⛔⛔⛔ ЛЕГАСИ (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="<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:<password>@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`