172 lines
13 KiB
Markdown
172 lines
13 KiB
Markdown
# Журнал сессии — 2026-08-16 (IoT: интеграция с shared-sqs, выбор пути миграции)
|
||
|
||
**Рабочая папка:** /home/naeel/nubes/IoT
|
||
|
||
> Правило этой сессии (задано пользователем): ВСЁ документировать в HISTORY,
|
||
> каждый шаг — сразу.
|
||
|
||
---
|
||
|
||
## 1. Контекст (состояние на входе)
|
||
|
||
- Пользователь завершил handoff по shared-sqs:
|
||
- `HANDOFF-NEW-CHAT.md` — в SQS-репозитории, закоммичен и запушен (`5fa34c2`).
|
||
- `doc/sqs-integration.md` — создан в IoT-репозитории, **не закоммичен** (untracked).
|
||
- `2026-08-13-migration-to-managed.md` — также untracked в IoT-репо.
|
||
- Git IoT: HEAD `368ff06` (feature/replace-kafka-with-sqs), origin отстаёт на 1 коммит
|
||
(HEAD не запушен: «fix(ingress): remove cert-manager + ssl-redirect»).
|
||
|
||
## 2. `doc/sqs-integration.md` — что важно для IoT (прочитан, суть)
|
||
|
||
- shared-sqs: прод v0.1.35, все тесты зелёные (api 22/22, sdk 15/15, FIFO/DLQ e2e),
|
||
нагрузка 168 591 операция — 0 сервисных ошибок.
|
||
- Внешний endpoint: `https://sqs.containerk8s.dev.nubes.ru` (регион us-east-1).
|
||
- Внутренний endpoint (поды того же кластера Nubes, realm iot-naeel):
|
||
`http://containerk8s.f1ffb134-7d16-45bd-8bef-69f6ec8ab33c.svc.cluster.local:4100`.
|
||
- Проблемы ПЛАТФОРМЫ (не сервиса):
|
||
1. Таймауты каждые 31–33с на внешнем пути (~5.5% запросов без ответа 30с).
|
||
Доказано tcpdump; внутри кластера потерь нет (port-forward 24977 раундов, 0 сбоев).
|
||
2. MSS=1448 при MTU 1400 → тела >~1400 байт виснут ~51с (PMTUD сломан).
|
||
Тикет Nubes подготовлен (`SQS-service/doc/thinking/nubes-ticket.md`).
|
||
- Рекомендации интеграции: AWS SDK + override endpoint; главная — внутренний адрес
|
||
(обходит шлюз полностью); ретраи ≥3, read_timeout ≥30с (для внешнего пути);
|
||
сообщения до ~1300 байт; VisibilityTimeout 30с+; FIFO требует MessageGroupId
|
||
(dedup 5 мин); DLQ через RedrivePolicy; лимит 50 очередей на тенанта.
|
||
- Креды детерминированы из email: tenantID `t-`+sha256(email)[:8],
|
||
AccessKey `SSAK-`+sha256(email)[:12], SecretKey sha256(email+":shared-sqs:secret-key:v1").
|
||
- Блокер прода — только тикет Nubes (для интернет-клиентов); для внутренней
|
||
интеграции блокеров нет.
|
||
|
||
## 3. Как сейчас развёртывается IoT (факты из репозитория)
|
||
|
||
- Код полностью на Go 1.25, модуль `gitea.services.ngcloud.ru/Nail/IoT`.
|
||
- Один образ Docker Hub `naeel/iot-operator:v0.2.6` (distroless), три бинарника:
|
||
`/iot-operator`, `/mqtt-bridge`, `/sqs-consumer`.
|
||
- Собственный k8s кластер `iot-naeel`, namespace `sless`, API `185.247.187.149:6443`.
|
||
- Деплой с ВМ: `ssh naeel@5.172.178.213`, каталог `/home/naeel/terra/IoT`,
|
||
`kubectl apply -f deployments/k8s/` (kubeconfig на ВМ, токен ~24ч).
|
||
- Компоненты:
|
||
- `iot-operator` — controller-manager (CRD IoTDevice) + REST API :9090;
|
||
- `iot-mqtt-bridge` — EMQX → SQS SendMessage;
|
||
- `iot-sqs-consumer` — SQS → Postgres (long poll 20с, delete после записи);
|
||
- EMQX 5.5.1 — MQTT-брокер (HTTP auth → operator).
|
||
- Секреты (ns sless): `iot-bridge-credentials`, `iot-sqs-credentials`
|
||
(`SQS_ENDPOINT=https://qu.kube5s.ru` — старый внешний адрес, устарел),
|
||
`iot-postgres-secret` (managed PG, `...svc.cluster.local:5432/sqsdb`).
|
||
- Ingress: устройства по `wss://iot.kube5s.ru/mqtt`.
|
||
- Миграция на managed Node.js (план от 13.08) — НЕ выполнена; Node.js-кода в репо нет.
|
||
|
||
## 4. Обсуждение: HTTP-контейнер vs Managed Node.js (вывод этой сессии)
|
||
|
||
- Вывод AI: HTTP-контейнер (как у shared-sqs) лучше: Go сохраняется целиком,
|
||
Dockerfile готов, схема доказана shared-sqs, внутренняя сеть без таймаутов.
|
||
Managed Node.js = полный rewrite (aedes, Express, @aws-sdk/client-sqs, pg) — запасной.
|
||
- Открытый риск контейнера: поддержка WebSocket upgrade на платформенном ingress
|
||
не проверена (устройства ходят по wss).
|
||
- Устройства в любом варианте ходят через внешний шлюз Nubes — проблемы шлюза
|
||
(5.5% таймаутов, MSS) остаются риском для wss в обоих вариантах.
|
||
|
||
## 5. Чтение SQS-репозитория (`/home/naeel/nubes/SQS-service`) — выяснено
|
||
|
||
- **План Node.js фактически отменён.** `doc/2026-08-13-migration-to-nubes.md`:
|
||
«Код не меняется вообще. Только инфраструктура».
|
||
- shared-sqs мигрировал в **«Простой HTTP контейнер» (nubes_http)** через deck-UI,
|
||
realm `iot-naeel`. Домен `sqs.containerk8s.dev.nubes.ru`, `/health` OK.
|
||
- Технические факты платформы (из HISTORY 13–14.08):
|
||
- Контейнер = k8s deployment `containerk8s`, namespace = UUID инстанса
|
||
(`f1ffb134-7d16-45bd-8bef-69f6ec8ab33c`), контейнер в поде называется `app`, порт 4100.
|
||
- Внутренний DNS: `containerk8s.<uuid-inst>.svc.cluster.local:4100`.
|
||
- Внешний: `*.containerk8s.dev.nubes.ru`, ingress/TLS встроены в контейнер.
|
||
- Образ — ТОЛЬКО публичный Docker Hub (Gitea/GHCR приватные платформа не тянет).
|
||
- Обновление образа — уникальным тегом (`latest` кэшируется зеркалом
|
||
`mirror.k8s.ngcloud.ru`); через `kubectl set image deployment/containerk8s app=...`
|
||
(правки через kubectl не откатываются — ownerReferences нет).
|
||
- Env vars — в deck-UI; Managed Redis — `redisk8s.<uuid>.svc.cluster.local:6379`.
|
||
- JWT-валидация: `https://lk-api-gateway.ngcloud.ru/api/v1/svc`
|
||
(deck-api*.ngcloud.ru — ЛЕГАСИ, не использовать).
|
||
- Long-running работает: SQS long-poll 20с; HTTP-соединение через ingress
|
||
держится ~150с (не режется).
|
||
- **WebSocket в SQS-истории не упоминается вообще** — поддержка wss на платформе
|
||
не проверялась. Единственный реальный риск контейнерного пути для IoT.
|
||
|
||
## 6. Вывод и следующий шаг
|
||
|
||
- Путь миграции IoT (кандидат): «Простой HTTP контейнер» в realm `iot-naeel`,
|
||
Go без переписывания; убрать operator/CRD → таблица `iot_devices` в Postgres;
|
||
bridge и consumer остаются Go.
|
||
- Первая проверка перед стартом: WebSocket upgrade (wss) через платформенный
|
||
ingress — тестовый контейнер с WS-эндпоинтом + `wscat`.
|
||
- ЖДЁМ решения пользователя по пути миграции.
|
||
|
||
---
|
||
|
||
## 7. Создан ws-probe — тестовый контейнер для проверки wss
|
||
|
||
**Команда пользователя:** «делай» (создать пробник + залить образ на Docker Hub;
|
||
для запуска в deck-UI нужен путь до образа).
|
||
|
||
**Что сделано** (все шаги):
|
||
1. Создан каталог `ws-probe/` в IoT-репозитории:
|
||
- `main.go` — Go echo-сервер на `gorilla/websocket`: `GET /health` (JSON
|
||
status+version), `GET /ws` (echo + ping каждые 20с, read-deadline 60с,
|
||
сбрасывается pong'ами — мёртвые соединения закрываются), `GET /` (HTML).
|
||
Без кредов, без внешних зависимостей.
|
||
- `Dockerfile` — multi-stage golang:1.25 → distroless/static:nonroot,
|
||
порт 8080 (меняется env `PORT`), версия через ldflags.
|
||
- `Makefile` — `docker-build`/`docker-push`, `VERSION=v0.1.0`, `IMAGE=naeel/iot-ws-probe`.
|
||
- `go.mod`/`go.sum` — отдельный модуль (gorilla/websocket v1.5.3), чтобы
|
||
не тащить зависимость в основной go.mod IoT.
|
||
2. `.gitignore` — добавлен `ws-probe/ws-probe` (сборочный бинарник).
|
||
3. Локальные проверки: `go vet` OK; smoke: `/health` → `{"status":"ok","version":"v0.1.0"}`;
|
||
WS-апгрейд → `HTTP/1.1 101 Switching Protocols` (Sec-WebSocket-Accept корректный).
|
||
4. Docker: логин в Docker Hub под `naeel` активен (проверен `docker info`).
|
||
5. Образ собран и запушен (публичный):
|
||
- **`naeel/iot-ws-probe:v0.1.0`** (и `:latest`),
|
||
- digest `sha256:dc029be3e79fb37f15ce23477096e340ab181b96c0132dc2e443f341e36f0688`,
|
||
- размер 10.9MB.
|
||
6. Коммиты: `27cb40e` (ws-probe), `2a7ecd2` (HISTORY). Пуш НЕ делался (не просили).
|
||
|
||
**Инструкция для deck-UI («Простой HTTP контейнер»):**
|
||
- Образ: **`naeel/iot-ws-probe:latest`** — стабильный путь БЕЗ уникального тега.
|
||
⛔ ПРАВИЛО (от пользователя): в deck-UI путь к образу фиксируется при создании
|
||
и НЕ редактируется — с тегом `:v0.1.0` мы не смогли бы поменять версию.
|
||
Обновление версии — новым пушем в `latest` + redeploy.
|
||
⚠️ Оговорка: зеркало платформы кэширует `latest` (случай shared-sqs) — если
|
||
redeploy подтянет старый digest, запасной путь: `kubectl set image
|
||
deployment/containerk8s app=naeel/iot-ws-probe:<новый-уникальный-тег>`.
|
||
- Env vars: не нужны (порт 8080 — дефолт; при необходимости `PORT`).
|
||
- Никаких кредов не требуется.
|
||
|
||
**Критерии проверки wss после деплоя** (будут применены):
|
||
- `curl -i -H "Connection: Upgrade" -H "Upgrade: websocket" ...` → 101;
|
||
- `wscat -c wss://<домен>/ws` → подключение, echo, соединение живёт 5–10 мин;
|
||
- переподключения стабильны; ping/pong проходят.
|
||
|
||
**Следующий шаг:** пользователь создаёт контейнер в deck-UI с этим образом —
|
||
далее тест wss по критериям выше.
|
||
|
||
---
|
||
|
||
## 8. Контейнер задеплоен в deck-UI — тест wss (16.08, ~07:26 GMT+03)
|
||
|
||
**Контейнер:** `https://testiot.containerk8s.dev.nubes.ru/` (создан пользователем
|
||
в deck-UI, образ `naeel/iot-ws-probe:latest`).
|
||
|
||
**Результаты проверок (первые):**
|
||
1. `GET /health` → `{"status":"ok","version":"v0.1.0"}` — под стянул наш образ.
|
||
2. `GET /` → HTML-страница ws-probe (200).
|
||
3. **WebSocket upgrade через платформенный ingress → `HTTP/1.1 101 Switching Protocols`**,
|
||
`Sec-WebSocket-Accept` корректный, HSTS на месте. (В выводе curl первая строка
|
||
`HTTP/1.1 200 Connection established` — локальный CONNECT-прокси машины,
|
||
ответ 101 пришёл от платформы.)
|
||
4. **`wscat -c wss://.../ws -x "probe-echo-1"` → echo вернулось, exit 0.**
|
||
Двусторонний обмен работает.
|
||
|
||
**КЛЮЧЕВОЙ ВЫВОД:** платформа Nubes («Простой HTTP контейнер») пропускает
|
||
WebSocket upgrade — wss для устройств возможен, путь контейнера подтверждён.
|
||
|
||
**Длительный тест стабильности** (запущен 08:26:10 по `date` = 07:26 GMT+03):
|
||
- скрипт `/tmp/ws-stability-test.py`: соединение 10 мин, echo каждые 10с,
|
||
ping/pong обе стороны (клиент ping_interval=25, сервер ping 20с),
|
||
лог с таймстемпами. Результат будет дописан ниже.
|