Files
drhider/PROBLEM-AND-SOLUTION.md
T

7.8 KiB
Raw Blame History

Проблема загрузки файлов: диагностика и решение

Дата: 2026-07-14


Суть проблемы

При передаче данных через HTTP POST из внешней сети на managed-сервисы кластера (Flask и другие) периодически возникала константная задержка 51 секунда. Проблема проявлялась на любом объёме данных — даже на 100 КБ. Внутри кластера (pod → ingress pod напрямую) передача работала мгновенно. Задержка возникала исключительно при прохождении трафика снаружи через VIP (kube-vip), который распределяет запросы по всем нодам кластера.


Причина

Несоответствие MTU: канал между серверами — 1450 байт, а каждый Geneve-туннель добавляет к пакету 50 байт. При дефолтном MTU пода в 1500 байт любой cross-node-переход через туннель создаёт пакет размером 1550 байт, который не помещается в канал и дропается. Ядро ждёт повторной передачи — отсюда и 51-секундная пауза.

В зависимости от того, на каких нодах окажутся поды, запрос проходит через 0, 1 или 2 Geneve-туннеля:

Путь Туннелей Размер пакета Проходит?
Клиент → нода с ingress → drhider на той же ноде 0 1500 б
Клиент → нода с ingress → drhider на другой ноде 1 1500 + 50 = 1550 б
Клиент → нода без ingress → ingress → drhider 2 1500 + 50 + 50 = 1600 б

Снижение MTU пода до 1400 байт устраняет проблему: 1400 + 50 = 1450, что точно вписывается в канал на каждом переходе.


Архитектура кластера

Кластер iot-naeel: 4 ноды (3 worker + 1 control-plane), Cilium с Geneve-энкапсуляцией, kube-vip.

Ноды:      6f74n  |  bhbvs  |  v8zq4  |  control-plane
Ingress:   2 пода (Штурвал, всегда на разных нодах)
Drhider:   1 под  (размещается на случайной worker-ноде)
VIP:       185.247.187.151 → kube-vip анонсирует на все 4 ноды через ARP

Маршрут запроса: браузер → VIP (случайная нода) → kube-proxy → ingress-под → kube-proxy → drhider-под. Каждый переход между нодами — Geneve-туннель (+50 байт).

Примеры возможных размещений:

Ситуация Drhider Ingress Туннелей ingress→drhider
A 6f74n 6f74n + bhbvs 50% без туннеля, 50% через
B bhbvs 6f74n + v8zq4 100% через туннель
C v8zq4 bhbvs + v8zq4 50% без туннеля, 50% через

Если клиент попадает на ноду без ingress-пода, kube-proxy добавляет ещё один Geneve-переход.


Тестирование (curl с ВМ, 30 запросов, 63 КБ)

Цель: подтвердить, что только MTU 1400 работает стабильно при любом размещении подов.

MTU пода Размещение drhider / ingress Результат
1400 bhbvs / bhbvs + v8zq4 30/30
1400 6f74n / 6f74n + bhbvs 30/30
1450 6f74n / 6f74n + bhbvs 30/30 (50% без туннеля — повезло)
1500 6f74n / 6f74n + bhbvs 30/30 (50% без туннеля — повезло)
1500 v8zq4 / v8zq4 + ctrl 23/30
1500 bhbvs / 6f74n + bhbvs 0/30

При MTU 1500 и drhider на bhbvs с ingress на 6f74n+bhbvs — все запросы идут через Geneve, результат 0/30.

Вывод: MTU 1500 работает только случайно (когда поды оказываются на одной ноде). MTU 1400 — единственно надёжное решение.


Баг #2: ERR_CONNECTION_RESET в браузере (2026-07-14)

Симптом

После фикса MTU — curl с ВМ работал, но браузер (Chrome/Electron) при загрузке 63KB docx получал ERR_CONNECTION_RESET.

Причина

Два механизма одновременно:

  1. keep-alive: 10 — nginx закрывал idle-соединения через 10с. Браузер не замечал FIN (буферизация TCP в ОС), отправлял POST в уже закрытый сокет → RST.

  2. kube-vip ARP flapping — при смене ARP-лидера трафик уходил на другую ноду без conntrack-записи → RST.

Решение

# Где Что Было Стало
1 Ingress ConfigMap keep-alive 10 75
2 index.html (JS) warmup GET при загрузке fetch('/health')

Результат тестов (v0.0.26)

Тест Результат
5× свежих страниц + POST 63KB 4× OK, 1× ABORTED (Playwright)
UI: 2 файла + SSE + download полный цикл, 18.5с
3× POST через 10+ мин (kube-vip окно) 3× OK:200
20× POST (батарея) 0 ERR_CONNECTION_RESET

Текущая конфигурация (2026-07-14)

Ingress ConfigMap

Параметр По умолчанию Текущее Причина
keep-alive 75 75 Было 10 — рвало idle → RST
use-http2 true "false" HTTP/2 давал PROTOCOL_ERROR
proxy-body-size 8m 1024m Загрузка docx/pdf до 1 ГБ
client-body-timeout 60 10 ⚠️ Надо вернуть 60
client-header-timeout 60 10 ⚠️ Надо вернуть 30
upstream-keepalive-connections 0 0 Был 50, эксперимент
error-log-level info debug Временно для диагностики

Cilium

Параметр По умолчанию Текущее Причина
mtu 1500 1400 Geneve +50 = 1450 = underlay

Приложение (drhider v0.0.26)

Файл Изменение
site/templates/index.html fetch('/health') при загрузке — прогрев upstream
site/app.py VERSION = "0.0.26"

Для DevOps — что должно быть в Штурвале

# Ingress ConfigMap
keep-alive: "75"               # ⚠️ было 10 — СРОЧНО исправить
client-body-timeout: "60"      # ⚠️ было 10 — СРОЧНО исправить
client-header-timeout: "30"    # ⚠️ было 10
use-http2: "true"              # можно вернуть
proxy-body-size: "1024m"       # под загрузку файлов
upstream-keepalive-connections: "0"
error-log-level: "info"        # вернуть после диагностики

# Cilium ConfigMap (kube-system)
mtu: "1400"                    # ⚠️ было 1500 — Geneve overhead