Files
SQS-service/doc/2026-08-13-architecture-and-migration-plan.md
T

15 KiB
Raw Blame History

Архитектура и план миграции shared-sqs → Nubes Managed

Дата: 2026-08-13 Источник: ответ Claude Sonnet на промпт по миграции


1. Код-ревью (блокеры деплоя)

Файл Находка Блокер? Что делать
goaws.yaml + tenant_helpers.go Host: goaws.com, Port: 4100 bake-in в образ. QueueUrl клиентам возвращается как http://us-east-1.goaws.com:4100/{tenantID}/{queueName}. Поскольку текущий k8s работает с тем же расхождением (ingress qu.kube5s.ru ≠ goaws.com), клиенты используют endpoint_url + только path из QueueUrl — не подключаются к goaws.com напрямую. НЕТ (условно) Обновить Host в goaws.yaml после выяснения нового домена, пересобрать образ. Это смена конфига, не Go-кода.
redis.go Подключение без TLS: redis.Options{Addr, Username, Password, DB:0} — нет TLSConfig. Если Managed Redis требует TLS на internal DNS — блокер. ОТКРЫТЫЙ ВОПРОС Уточнить у Nubes: требует ли managed Redis TLS для *.svc.cluster.local. Если нет — не блокер.
goaws.go Порт задаётся флагом --port (default 4100), не env PORT. ENTRYPOINT в Dockerfile не передаёт --port — слушает 4100. НЕТ nubes_http должен маршрутизировать на порт 4100 (или платформа сама определяет порт — уточнить).
goaws.go ReceiveMessage с long-polling держит HTTP-соединение до 20 секунд (WaitTimeSeconds). В Ingress был proxy-read-timeout: 30. НЕТ (в коде) nubes_http должен иметь read-timeout ≥ 30 секунд — уточнить у платформы.
admin.go NUBES_ENDPOINT default = https://deck-api-test.ngcloud.ru/api/v1 (тестовый). Для production нужен production URL. НЕТ (env-override) Задать NUBES_ENDPOINT production URL в env vars контейнера.
deployment.yaml SHARED_SQS_SEED_DEMO=true — сидирует демо-данные при каждом старте. НЕТ Не задавать или =false в production деплое.
Dockerfile imagePullSecrets: sless-registry-auth (k8s). Образ naeel/shared-sqs на Docker Hub — нужно проверить публичность. НЕТ (вероятно) Если образ публичный — секрет не нужен. Уточнить, как nubes_http указывает registry.

Вывод: жёстких code-level блокеров нет. Код менять не нужно. Единственное изменение при известном домене — пересборка образа с обновлённым goaws.yaml (поле Host).


2. Целевая архитектура

Схема

graph TD
    Client["AWS SDK/CLI<br/>boto3 / aws-sdk-go<br/>(endpoint_url = новый домен)"]
    LB["Nubes HTTP Gateway<br/>TLS termination<br/>timeout ≥ 30s"]
    App["nubes_http<br/>naeel/shared-sqs:v0.1.x<br/>port 4100<br/>RAM 64–256Mi / CPU 50–500m"]
    Redis["Managed Redis<br/>redisk8s.4ff5678b-b683-4aef-91da-22d89dec8a25<br/>.svc.cluster.local:6379<br/>512MB / 10GB"]
    PG["Managed PostgreSQL<br/>(опционально, billing)"]
    NubesAPI["Nubes API<br/>deck-api-*.ngcloud.ru<br/>JWT-валидация UI-сессий"]
    VM["VictoriaMetrics<br/>scrape /metrics"]

    Client -->|"HTTPS :443"| LB
    LB -->|"HTTP :4100<br/>internal"| App
    App -->|"TCP :6379<br/>internal DNS<br/>без TLS"| Redis
    App -.->|"TCP :5432<br/>BILLING_PG_HOST"| PG
    App -->|"HTTPS<br/>JWT validate"| NubesAPI
    VM -->|"GET /metrics"| App

Компоненты

Компонент Ресурс Nubes Описание
SQS-сервис nubes_http Образ naeel/shared-sqs:v0.1.x, порт 4100, stateless
Persistence Managed Redis 4ff5678b-... Ключи ssq:tenants, ssq:queues, ssq:msg:{key}
Billing (opt.) Managed PostgreSQL Таблица sqs_usage_records, auto-migrate при старте
JWT auth Nubes API (внешний) Только для UI-консоли, не для SQS API

Переменные окружения для nubes_http

