Files
SQS-service/doc/thinking/nubes-large-body-workaround-findings-2026-10-08.md
T
Repinoid a1d72b61d4 doc: находки — как та же сетевая болезнь платформы обойдена в drhider (буфер на ВМ, pull вместо push)
Собраны и проверены по источникам все факты из ~/nubes/drhider:
- диагноз: ingress рвёт входящие тела >64КБ, egress не ограничен;
- схема пофайлового транзита через ВМ-буфер + egress-GET (pull);
- применённые фиксы платформы (cilium mtu:1400, keep-alive 10->75,
  proxy-body-size 1024m, HTTP/2 off) через ShturvalServiceConfig;
- ложные следы, лимиты, SSRF/path-traversal защиты, тесты 25/25;
- вывод: тикет в поддержку — не решение, нужен архитектурный обход.
2026-10-08 16:50:36 +03:00

12 KiB
Raw Blame History

Находки: как та же сетевая болезнь платформы уже решена в drhider (буфер на ВМ)

Дата: 2026-10-08 Источник находок: репозиторий ~/nubes/drhider (+ ~/nubes/drhider/upload-platform) Зачем: у shared-sqs та же проблема (таймауты внешнего пути, большие тела), и наивный путь «отправить тикет в Nubes» оказался тупиком — явление признано болезнью платформы. В drhider ту же болезнь обошли архитектурно, а не починили.

⚠️ ГЛАВНЫЙ ВЫВОД

Ingress managed-кластера обрывает ВХОДЯЩИЕ тела больше порога, а ИСХОДЯЩИЙ трафик (egress) из кластера не ограничен. Отсюда рабочий приём: не «толкать» большие данные в кластер (push), а «вытягивать» их извне (pull) — данные кладутся на внешний хост-буфер, а приложение в кластере забирает их исходящим запросом. Тикет в поддержку проблему не решает.


1. Диагноз проблемы (цитаты из drhider)

upload-platform/HISTORY/2026-09-06-layer2-architecture-and-api-boundaries.md:18:

«Причина существования: Ingress-контроллер k8s-кластера (pythonk8s) обрывает входящие запросы с телом более 64 КБ. Прямой POST больших файлов в кластер невозможен; исходящий трафик (egress) из кластера не ограничен.»

PLAN-upload-layers.md:27 (контекст, «что уже проверено»):

«Паттерн загрузки (шлюз managed-кластера рвёт тела >64КБ, egress не ограничен): браузер → PUT на ВМ-буфер → Flask POST /api/upload_refs → egress GET (pull) → сессия.»

Это ровно тот же класс, что явление A у shared-sqs (большие POST), только у drhider порог «тело >64 КБ» на ingress, а у SQS — MSS/MTU-порог ~1.4 КБ на внешнем шлюзе (doc/thinking/nubes-ticket.md, явление A). Причина на стороне платформы в обоих случаях.

2. Схема обхода, применённая в drhider (пошагово)

upload-platform/HISTORY/2026-09-06-layer2-architecture-and-api-boundaries.md:19-27 — «Пофайловый транзит (Per-File Transit)»:

  1. Файл №k браузер отправляет методом PUT в буфер на ВМ (вне кластера).
  2. Браузер сразу шлёт в приложение (Flask в кластере) POST /api/upload_refs — тело запроса маленькое (только ссылка на файл), поэтому ingress его пропускает.
  3. Приложение само забирает файл исходящим потоковым GET прямо в память сессии — egress не режется.
  4. Приложение немедленно отправляет DELETE на ВМ — буфер очищен.
  5. Браузер переходит к файлу №k+1. В буфере на ВМ в любой момент максимум один файл.

Дополнительные принципы оттуда же:

  • RAM-only: без постоянной записи на диск ни в кластере, ни на ВМ.
  • Zero hardcode: адрес буфера и префикс — параметры конфигурации (vmUploadUrl на фронте, vmUploadPrefix на бэке для обязательной SSRF-проверки).
  • Автономный mock-буфер (in-memory WebDAV) для тестов без внешней сети.

upload/README.md:13 подтверждает ту же механику:

«Пофайловый стриминг: для каждого файла выполняется PUT в буфер на ВМ, немедленный POST /api/upload_refs в бэкенд, и после подтверждения приёма буфер на ВМ очищается.»

3. Что нельзя потерять при переносе (накоплено в drhider)

PLAN-upload-layers.md (раздел «Требования/что НЕЛЬЗЯ потерять»):

  • дедупликация «имя+размер» (иначе суффикс _2);
  • раскрытие ZIP с путями и фильтр расширений (.pdf .doc .docx .txt .md);
  • рекурсивный обход папок (webkitdirectory);
  • SSRF-валидация URL (url.startswith(VM_UPLOAD_PREFIX));
  • _safe_name — защита от path traversal с сохранением подпапок;
  • повторы pull (3 попытки, пауза 2 с);
  • лимиты 50 МБ на файл / 500 МБ на сессию;
  • различение «сессия не найдена» и «превышен лимит»;
  • статусы «пропущен (лимит)» / «не извлечён»;
  • экранирование имён для защиты от self-XSS.

4. Фиксы платформы, которые уже применили (и их границы)

PROBLEM-AND-SOLUTION.md (drhider, v0.0.31, 2026-07-16):

