5.7 KiB
Проблема загрузки файлов: диагностика и решение
Дата: 2026-07-16
Версия приложения: v0.0.31
Кластер: iot-naeel (4 ноды, Cilium Geneve, kube-vip, Штурвал)
Проблема 1: 51-секундная задержка (MTU)
Суть
При передаче POST из внешней сети — пауза 51с на любом объёме данных. Внутри кластера — мгновенно.
Причина
Underlay MTU = 1450. Cilium Geneve добавляет 50 байт. Дефолтный MTU пода = 1500.
Cross-node пакет = 1500+50 = 1550 → фрагментация → дроп → ядро ждёт повторной передачи 51с.
Решение
cilium-config → mtu: 1400. 1400+50 = 1450 = underlay.
Задокументировано 2026-07-13, применено 2026-07-14.
Проблема 2: ERR_CONNECTION_RESET после фикса MTU
Суть
curl с ВМ работает. Браузер — ERR_CONNECTION_RESET или ⏳ 100%.
После rollout restart ingress — работает 5-10 минут, потом снова RST.
Ложные следы (опровергнуты)
| Гипотеза | Почему неверна |
|---|---|
| kube-vip ARP flapping | kube-vip.io/vipHost: control-plane — VIP жёстко на одной ноде |
| hostPort на 2 из 4 нод | Трафик только на control-plane, не на все 4 |
| upstream keepalive stale | upstream-keepalive-connections: 0 не помог |
| conntrack | ETPolicy Cluster не при чём — трафик на одну ноду |
| MTU | Решено в проблеме 1 |
| HTTP/2 | ERR_HTTP2_PROTOCOL_ERROR → отключили → ERR_CONNECTION_RESET — симптом тот же |
Истинная причина
keep-alive: 10 — Штурвал ставил nginx keep-alive: 10 (вместо дефолта 75).
Сценарий:
- Браузер грузит страницу — TCP открыт
- Пользователь выбирает файлы (>10с) — nginx закрывает idle соединение
- Браузер не замечает FIN (буферизация ОС)
- POST 63KB летит в закрытый сокет → TCP RST
Решение (применено в платформе Штурвала 2026-07-15)
Через ShturvalServiceConfig (не kubectl — ArgoCD откатывает):
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
Также в коде — fetch('/health') при загрузке страницы (прогрев upstream).
v0.0.30 — .doc в диалоге выбора файлов
Добавлен accept=".doc,.docx,.pdf,.txt,.zip" в <input type="file">.
doc_to_markdown() переписан: вместо soffice (LibreOffice на ВМ) — HTTP-вызов liberta.containerk8s.dev.nubes.ru/convert через httpx.
v0.0.31 — .doc обрабатывается обфускацией
.doc теперь не просто конвертируется, а проходит полную обфускацию (замена сущностей, mapping.csv). В архиве — .md с заменённым текстом, не исходный .doc.
Результаты тестов (v0.0.29, 2026-07-15)
| Тест | Результат |
|---|---|
| 10× POST 63KB (свежие страницы) | 10/10 OK:200 |
| UI: 5 файлов + SSE + ZIP + CSV | ✅ полный цикл |
| 5× POST через 75с (keep-alive окно) | 5/5 OK:200 |
| 5MB PDF через curl | ✅ 200 OK, 0.62с |
| ИТОГО | 25/25 успешно, 0 RST |
Итоговая конфигурация кластера
Ingress (через платформу Штурвала, несбрасываемо ArgoCD)
| Параметр | Дефолт nginx | Было (Штурвал) | Сейчас |
|---|---|---|---|
keep-alive |
75 | 10 | 75 |
proxy-body-size |
8m | 8m | 1024m |
client-body-timeout |
60 | 10 | 60 |
client-header-timeout |
60 | 10 | 30 |
use-http2 |
true | true | false |
upstream-keepalive-connections |
0 | 50 | 0 |
error-log-level |
info | info | debug (вернуть info) |
Cilium (через kubectl, не сбрасывается)
| Параметр | Дефолт | Сейчас |
|---|---|---|
mtu |
1500 | 1400 |
Приложение (drhider v0.0.29)
# site/app.py
VERSION = "0.0.29"
app.config["MAX_CONTENT_LENGTH"] = 200 * 1024 * 1024 # 200 MB
// site/templates/index.html — warmup при загрузке
window.addEventListener('load', () => {
sf = []; fi.value = ''; rr();
fetch('/health').catch(() => {}); // прогрев upstream
});
Документация
| Файл | Что |
|---|---|
docs/ARCHITECTURE.md |
Архитектура + требования к Flask |
docs/MIGRATION-GUIDE.md |
Инструкция переноса UI на Flask |
History/2026-07-14-browser-rst-debug.md |
Полная история диагностики RST |
History/2026-07-15-rst-root-cause.md |
Финальный диагноз |
History/2026-07-15-cluster-dump.md |
Дамп кластера |
STATE-2026-07-15.md |
Состояние проекта на 2026-07-15 |