refactor: старые артефакты IoT перенесены в legacy/ (git mv, история сохранена); активное дерево очищено, go build OK
This commit is contained in:
@@ -1,241 +0,0 @@
|
||||
> ⛔⛔⛔ ЛЕГАСИ (2026-08-16) — СТАРЫЙ IoT (k8s-деплой). НЕ ПРИНИМАТЬ ВО ВНИМАНИЕ. Актуальное: HISTORY/2026-08-16-session-log.md
|
||||
|
||||
# 2026-04-12 — GitHub Copilot (Claude Opus 4.6)
|
||||
|
||||
## Задача
|
||||
Пользователь просит:
|
||||
1. Просмотреть всё в репе IoT
|
||||
2. Оценить замену Kafka и RabbitMQ на собственный SQS-service (из ~/terra/SQS-service)
|
||||
3. Контекст: проект переехал из ~/terra/sless в отдельную репу ~/remote_dev/IoT
|
||||
|
||||
## План
|
||||
1. Изучить текущую структуру IoT репы — что есть, как используется Kafka
|
||||
2. Посмотреть SQS-service — что он из себя представляет
|
||||
3. Посмотреть старую репу sless — что оттуда переехало, что осталось
|
||||
4. Дать краткую оценку замены Kafka/RabbitMQ → SQS
|
||||
|
||||
## Ход работы
|
||||
|
||||
### Анализ: что нашёл
|
||||
|
||||
**IoT — текущее использование Kafka:**
|
||||
- `cmd/mqtt-bridge/main.go` — MQTT → Kafka writer (топик `iot.telemetry`, библиотека `segmentio/kafka-go`)
|
||||
- `cmd/kafka-consumer/main.go` — Kafka reader → Postgres (consumer group `iot-pg-consumer`)
|
||||
- `internal/api/handler/iot_admin_stats_handler.go` — admin UI: показывает Kafka consumer lag
|
||||
- `cmd/iot-operator/main.go` — передаёт KAFKA_BROKERS в handler
|
||||
- `deployments/k8s/kafka.yaml` — деплоймент Kafka в кластере
|
||||
- В документации (deployment.md, iot-mvp-plan.md) упоминается RabbitMQ как изначальный план, но в коде **RabbitMQ нет** — в реальности уже Kafka
|
||||
|
||||
**SQS-service (shared-sqs):**
|
||||
- AWS SQS-совместимый сервис на Go (модуль `shared-sqs`)
|
||||
- HTTP API с SQS-протоколом: CreateQueue, SendMessage, ReceiveMessage, DeleteMessage и т.д.
|
||||
- Multi-tenant: TenantStore с auth
|
||||
- Persistence: Redis (write-through) + in-memory
|
||||
- Billing через PostgreSQL
|
||||
- Порт 4100, gorilla/mux
|
||||
- **Не Kafka-совместимый** — это pull-based очередь (SQS-like), не streaming platform
|
||||
|
||||
**RabbitMQ в IoT:** только в документах (plan). В коде не используется.
|
||||
|
||||
**shared-SQS (из Gitea README):**
|
||||
- AWS SQS-совместимый managed сервис — 17 операций SQS
|
||||
- Уже задеплоен: endpoint `https://qu.kube5s.ru`, Web UI `https://qu.kube5s.ru/ui/`
|
||||
- Multi-tenant с auth (JWT nubes → auto-create tenant → Access Key / Secret Key)
|
||||
- Можно использовать через стандартные AWS SDK (Go, Python boto3, Node.js @aws-sdk/client-sqs)
|
||||
- Persistence: Redis + in-memory
|
||||
- Billing: PostgreSQL
|
||||
- Benchmark: конкурентоспособен с Yandex MQ
|
||||
|
||||
### Выводы по замене Kafka → shared-SQS
|
||||
|
||||
**Что реально нужно сделать в IoT:**
|
||||
1. `mqtt-bridge`: заменить `kafka.Writer` → AWS SDK SQS `SendMessage` (endpoint = qu.kube5s.ru или внутрикластерный)
|
||||
2. `kafka-consumer` → переименовать в `sqs-consumer`: заменить `kafka.Reader` → SQS `ReceiveMessage` + `DeleteMessage` polling loop
|
||||
3. `admin stats handler`: вместо Kafka lag → SQS `GetQueueAttributes` (ApproximateNumberOfMessages)
|
||||
4. Удалить `deployments/k8s/kafka.yaml`
|
||||
5. Убрать `segmentio/kafka-go` из go.mod, добавить `aws-sdk-go-v2`
|
||||
6. Обновить env vars: KAFKA_BROKERS → SQS_ENDPOINT + SQS credentials
|
||||
7. Обновить документацию
|
||||
|
||||
**Плюс:** AWS SDK for Go — стандартная библиотека, код станет проще. shared-SQS уже живой.
|
||||
**Минус:** polling latency (ReceiveMessage WaitTimeSeconds до 20s) vs Kafka push. Для IoT телеметрии — приемлемо.
|
||||
|
||||
---
|
||||
|
||||
## Продолжение — GitHub Copilot (Claude Opus 4.6)
|
||||
|
||||
### Реализация замены Kafka → SQS
|
||||
|
||||
**Что сделано:**
|
||||
|
||||
1. **mqtt-bridge** — полностью переписан:
|
||||
- Убран `segmentio/kafka-go`, добавлен `aws-sdk-go-v2` (sqs, config, credentials)
|
||||
- `kafka.Writer` → `sqs.Client.SendMessage`
|
||||
- При старте: `GetQueueUrl` для резолва URL очереди "iot-telemetry"
|
||||
- Env vars: `SQS_ENDPOINT`, `SQS_ACCESS_KEY`, `SQS_SECRET_KEY`, `SQS_QUEUE_NAME`, `SQS_REGION`
|
||||
- Формат сообщения (MessageBody JSON) не изменился: `{namespace, device_id, topic, payload, received_at}`
|
||||
|
||||
2. **sqs-consumer** — создан с нуля (заменяет kafka-consumer):
|
||||
- Long polling: `ReceiveMessage(WaitTimeSeconds=20)` — минимизирует запросы при пустой очереди
|
||||
- At-least-once: `DeleteMessage` только после успешной записи в Postgres
|
||||
- Использует `iotpg.IoTPostgresStore` — тот же механизм per-tenant DB что и kafka-consumer
|
||||
|
||||
3. **admin stats handler** — переписан:
|
||||
- Вместо Kafka consumer lag → `GetQueueAttributes(ApproximateNumberOfMessages, ApproximateNumberOfMessagesNotVisible)`
|
||||
- Pod labels для consumer: `iot-kafka-consumer` → `iot-sqs-consumer`
|
||||
|
||||
4. **Баг .gitignore**: паттерны `mqtt-bridge` и `kafka-consumer` без `/` игнорировали `cmd/mqtt-bridge/` и `cmd/kafka-consumer/`. Исправлено добавлением `/` префикса.
|
||||
|
||||
5. **Баг router**: при удалении `KafkaBrokers` из handler init случайно удалилась строка `router := iotapi.NewRouter(h, log)`. Восстановлена.
|
||||
|
||||
### Деплой в новый кластер iot-naeel
|
||||
|
||||
**Обнаружения при деплое:**
|
||||
|
||||
1. Кластер **полностью новый** — namespace `sless` не существовал, ничего не задеплоено.
|
||||
|
||||
2. **Postgres** — пользователь указал использовать managed PG17, тот же инстанс что SQS billing.
|
||||
Credentials в `/home/naeel/terra/SQS-service/secrets/iot_pg.md`.
|
||||
Самодеплоенный postgres:16-alpine из YAML заменён на DSN к managed PG.
|
||||
|
||||
3. **EMQX** — пришлось создать deployment для IoT-репы заново, адаптировав из sless.
|
||||
Ключевое изменение: auth URL `sless-operator.sless.svc:9090` → `iot-operator.sless.svc:9090`.
|
||||
|
||||
4. **iot-operator deployment** — его не было в IoT-репе! Создан новый:
|
||||
- ServiceAccount + ClusterRole (iotdevices CRD, secrets, events, namespaces, leases)
|
||||
- ClusterRoleBinding
|
||||
- Deployment + Service :9090
|
||||
|
||||
5. **kubectl токен** истекал за 24 часа — пользователь обновлял вручную.
|
||||
|
||||
6. **Docker Hub** вместо pearlharbor (Harbor) — убраны imagePullSecrets, image `naeel/iot-operator:v0.2.0`.
|
||||
|
||||
7. **shared-SQS tenant** создан через API:
|
||||
- Tenant: `iot-service`, ID: `t-96afe7e9f781f6ca`
|
||||
- Queue: `iot-telemetry`
|
||||
- Admin token из Secret `shared-sqs-admin` в namespace `shared-sqs`
|
||||
|
||||
### Результат
|
||||
|
||||
Все 4 пода Running 1/1:
|
||||
- `iot-operator` — controller работает, MQTT auth/acl обрабатывает запросы
|
||||
- `emqx` — MQTT брокер, подключает IoT устройства
|
||||
- `iot-mqtt-bridge` — подписан на EMQX, SQS queue resolved
|
||||
- `iot-sqs-consumer` — подключён к PG и SQS, polling loop активен
|
||||
|
||||
TLS сертификат для `iot.kube5s.ru` выпускается cert-manager.
|
||||
|
||||
---
|
||||
|
||||
## Сессия 2 — Агент: GitHub Copilot (Claude Opus 4.6)
|
||||
|
||||
### Контекст
|
||||
Продолжение деплоя после замены Kafka→SQS. Новый кластер iot-naeel, namespace sless пустой.
|
||||
|
||||
### Что обнаружил
|
||||
1. kubeconfig токен истёк (JWT TTL=24ч) — пользователь обновил вручную
|
||||
2. Kafka отсутствует в кластере — уже нет, удалять нечего
|
||||
3. Namespace sless создан в предыдущей сессии, но пуст (только Secret iot-sqs-credentials)
|
||||
4. IoT Postgres: пользователь указал использовать managed PG из SQS-service (не self-hosted)
|
||||
- Креды в /home/naeel/terra/SQS-service/secrets/iot_pg.md
|
||||
- Host: postgresqlk8s-master.dc5db45d-f8b4-4fd0-ad33-ec4dd017f2d5.svc.cluster.local
|
||||
- PG17, managed через оператор
|
||||
5. EMQX: deployment-файла не было в IoT-репе, адаптировал из sless
|
||||
- Ключевое: auth URL `sless-operator` → `iot-operator`
|
||||
6. iot-operator deployment: не было, создал с нуля (RBAC, ServiceAccount, ClusterRole)
|
||||
|
||||
### Что сделал
|
||||
1. Обновил image во всех deployments: pearlharbor → Docker Hub naeel/iot-operator:v0.2.0
|
||||
2. Убрал imagePullSecrets (Docker Hub публичный)
|
||||
3. Собрал Docker образ на ВМ, запушил в Docker Hub (v0.2.0 + latest)
|
||||
4. Обновил iot-postgres.yaml: убрал self-hosted Deployment/Service, оставил только Secret с managed PG DSN
|
||||
5. Создал emqx.yaml (EMQX 5.5.1 + ConfigMap + Service, auth→iot-operator)
|
||||
6. Создал emqx-ws-ingress.yaml (ws Ingress + TLS cert-manager)
|
||||
7. Создал iot-operator.yaml (ServiceAccount + ClusterRole + ClusterRoleBinding + Deployment + Service)
|
||||
8. Установил CRD iotdevices.iot.kube5s.ru
|
||||
9. Создал секреты: iot-postgres-secret, iot-bridge-credentials
|
||||
10. Задеплоил всё: EMQX → operator → mqtt-bridge → sqs-consumer
|
||||
11. Все 4 пода Running 1/1, логи чистые
|
||||
|
||||
### Ошибки по ходу
|
||||
- Пытался редактировать файлы локально через VS Code tools вместо SSH на ВМ — нарушение правил
|
||||
- Пытался копировать файлы через scp — не нужно, ~/remote_dev/ = ~/terra/ (одна ФС)
|
||||
- Правило записано в copilot-instructions.md и в memory
|
||||
|
||||
### Результат
|
||||
Полный IoT стек развёрнут в кластере iot-naeel:
|
||||
- EMQX (MQTT :1883, WS :8083) → iot-operator (auth/acl, API :9090)
|
||||
- mqtt-bridge (MQTT→SQS) → sqs-consumer (SQS→Postgres)
|
||||
- Managed PG17, shared-SQS, cert-manager TLS
|
||||
- Ingress: wss://iot.kube5s.ru/mqtt, https://iot.kube5s.ru/console
|
||||
|
||||
### Следующий шаг
|
||||
E2E тест: создать устройство → MQTT publish → проверить SQS → Postgres → API.
|
||||
|
||||
---
|
||||
|
||||
## Сессия: Документация v0.2.3 (продолжение)
|
||||
|
||||
Агент: GitHub Copilot (Claude Opus 4.6)
|
||||
|
||||
### Что сделано
|
||||
|
||||
1. **architecture/current-v0.2.3.md** — полная архитектура:
|
||||
- Data flow diagram (Device → EMQX → Bridge → SQS → Consumer → PG → API)
|
||||
- Все 4 компонента с параметрами
|
||||
- CRD spec/status/reconcile logic
|
||||
- Auth: REST JWT, MQTT auth/acl, bridge auth
|
||||
- Storage: PG per-tenant, SQS
|
||||
- Docker image, сетевая схема, секреты, эволюция архитектуры
|
||||
|
||||
2. **api/endpoints-v0.2.3.md** — документация API:
|
||||
- Изучил router.go, handler.go, iot_device_handler.go, iot_telemetry_handler.go, iot_admin_stats_handler.go
|
||||
- Задокументировал ВСЕ endpoints: CRUD, telemetry, MQTT auth/acl, admin stats, UI
|
||||
- Request/response примеры с точными JSON форматами
|
||||
- Curl примеры для всех операций
|
||||
|
||||
3. **run-and-test.md** — руководство по тестированию:
|
||||
- Сборка, деплой, E2E тест по шагам
|
||||
- Проверка компонентов, отладка
|
||||
- WebSocket и port-forward варианты MQTT
|
||||
|
||||
4. **progress.md** — дополнена секция документации
|
||||
|
||||
### Проблемы
|
||||
- Скрипт /tmp/write_arch.py был хардкодом на один файл — случайно перезаписал architecture doc при тесте
|
||||
- Создал универсальный /tmp/write_file.py с аргументом пути — больше проблем нет
|
||||
|
||||
### Осталось
|
||||
- git commit + push
|
||||
|
||||
---
|
||||
|
||||
## Агент: GitHub Copilot (Claude Opus 4.6) — E2E тестирование и баг-фиксы
|
||||
|
||||
### Баг 1: MQTTAuth ищет IoTDevice по Name=deviceID
|
||||
|
||||
**Проблема**: `MQTTAuth` хэндлер вызывал `h.K8s.Get(client.ObjectKey{Name: deviceID})`, но имя K8s объекта IoTDevice (`e2e-test-device`) не равно `Spec.DeviceID` (`e2e-test-01`). Результат — `deny`.
|
||||
|
||||
**Диагностика**:
|
||||
1. Auth endpoint вернул `{"result":"deny"}` при прямом вызове curl
|
||||
2. Secret `iot-e2e-test-01` существует и пароль совпадает → проблема НЕ в пароле
|
||||
3. IoTDevice объект называется `e2e-test-device`, а MQTTAuth ищет по `Name: "e2e-test-01"` → NotFound → deny
|
||||
|
||||
**Фикс**: Заменил `Get` на `List` + фильтр по `Spec.DeviceID == deviceID`. Это O(n) по количеству устройств в namespace, но для MVP приемлемо. При необходимости можно добавить label-index.
|
||||
|
||||
### Баг 2: PG15+ требует GRANT перед CREATE DATABASE ... OWNER
|
||||
|
||||
**Проблема**: `EnsureTenantDB` делал `CREATE DATABASE tenant_sless OWNER tenant_sless`, но PG17 (PG15+) требует `SET ROLE` privileges для target owner. Ошибка: `pq: must be able to SET ROLE "tenant_sless" (42501)`.
|
||||
|
||||
**Фикс**: Добавил `GRANT {userName} TO CURRENT_USER` перед `CREATE DATABASE ... OWNER`.
|
||||
|
||||
### E2E тест v0.2.5 — полный пайплайн
|
||||
|
||||
1. `POST /v1/namespaces/sless/iot/devices` → 201, device created
|
||||
2. `GET /devices/e2e-test-device` → phase=Active, credentials получены
|
||||
3. `mosquitto_pub` → CONNACK(0), PUBLISH OK
|
||||
4. mqtt-bridge → `forwarded IoT telemetry to SQS`
|
||||
5. sqs-consumer → `created tenant DB` + `telemetry saved to Postgres`
|
||||
6. `GET /v1/namespaces/sless/iot/telemetry?device_id=e2e-test-01` → 2 записи с temperature/humidity
|
||||
|
||||
**Все компоненты работают end-to-end.**
|
||||
Reference in New Issue
Block a user