Проблема 1 — пауза 51 с (MTU). Underlay MTU 1450, Cilium Geneve добавляет 50 байт, дефолт пода 1500 → пакет 1550 → фрагментация → дроп → ядро ждёт повторной передачи 51 с. Решение: cilium-config → mtu: 1400 (1400+50 = 1450). Задокументировано 2026-07-13, применено 2026-07-14.

Проблема 2 — ERR_CONNECTION_RESET в браузере. Истинная причина — keep-alive: 10 вместо дефолта 75: пока пользователь выбирал файлы (>10 с), nginx закрывал idle-соединение, браузер не замечал FIN и слал POST в закрытый сокет → RST. Решение (в платформе Штурвала, 2026-07-15) — через ShturvalServiceConfig, а не через kubectl:

controller:
  config:
    keep-alive: "75"
    proxy-body-size: "1024m"
    client-body-timeout: "60"
    client-header-timeout: "30"
  hostPort:
    enabled: true
  replicaCount: 2
  service:
    type: LoadBalancer

Важно: правки ingress на платформе делаются через ShturvalServiceConfig — через kubectl нельзя, ArgoCD откатывает.

Ложные следы, которые опровергли (та же таблица): kube-vip ARP flapping, hostPort на 2 из 4 нод, upstream keepalive, conntrack, MTU (для проблемы 2), HTTP/2.

Результаты после фиксов (v0.0.29, 2026-07-15): 10× POST 63 КБ — 10/10 OK; полный UI-цикл (5 файлов + SSE + ZIP + CSV) — OK; 5× POST с интервалом 75 с — 5/5 OK; PDF 5 МБ через curl — 200 OK за 0.62 с. Итого 25/25, 0 RST.

⚠️ Но это вылечило ingress-таймер и MTU-фрагментацию, а не порог размера тела: обходной слой через ВМ-буфер остался нужен и является рабочим решением.

5. Обходы «не по сети» — тот же принцип pull/вынос

PROBLEM-AND-SOLUTION.md:71: doc_to_markdown() переписали — вместо локального soffice (LibreOffice на ВМ, тяжёлый и не тянул) HTTP-вызов внешнего сервиса liberta.containerk8s.dev.nubes.ru/convert через httpx. То есть тяжёлую/медленную работу тоже вынесли наружу и ходят к ней по HTTP.

Смежное (из кода drhider): конвертация .doc через liberta выполняется последовательно (_LLM_CONCURRENCY = 1) — сервис не выдерживает конкурентность (drhider/extractor.py:160-194, drhider/scanner.py:122).

6. Готовый переиспользуемый модуль (для справки)

PLAN-upload-layers.md — слои выделены так, чтобы их можно было перенести в любой проект копированием (без общей репы):

Слой Где Ответственность
0. Конфиг общий адрес ВМ-буфера, расширения, лимиты
1. Выбор файлов фронт таблица, дедуп, ZIP, дерево папок, статусы
2. Закачка через ВМ фронт + бэк PUT на ВМ → upload_refs pull → сессия
3. Логика приложения бэк обработка (у каждого своя) — не выносится

Реализация-эталон: ~/nubes/drhider/upload-platform/ (Слой 1 v0.1.13 готов, Слой 2 в работе на 2026-09-06), а также рабочий образец ~/nubes/drhider/upload/.

7. Инфраструктурные факты

  • ВМ-буфер: 5.172.178.213 (вне кластера). Nginx WebDAV, каталоги /var/www/drhider-upload/, /var/www/contracts-upload/, cron-очистка каждые 5 минут (find … -mmin +30 -delete). Для целевого прода — перевод в RAM/in-memory буфер.
  • SSH к ВМ: ssh -i ~/.ssh/naeel_vm_id_ed25519 naeel@5.172.178.213 (README.md:141).
  • Кластеры/неймспейсы: pythonk8s (ingress режет >64 КБ); сервисы drhider, contractor, uploader-dev.

8. Выводы (что берём из чужого опыта)

  1. Тикет в Nubes — не решение. Это болезнь платформы; в drhider её не «починили», а обошли. Тикет (doc/thinking/nubes-ticket.md) имеет смысл только как фиксация для истории, не как способ получить рабочий сервис.
  2. Рабочий приём — pull вместо push: внешний буфер + исходящий GET из кластера. Egress свободен, ingress режется.
  3. Правки ingress на платформе — только через ShturvalServiceConfig (kubectl откатывается ArgoCD).
  4. MTU-фикс (cilium mtu: 1400) нужен как гигиена, но порога больших тел не снимает.
  5. Тяжёлые/медленные операции тоже выносятся наружу и вызываются по HTTP (пример: liberta).

Источники (файл:строка)

Источник Что
~/nubes/drhider/PROBLEM-AND-SOLUTION.md MTU-фикс, keep-alive 10→75, ложные следы, тесты 25/25
~/nubes/drhider/PLAN-upload-layers.md:27 паттерн «шлюз рвёт >64 КБ, egress свободен»
~/nubes/drhider/upload/README.md:13 пофайловый стриминг через ВМ-буфер
~/nubes/drhider/upload-platform/HISTORY/2026-09-06-layer2-architecture-and-api-boundaries.md:18-27 диагноз + пошаговый пофайловый транзит, RAM-only, mock-буфер
~/nubes/drhider/docs/MSS-CLAMPING-PLAN.md, docs/about1500.md, docs/INGRESS-SOLUTIONS.md сопутствующие материалы по MTU/MSS/ingress
~/nubes/drhider/drhider/extractor.py:160-194, drhider/scanner.py:122 вынос конвертации в сервис liberta, последовательные вызовы