From d8bdea75f3f3ad909cead797ddc3422076209390 Mon Sep 17 00:00:00 2001 From: Repinoid Date: Thu, 8 Oct 2026 16:51:45 +0300 Subject: [PATCH] =?UTF-8?q?doc:=20=D1=80=D0=B0=D0=B7=D0=B1=D0=BE=D1=80=20?= =?UTF-8?q?=D0=BF=D1=80=D0=B8=D0=BC=D0=B5=D0=BD=D0=B8=D0=BC=D0=BE=D1=81?= =?UTF-8?q?=D1=82=D0=B8=20=D0=BE=D0=B1=D1=85=D0=BE=D0=B4=D0=B0=20drhider?= =?UTF-8?q?=20=D0=BA=20SQS=20(=D0=BF=D1=80=D0=BE=D0=BA=D1=81=D0=B8=20?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=92=D0=9C,=20pull=20=D0=B2=D0=BC=D0=B5=D1=81?= =?UTF-8?q?=D1=82=D0=BE=20push)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - почему схему 1:1 скопировать нельзя (клиенты на AWS SDK, синхронный протокол) -> аналог: прозрачный прокси на ВМ; - условия: сохранение Host для SigV4 (auth_middleware.go:315-317), внешние QueueUrl (сейчас http://region.host:port из goaws.yaml, tenant_helpers.go:29-37), TLS, доступ ВМ->кластер, отключение буферизации; - 4 варианта с оценкой (прокси / настройки ingress / MTU / ретраи) и таблица 'что какой лечит'; - обязательные замеры до решения (раздел 5) и открытые вопросы. --- ...karound-applicability-to-sqs-2026-10-08.md | 138 ++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 doc/thinking/nubes-workaround-applicability-to-sqs-2026-10-08.md diff --git a/doc/thinking/nubes-workaround-applicability-to-sqs-2026-10-08.md b/doc/thinking/nubes-workaround-applicability-to-sqs-2026-10-08.md new file mode 100644 index 0000000..f470815 --- /dev/null +++ b/doc/thinking/nubes-workaround-applicability-to-sqs-2026-10-08.md @@ -0,0 +1,138 @@ +# Применимость обхода из drhider к shared-sqs (разбор) + +**Дата:** 2026-10-08 +**Основание:** `nubes-large-body-workaround-findings-2026-10-08.md` (находки из `~/nubes/drhider`). +**Статус:** анализ. **Ничего ещё не внедрено** — сначала замеры (см. раздел 5). + +--- + +## 1. Почему нельзя скопировать схему 1:1 + +В drhider обе стороны протокола — свои (браузер + своё приложение на Flask), +поэтому туда встроили двухшаговый протокол: `PUT` файла на ВМ-буфер → `POST` +ссылки в кластер → исходящий `GET` (pull). + +В shared-sqs **обе стороны чужие по протоколу**: клиенты — сторонние приложения +с AWS SDK (подпись SigV4), протокол SQS синхронный (запрос → ответ). Вставить +«положите тело на буфер, пришлите ссылку» нельзя без изменения клиентов и потери +совместимости с AWS SDK. + +→ Прямой аналог буфера в SQS — **прозрачный обратный прокси на ВМ**, а не буфер +и не хранилище. Данные идут потоком, протокол не меняется. + +## 2. Что именно болит у shared-sqs (наши факты) + +| Явление | Причина по нашим документам | Источник | +|---|---|---| +| A. Большие тела (>~1.4 КБ) виснут ~51 с | MSS, анонсируемый шлюзом = 1448; MTU пода 1400, underlay 1450 → сегменты 1488 дропаются при DF; PMTUD мёртв (ICMP не доходит) | `nubes-ticket.md:11-13` | +| B. Малые тела (512 Б) — `ReadTimeout` 30 с с периодом 31–33 с | таймер на ВНЕШНЕМ шлюзе; доказано tcpdump: запросы не доходят до пода, nginx чист, Cilium 0 дропов | `nubes-ticket.md` (явление B), HANDOFF | + +Что уже проверено ранее: +- **внутри кластера** — проблем нет; **с ВМ (5.172.178.213) p50 = 6–8 мс, таймаутов нет**; +- **MTU пода уже 1400** — то есть тот фикс, который помог drhider (1500→1400), у нас + **уже применён**, а явление A осталось: причина в MSS-анонсе шлюза, а это вне + нашего контроля (`nubes-ticket.md:16-17`); +- лимит SQS — тело ≤ 262144 Б (256 КБ), т.е. большие тела легальны и попадают + ровно в проблемную зону. + +## 3. Варианты, применимые к SQS + +### Вариант 1 (основной) — публичная точка входа на ВМ-прокси + +Схема: `клиент (AWS SDK) → HTTPS на ВМ → nginx (ВМ) → под shared-sqs в кластере`. + +Убираем из пути внешний шлюз платформы. Основания: +- путь «интернет → ВМ» в drhider держал тела 63 КБ и 5 МБ **успешно** + (`PROBLEM-AND-SOLUTION.md`), то есть у ВМ нет той патологии, что у шлюза кластера; +- путь «ВМ → кластер» у нас измерен: 6–8 мс, таймаутов нет. + +**Технические условия (важные, иначе не заработает):** + +1. **SigV4 подписывает заголовок `Host`.** nginx обязан передавать внешний хост: + `proxy_set_header Host $host;`. Сервис берёт хост именно из запроса — + `auth_middleware.go:315-317` (`canonicalHeaderValue`: `name == "host"` → `r.Host`). + Подменять Host на внутренний (например, на служебное имя сервиса) **нельзя** — + подпись перестанет сходиться, клиент получит `SignatureDoesNotMatch`. +2. **URL очередей сейчас жёстко внутренние и без TLS.** `tenantQueueURL` + (`tenant_helpers.go:29-37`) формирует `http://{region}.{host}:{port}/{tenantID}/{queue}`. + Хост и порт читаются **только из `goaws.yaml`** (`conf/config.go:87-90`; в + `applyEnvironmentDefaults` env-переменных для Host нет). Следствия: + - клиенты, которые сами задают `endpoint_url` (все наши тесты), берут из + `QueueUrl` только путь — они не пострадают; + - клиенты **без** `endpoint_url` пойдут на хост из `QueueUrl` → обязаны получить + внешний адрес. Значит: либо (а) подставляем внешний хост/схему в конфиг, либо + (б) nginx переписывает `QueueUrl` в ответах (хрупко), либо (в) требуем от + клиентов явный `endpoint_url`. +3. **Транспорт.** Схема `http://` в URL — при переносе на ВМ либо оставляем + `http` (нежелательно для интернета), либо переводим на `https` и правим + формирование URL. Для TLS нужен сертификат на ВМ-домен. +4. **Доступ ВМ → кластер.** ВМ вне кластера; понадобится NodePort/VIP (или + опубликованный внутренний адрес). Путь ВМ→кластер нужно замерить (см. раздел 5). +5. **Проксирование тел** — без буферизации на диск: `proxy_request_buffering off`, + `client_max_body_size` ≥ 256 КБ (+ запас). + +### Вариант 2 — настройки ingress через `ShturvalServiceConfig` + +В drhider этим вылечили RST (`keep-alive: 10` → `75`), подняли +`proxy-body-size: 1024m`, выключили HTTP/2, подняли `client-body-timeout: 60`. +Правки делаются **только через `ShturvalServiceConfig`** — kubectl откатывает ArgoCD +(`PROBLEM-AND-SOLUTION.md`). + +Оценка для нас: **скорее всего не поможет**. Наш таймер (явление B) доказанно +находится **вне кластера** (tcpdump: запросы не доходят до пода; nginx чист), а +MSS-анонс делает **внешний шлюз**. Но правка дешёвая и безопасная → имеет смысл +проверить замером «до/после», прежде чем строить прокси. + +### Вариант 3 — MTU-фикс + +**Уже сделан** (MTU пода 1400). Явление A не убрал — так как причина в шлюзе. +Для SQS этот путь закрыт. + +### Вариант 4 — клиентские ретраи (+ идемпотентность) + +Маскирует явление B, **не решает** явление A (51 с ожидания при большом теле). +Для standard-очередей ретрай без идемпотентности даёт дубли (FIFO-дедуп по +`MessageDeduplicationId` у нас есть, для standard — нет). Годится как временная +мера, не как решение. + +## 4. Сводка: что какой вариант лечит + +| Вариант | Явление A (большие тела) | Явление B (таймер) | Стоимость/риск | +|---|---|---|---| +| 1. Прокси на ВМ | да (по аналогии с drhider) | да (шлюз вне пути) | TLS, Host, внешние URL, доступ ВМ→кластер | +| 2. Настройки ingress | вероятно нет | вероятно нет | низкая; нужен доступ к платформенному конфигу | +| 3. MTU | нет (уже 1400) | нет | — | +| 4. Ретраи клиентов | нет | частично | дубли для standard | + +## 5. Что обязательно замерить ДО решения + +Правило проекта: рекомендация без замера — догадка. Замеры на том же хосте и тем +же инструментом: + +1. **С ВМ → внешний endpoint кластера**: тела 1 КБ / 10 КБ / 100 КБ / 256 КБ — + есть ли ~51 с? (Малые тела с ВМ проверены: 6–8 мс, таймаутов нет; большие, судя + по всему, отдельно не мерили.) +2. **ВМ → кластер (NodePort/VIP)**: те же размеры — сколько занимает. +3. **Явление B с ВМ**: сохраняется ли период 31–33 с. +4. **После варианта 2** (если включаем): те же замеры, сравнение «до/после». + +Инструменты уже есть в репозитории: `tests/tcp_mtu_probe.py`, +`tests/long_compare_local.py`, `tests/compare_ymq_vs_shared.py`, +`tests/platform_probe.py`, `tests/reboot_probe.py`. + +## 6. Вывод + +- Тикет в поддержку — **не решение** (болезнь платформы), оставляем только как + историю; в drhider её тоже не «починили», а обошли. +- Архитектурный аналог решения drhider для SQS — **прозрачный прокси на ВМ** + (вариант 1). Ключевые камни: сохранение `Host` для SigV4, внешние `QueueUrl` + (сейчас `http://{region}.{host}:{port}` из `goaws.yaml`), TLS, доступ ВМ→кластер. +- Перед стройкой — замеры из раздела 5; возможно, вариант 2 закроет часть проблемы + за 10 минут вместо новой инфраструктуры. + +## 7. Открытые вопросы (нужны твои решения) + +1. Внешний домен и сертификат для ВМ-прокси — какой брать? +2. Клиенты без явного `endpoint_url` — есть ли такие (иначе вопрос №3 в разделе 3 + отпадает)? +3. Делать ли сначала замеры (раздел 5) — я готов.