SHARED_SQS_ADMIN_TOKEN=<секрет>
REDIS_ADDR=redisk8s.4ff5678b-b683-4aef-91da-22d89dec8a25.svc.cluster.local:6379
REDIS_USER=default
REDIS_PASSWORD=ZJOke5b2bIr6YPOKrnJG
NUBES_ENDPOINT=https://deck-api.ngcloud.ru/api/v1   ← production URL (уточнить)
SHARED_SQS_SEED_DEMO=false

# Опционально — billing:
BILLING_PG_HOST=...
BILLING_PG_PORT=5432
BILLING_PG_DATABASE=...
BILLING_PG_USER=...
BILLING_PG_PASSWORD=...
BILLING_PG_SSLMODE=require

Ключевые инварианты архитектуры

  • Один процесс, stateless: всё состояние в Redis. Рестарт контейнера восстанавливает тенантов и очереди через LoadAllTenantsRaw + LoadAllQueues.
  • Фоновые горутины: PeriodicTasks (visibility timeout, DLQ, dedup) и StartGaugeUpdater запускаются в рамках процесса — не требуют отдельных воркеров.
  • Long-polling: до 20 секунд на соединение — платформа должна держать коннект открытым.
  • Multi-tenancy: изоляция по ключу {accessKey}:{queueName} в Redis и in-memory; тенанты хранятся в ssq:tenants HASH.

3. План миграции

Фаза 0 — Ответы на открытые вопросы (блокирует всё остальное)

0.1. Уточнить у команды Nubes: порт nubes_http (видит ли платформа EXPOSE 4100 или нужно явно указывать), DNS-видимость *.svc.cluster.local, TLS на Redis. Критерий приёмки: письменный ответ по всем 4 пунктам открытых вопросов.

0.2. Проверить доступность Managed Redis из realm iot-naeel. Команда (запустить временный pod или контейнер в том же realm):

redis-cli -h redisk8s.4ff5678b-b683-4aef-91da-22d89dec8a25.svc.cluster.local \
  -p 6379 -u default -a ZJOke5b2bIr6YPOKrnJG ping

Критерий: ответ PONG.


Фаза 1 — Подготовка (без downtime, k8s работает)

1.1. Решить судьбу данных: мигрировать из k8s Redis или начать с чистого листа.

  • Вариант А (clean start): нулевое состояние. Требует пересоздания тенантов через Admin API и уведомления клиентов о новых ключах. Риск: потеря очередей с сообщениями.
  • Вариант Б (миграция): экспорт ssq:tenants, ssq:queues, ssq:msg:* из k8s Redis → импорт в Managed Redis. Сохраняет всё состояние, но требует краткого maintenance window. Критерий: явное решение согласовано.

1.2 (если Вариант Б). Экспортировать ключи из k8s Redis:

# Получить все ключи ssq:*
kubectl exec -n shared-sqs deploy/shared-sqs-redis -- \
  redis-cli -a <пароль> --scan --pattern "ssq:*" | \
  xargs -I{} kubectl exec -n shared-sqs deploy/shared-sqs-redis -- \
  redis-cli -a <пароль> DUMP {}

Либо через redis-cli --rdb /tmp/dump.rdb, затем передать в Managed Redis через RESTORE. Критерий: все ключи ssq:tenants, ssq:queues, ssq:msg:* присутствуют в Managed Redis (проверить HLEN ssq:tenants, HLEN ssq:queues).

1.3. Обновить goaws.yaml с новым Host (финальный домен из ответа на вопрос 0.1.4), пересобрать и запушить образ:

docker build -t naeel/shared-sqs:v0.1.23 .
docker push naeel/shared-sqs:v0.1.23

Критерий: образ доступен в registry. Откат: не нужен — k8s ещё работает на v0.1.22.


Фаза 2 — Деплой nubes_http (параллельно с k8s)

2.1. Создать ресурс nubes_http в realm iot-naeel:

  • Образ: naeel/shared-sqs:v0.1.23
  • Порт: 4100 (или как определит платформа)
  • Ресурсы: min 64Mi/50m, max 256Mi/500m
  • Env vars: полный список из раздела архитектуры выше
  • Health check: GET /health (уже в Dockerfile)

Критерий: контейнер запустился, /health отвечает 200 OK. Откат: удалить ресурс nubes_http, трафик остаётся в k8s.

2.2. Smoke test нового инстанса (напрямую по URL nubes_http, до переключения трафика):

# Health
curl -m 5 https://new-domain.nubes.example/health

# Создать тенанта через Admin API
curl -m 5 -X POST https://new-domain.nubes.example/admin/tenants \
  -H "Authorization: Bearer <ADMIN_TOKEN>" \
  -d '{"name":"smoke-test","maxQueues":5}'

