134 lines
8.8 KiB
Markdown
134 lines
8.8 KiB
Markdown
# Проблема загрузки файлов: диагностика и решение
|
||
|
||
**Дата:** 2026-07-13
|
||
|
||
---
|
||
|
||
## В чём проблема
|
||
|
||
При загрузке файлов через веб-интерфейс drhider периодически возникает пауза 51 секунда. Размер файла не важен — даже 100 KB могут «залипнуть».
|
||
|
||
### Причина (технически)
|
||
|
||
Кластер использует Cilium CNI с Geneve-туннелем между нодами. Geneve добавляет 50 байт к каждому пакету. При underlay MTU = 1450 (подтверждено замером) пакет с Geneve-заголовком не влазит → роутер не может фрагментировать (ICMP заблокирован) → TCP ждёт таймаут 51 секунду.
|
||
|
||
### Почему не всегда
|
||
|
||
Проблема только при **cross-node**-форвардинге (kube-vip направляет трафик на ingress-под на другой ноде). При попадании на ту же ноду — всё мгновенно.
|
||
|
||
---
|
||
|
||
## Что сделано сейчас (временный обход)
|
||
|
||
Чтобы файлы не зависали, мы вручную поставили два костыля:
|
||
|
||
**1. `externalTrafficPolicy: Local`** — запрещает cross-node-форвардинг. Трафик обслуживается только на той ноде куда пришёл, без туннеля → stall нет.
|
||
|
||
**2. `kubectl scale replicas=4`** — у Штурвала по умолчанию 2 ingress-пода, а нод 4. С `Local` трафик на ноду без пода дропается. Поставили 4 реплики — по одной на каждую ноду.
|
||
|
||
**Проблема:** Штурвал управляет ingress через Helm с `replicaCount: 2` и при каждом реконсиле (~каждый час) сбрасывает 4 обратно на 2. Половина запросов начинает падать с `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-костыль.
|