Files
drhider/PROBLEM-AND-SOLUTION.md
T
2026-07-13 17:49:42 +04:00

128 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Проблема загрузки файлов: диагностика и решение
**Дата:** 2026-07-13
---
## В чём проблема
При загрузке файлов через веб-интерфейс drhider периодически возникает пауза 51 секунда. Размер файла не важен — даже 100 KB могут «залипнуть».
### Причина (технически)
Кластер использует Cilium CNI с Geneve-туннелем между нодами. Geneve добавляет 50 байт к каждому пакету. При underlay MTU = 1450 (подтверждено замером) пакет с Geneve-заголовком не влазит → роутер не может фрагментировать (ICMP заблокирован) → TCP ждёт таймаут 51 секунду.
### Почему не всегда
Проблема только при **cross-node**-форвардинге (kube-vip направляет трафик на ingress-под на другой ноде). При попадании на ту же ноду — всё мгновенно.
---
## Текущий временный обход
`externalTrafficPolicy: Local` запрещает cross-node-форвардинг. Но требует ingress-под на **каждой** ноде (4 шт.). Штурвал управляет ingress через Helm с `replicaCount: 2` и периодически сбрасывает ручной scale на 4 обратно на 2. Результат: 50% запросов падают с `ERR_TIMED_OUT`.
---
## Корневое решение
Выставить корректный MTU в Cilium:
```bash
kubectl edit configmap -n kube-system cilium-config
# mtu: 1450
kubectl rollout restart ds/cilium -n kube-system
kubectl rollout restart deploy -n ingress shturval-ingress-controller-controller
# Вернуть Cluster
kubectl patch svc -n ingress shturval-ingress-controller-controller \
-p '{"spec":{"externalTrafficPolicy":"Cluster"}}'
```
После этого:
- Cross-node-форвардинг работает без stall
- Ingress остаётся на 2 репликах (стоковое значение Штурвала)
- `Local` не нужен
- Ручной scale не нужен
### Почему 1450
Замер выполнен 2026-07-13:
```
ping -M do -s 1472 → Message too large (1500 не проходит)
ping -M do -s 1422 → ok (1450 проходит)
```
Underlay MTU = 1450 → Geneve-туннель = 1400 → поды = 1400.
### Почему это безопасно
- Понижение MTU консервативно: меньше пакет → меньше проблем
- Влияет на весь кластер одинаково (не ломает отдельные сервисы)
- Стандартная практика для туннельных CNI (Geneve, VXLAN, GRE)
- Не трогает логику Штурвала
---
## Полный анализ Опуса 4.8
### Главное уточнение
`mtu` в cilium-config — это MTU **underlay-сети** (физической сети между нодами), а НЕ туннеля. Cilium сам вычитает 50 байт на Geneve:
```
mtu: 1450 → tunnel/pod MTU = 1450 50 = 1400
```
По умолчанию Cilium автодетектит underlay 1500 → туннель 1450. Если stall всё равно происходит — значит реальный underlay < 1500. Поэтому нужно измерять, не гадать.
### Подводные камни
- **ConfigMap сам не применяется.** Нужен `kubectl rollout restart ds/cilium -n kube-system` — без этого агенты продолжат работать со старым MTU.
- **Существующие TCP-соединения не поменяют MSS** — эффект только на новые соединения после рестарта.
- **Нужен рестарт подов приложения** (ingress, drhider) — veth-интерфейс пода получает MTU при создании. Для транзитного (ingress) трафика обновления маршрутов ноды обычно достаточно на новых соединениях, но для чистоты — пересоздать.
- Пересоздавать туннели вручную не нужно — рестарт агента переинициализирует `cilium_geneve` device.
- **Установленный вручную `externalTrafficPolicy: Local`** надо вернуть на `Cluster` через `kubectl patch` (ставился не через Helm, сам не откатится).
### Риски для других сервисов
`mtu` — параметр всего кластера, влияет на ВСЕ поды и ВСЕ ноды. **Понижение MTU безопасно** (меньше = консервативнее). Максимум — небольшой рост числа пакетов / микроскопическая потеря throughput. Функционально ничего не ломает.
Риск в момент раскатки: `rollout restart ds/cilium` перезапускает агенты по нодам. eBPF-датаплейн переживает рестарт (трафик идёт), но возможны короткие блипы. Лучше делать в окно.
Настоящий риск — только если поставить **слишком высокий** MTU (проблема останется) или **слишком низкий без нужды** (лишний оверхед). Поэтому критически важен замер.
### iptables как альтернатива
**Не равноценен и с Cilium может не сработать.** Cilium с eBPF-датаплейном / kube-proxy replacement обходит netfilter `FORWARD` для трафика подов. Правило в `mangle/FORWARD` может просто не видеть нужные пакеты.
`--clamp-mss-to-pmtu` берёт MSS из PMTU исходящего маршрута. Если MTU маршрута неверен — клампинг будет неправильным. Надёжнее явный `--set-mss 1400`.
На какую ноду: kube-vip раздаёт VIP на **все 4 ноды** → правило нужно на всех (DaemonSet/node-level).
Вывод: iptables — костыль-fallback, который с Cilium может оказаться нерабочим. **Правильный путь — `cilium-config`**, потому что Cilium ставит `advmss` на маршрутах (тот же MSS clamping, но нативно в eBPF-датаплейне).
### Верификация после изменения
```bash
# Проверить что Cilium применил MTU
kubectl -n kube-system exec ds/cilium -- cilium-dbg status | grep -i mtu
ip link show | grep -E 'cilium|geneve' # MTU tunnel-девайса
ip route show | grep -E 'advmss|mtu' # advmss на cilium-маршрутах
# Проверить MSS в SYN-пакетах
tcpdump -ni any 'tcp[tcpflags] & tcp-syn != 0' -vv | grep mss
```
Функционально: файл >5 MB через drhider — без паузы 51с. F5 без ERR_TIMED_OUT.
### Более грамотная альтернатива на будущее
Cilium **DSR + `kubeProxyReplacement`** убирает cross-node SNAT на обратном пути целиком — двойной инкапсуляции return-трафика (главная причина stall) просто не возникает. Но это меняет всю сетевую конфигурацию, для быстрого фикса — избыточно.
### Итоговый вердикт
Путь правильный (фикс корня, а не симптома). Конечное состояние `Cluster + корректный MTU + replicaCount: 2` — чистое и согласованное: убирает разом и ручной scale, и Local-костыль.