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; - вывод: тикет в поддержку — не решение, нужен архитектурный обход.
This commit is contained in:
@@ -0,0 +1,176 @@
|
|||||||
|
# Находки: как та же сетевая болезнь платформы уже решена в 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:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
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`, последовательные вызовы |
|
||||||
Reference in New Issue
Block a user