98 lines
4.4 KiB
Markdown
98 lines
4.4 KiB
Markdown
# 2026-07-10 — ИТОГ РАССЛЕДОВАНИЯ: почему режется загрузка
|
||
|
||
## Методология
|
||
Тестирование от внутреннего к внешнему — послойная изоляция каждого компонента.
|
||
|
||
## Результаты тестов
|
||
|
||
| # | Тест | Маршрут | Размер | Результат |
|
||
|---|------|---------|--------|-----------|
|
||
| 1 | Python внутри пода → Flask | pod → localhost:5000 | 100KB base64 (136KB POST) | ✅ 102400 байт, 4ms |
|
||
| 2 | Python внутри пода → Ingress HTTPS | pod → 10.105.13.158:443 | 136KB POST | ✅ 102400 байт, 2ms |
|
||
| 3 | Python снаружи → Ingress | интернет → 185.247.187.151:443 | 136KB POST | ❌ **СЛУЧАЙНО** |
|
||
| 4 | 10×100KB снаружи | интернет → 185.247.187.151:443 | 136KB POST | ~50% OK, ~50% RemoteDisconnected |
|
||
|
||
## Бинарный поиск предела (снаружи)
|
||
|
||
| Размер POST | Результат |
|
||
|-------------|-----------|
|
||
| 1-48 KB | ✅ всегда |
|
||
| 50 KB | ❌ |
|
||
| 52 KB | ✅ |
|
||
| 55-80 KB | ❌ |
|
||
| 100 KB | ✅ (иногда!) |
|
||
|
||
**Твёрдого предела НЕТ.** Сбои случайные.
|
||
|
||
## Исключённые причины
|
||
|
||
| Компонент | Статус | Доказательство |
|
||
|-----------|--------|----------------|
|
||
| Flask dev-сервер | ✅ OK | Тест #1 |
|
||
| nginx ingress | ✅ OK | Тест #2 |
|
||
| ModSecurity (128KB NoFilesLimit) | ✅ OK | Тест #2, DetectionOnly |
|
||
| Gunicorn field_size | ❌ не используется | Shturval запускает `python app.py` |
|
||
|
||
## Где проблема
|
||
|
||
```
|
||
Клиент (интернет)
|
||
│
|
||
▼
|
||
??? Edge-инфраструктура хостера (NUBES LLC, AS212136) ???
|
||
│ ← здесь рвётся TCP-соединение для POST > ~48KB
|
||
▼
|
||
185.247.187.151:443 (hostPort на ноде)
|
||
│
|
||
▼
|
||
nginx ingress pod ─── ModSecurity ─── pythonk8s pod (Flask)
|
||
✅ всё работает
|
||
```
|
||
|
||
IP в логах ingress: `192.168.255.x` — приватный IP хостинг-провайдера.
|
||
На ноде порт 443 слушает kube-vip/hostPort напрямую (без proxy_protocol).
|
||
Внутри кластера всё идеально. Снаружи — случайные обрывы.
|
||
|
||
## Аналогичный опыт (contracts)
|
||
`/home/naeel/nubes/contracts/History/`:
|
||
- `connection-reset-analysis.md`: curl 100KB → HTTP 000 случайно (5 тестов: 2 OK, 3 обрыв)
|
||
- `sonnet-upload-from-scratch.md`: "64KB — жёсткий предел платформы" (43 версии)
|
||
- `session-08-upload-saga.md`: чанки 50KB — только первый доходит
|
||
|
||
## Ложные правки (эта сессия)
|
||
- Dockerfile `--limit-request-field_size 0` — бесполезно (Gunicorn не используется)
|
||
- Версия 1.0.4 → 1.0.5 — задеплоено, но проблемы не решает (причина вне кода)
|
||
|
||
## Компоненты инфраструктуры (найдены)
|
||
- Shturval v2.12.1
|
||
- kube-vip (DaemonSet) — VIP для LoadBalancer, IP 185.247.187.151
|
||
- nginx ingress через hostPort (порты 80/443 прямо на ноде)
|
||
- ModSecurity OWASP CRS 4.10.0, SecRuleEngine DetectionOnly
|
||
- `SecRequestBodyNoFilesLimit 131072` (128KB) — НЕ причина (внутри кластера работает)
|
||
|
||
## РЕШЕНИЕ
|
||
|
||
Contracts на этой же ВМ работает потому, что использует **родной nginx** с `proxy_request_buffering off`:
|
||
|
||
```nginx
|
||
# /etc/nginx/sites-enabled/nginx-contracts.conf
|
||
location /upload {
|
||
proxy_pass http://127.0.0.1:8766;
|
||
client_max_body_size 100m;
|
||
proxy_request_buffering off; # ← вот почему работает
|
||
}
|
||
```
|
||
|
||
Для loadtest добавлена аннотация в k8s ingress:
|
||
```
|
||
nginx.ingress.kubernetes.io/proxy-request-buffering: "off"
|
||
```
|
||
|
||
Конфиг применился (подтверждено в `/etc/nginx/nginx.conf` внутри ingress pod):
|
||
```
|
||
proxy_request_buffering off;
|
||
```
|
||
|
||
## Причина
|
||
Edge-инфраструктура хостера (NUBES LLC) рвёт TCP-соединения при буферизации больших POST-тел. `proxy_request_buffering off` стримит тело напрямую в бэкенд — edge не рвёт.
|