# Получить AccessKey/SecretKey из ответа, создать очередь
aws sqs create-queue --queue-name test-q \
  --endpoint-url https://new-domain.nubes.example \
  --region us-east-1

# Отправить и получить сообщение
aws sqs send-message --queue-url ... --message-body "hello"
aws sqs receive-message --queue-url ...

Критерий: все 3 операции успешны. Prometheus /metrics содержит метрики sqs_.* Откат: исправить env vars, пересобрать образ.

2.3 (если Вариант Б — миграция данных). Проверить восстановление:

# Запустить контейнер → смотреть логи старта
# Должны увидеть: "persistence: подключились к Redis", "Ошибка загрузки тенантов" НЕ должна быть
# Проверить тенантов через Admin API
curl https://new-domain.nubes.example/admin/tenants -H "Authorization: Bearer ..."

Критерий: в ответе Admin API видны все тенанты, перенесённые из k8s Redis.


Фаза 3 — Переключение трафика

3.1. Договориться с клиентами о maintenance window (только для Варианта Б, чтобы не было записей во время переноса данных). Для Варианта А — без окна.

3.2. Обновить конфигурацию клиентов: заменить endpoint_url c qu.kube5s.ru (или текущего) на новый домен Nubes. Домен qu.kube5s.ru — не использовать.

3.3. Мониторить в течение N минут/часов:

  • /metrics → Prometheus: sqs_messages_sent_total, sqs_messages_received_total
  • Ошибки в логах контейнера (log.Error)
  • Latency Redis (таймауты в логах persistence: HSet)

Критерий: ошибок в логах нет, метрики растут, клиенты работают. Откат: перевести клиентов обратно на k8s endpoint (он ещё жив на этом этапе).


Фаза 4 — Вывод k8s (только после подтверждённой стабильности)

4.1. После N дней стабильной работы: удалить k8s ресурсы shared-sqs (Deployment, Service, Ingress, HPA если есть).

4.2. Удалить k8s Redis (Deployment shared-sqs-redis, PVC shared-sqs-redis-pvc). Необратимо — делать только после полной проверки данных в Managed Redis.

4.3. Удалить k8s Secrets (shared-sqs-admin, shared-sqs-redis). Предварительно убедиться, что все значения есть в nubes_http env или Nubes Secrets.

Критерий: namespace shared-sqs в k8s пуст или удалён. Сервис работает только через nubes_http.


4. Открытые вопросы и риски

# Приоритет Вопрос Последствие
1 КРИТИЧНО Какой порт принимает nubes_http? Читает ли платформа EXPOSE 4100 или нужно явно задать в конфиге ресурса? Если порт не 4100 — нужен флаг --port N в ENTRYPOINT или env PORT
2 КРИТИЧНО Видит ли контейнер nubes_http DNS *.svc.cluster.local в realm iot-naeel? Если нет — REDIS_ADDR с internal hostname не сработает, нужен другой адрес Redis
3 КРИТИЧНО Требует ли Managed Redis TLS для internal соединений? Если да — код redis.Options без TLSConfig — блокер, нужна правка (минимальная: добавить TLSConfig: &tls.Config{})
4 ВЫСОКИЙ Какой внешний URL/домен получит nubes_http ресурс? Без домена нельзя обновить goaws.yaml → QueueUrl клиентам будет содержать goaws.com (косметика, но может сломать специфичные клиенты)
5 ВЫСОКИЙ Docker Hub образ naeel/shared-sqs публичный или приватный? Как nubes_http указывает registry credentials? Если приватный — нужна конфигурация pull secret на уровне платформы
6 ВЫСОКИЙ Поддерживает ли nubes_http long-lived HTTP соединения до 20 секунд? (long-polling SQS) Если платформа имеет timeout < 20s — клиенты с WaitTimeSeconds>0 будут получать ошибки
7 СРЕДНИЙ Мигрировать данные (тенанты, очереди, сообщения) из k8s Redis или начать чисто? Clean start: нет риска, но потеря данных. Миграция: сложнее, требует maintenance window
8 НИЗКИЙ Какой production URL для NUBES_ENDPOINT? Текущий default deck-api-test.ngcloud.ru — тестовый UI JWT-авторизация сломается на production если endpoint не обновить
9 НИЗКИЙ SHARED_SQS_SEED_DEMO=true в k8s — нужен ли демо-режим в production nubes_http? Если нет — просто не задавать env. Демо-тенант создаётся при каждом старте