Files
drhider/PROBLEM-AND-SOLUTION.md
T

132 lines
9.5 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
---
## В чём проблема
При передаче данных через HTTP POST из внешней сети на managed-сервисы, развёрнутые в кластере (Flask, любые другие), периодически возникает пауза длительностью 51 секунда. Проблема затрагивает любой сервис, чей трафик проходит через ingress-контроллер Штурвала при cross-node-форвардинге. Размер передаваемых данных не имеет значения — задержка константна, проявляется даже на 100 KB.
Внутри кластера (pod → ingress pod напрямую) передача происходит мгновенно. Проблема проявляется только при проходе трафика извне через VIP (kube-vip), который распределяет запросы на все ноды кластера.
### Причина (технически)
*Cilium — сетевая подсистема Kubernetes, отвечает за связь между подами на разных нодах. Geneve — протокол туннелирования, который Cilium использует чтобы «обернуть» обычный сетевой пакет для передачи через физическую сеть между серверами.*
```mermaid
flowchart LR
A["Geneve 50 B"]:::orange --- B["Данные 1450 B"]:::green
B -.-> C["Итого 1500 B"]:::gray
C --> D["Underlay-канал 1450 B"]:::red
D --> E["TCP stall 51с"]:::dark
classDef orange fill:#ff9800,color:#fff,stroke:#e65100
classDef green fill:#4caf50,color:#fff,stroke:#2e7d32
classDef gray fill:#9e9e9e,color:#fff,stroke:#616161
classDef red fill:#f44336,color:#fff,stroke:#c62828
classDef dark fill:#b71c1c,color:#fff
```
> **1450** (данные) + **50** (Geneve) = **1500** → underlay **1450** → не проходит → TCP stall **51 секунда**.
- Cilium соединяет ноды через **Geneve-туннель**
- Geneve добавляет к каждому пакету **+50 байт** заголовков (внешний IP 20 + UDP 8 + Geneve 8 + инкапсулируемый L2-кадр 14)
- Cilium ожидает что underlay (физический канал между нодами) = **1500 байт**
> *Стандартный Ethernet MTU — 1500. Все сетевые интерфейсы, драйверы и протоколы по умолчанию исходят из этого. Cilium опрашивает интерфейс ноды, видит 1500 и принимает за истину. Почему реальный underlay оказался 1450 — может быть связано с инфраструктурой хостинг-провайдера, промежуточным сетевым оборудованием или инкапсуляцией на уровне виртуализации. Без диагностики на стороне провайдера точная причина неизвестна. Факт установлен замером: 1500 не проходит, 1450 проходит.*
- Cilium считает: **1500 50 = 1450** — такой MTU назначает туннелю и подам
- Реальный underlay (измерен `ping -M do`): **1450 байт**, а не 1500
- Пакет из пода = **1450 байт**
- Пакет + Geneve-заголовок = **1450 + 50 = 1500 байт**
- Реальный канал = **1450 байт** → пакет **1500 не влазит**
- Роутер пытается отправить ICMP «fragmentation needed», но ICMP заблокирован
- Отправитель не знает что пакет не дошёл, ждёт подтверждения
- TCP включает exponential backoff от начального RTO 200ms: **0.2с → 0.4с → 0.8с → 1.6с → 3.2с → 6.4с → 12.8с → 25.6с = 51 секунда**
- Через 51 секунду прикладной таймаут (браузер/curl) разрывает ожидание → `ERR_TIMED_OUT`
### Почему не всегда
Проблема только при **cross-node**-форвардинге (kube-vip направляет трафик на ingress-под на другой ноде). При попадании на ту же ноду — всё мгновенно.
---
## Что сделано сейчас (временный обход)
Для обхода проблемы были применены два изменения:
**1. `externalTrafficPolicy: Local`** — запрещает cross-node-форвардинг. Трафик обслуживается только на той ноде куда пришёл, без туннеля → stall отсутствует.
**2. 4 реплики ingress вместо 2** — у Штурвала по умолчанию 2 пода, а нод 4. С `Local` трафик на ноду без пода дропается. Выставлено 4 — по одной на каждую ноду.
**Проблема:** Штурвал управляет ingress через Helm с `replicaCount: 2`. При обнаружении расхождения (фактическое состояние не соответствует Helm-значениям) реконсилер возвращает реплики с 4 обратно на 2. Интервал не фиксирован — срабатывает по событию (деплой, изменение конфигурации). Половина запросов начинает падать с `ERR_TIMED_OUT`.
---
## Корневое решение
Выставить корректный MTU в Cilium:
```bash
kubectl patch configmap -n kube-system cilium-config --type merge -p '{"data":{"mtu":"1400"}}'
kubectl rollout restart ds/cilium -n kube-system
kubectl delete pods -n ingress --all --force --grace-period=0
kubectl delete pods -n <drhider-ns> --all --force --grace-period=0
```
После этого:
- Cross-node-форвардинг работает без stall
- Ingress остаётся на 2 репликах (стоковое значение Штурвала)
- `Local` не нужен
- Ручной scale не нужен
### Почему 1400
Underlay между нодами — **1450** (измерен `ping -M do`).
В этой версии Cilium (`routing-mode: tunnel`, `tunnel-protocol: geneve`) значение `mtu` задаёт pod MTU напрямую. Geneve-заголовок (+50 байт) добавляется поверх.
```
Pod MTU 1400 → пакет 1400 + Geneve 50 = 1450 = underlay 1450 → проходит
Pod MTU 1450 → пакет 1450 + Geneve 50 = 1500 > underlay 1450 → дроп
```
| Параметр | Значение |
|---------|----------|
| Underlay между нодами | 1450 |
| `cilium-config mtu:` | **1400** |
| Pod MTU | 1400 |
| Пакет через Geneve | 1400 + 50 = 1450 |
> В некоторых версиях Cilium `mtu` означает underlay (Cilium сам вычитает 50). В нашей — задаёт pod MTU напрямую. Проверено 2026-07-14: `mtu: 1450` → pod 1450 (не работает через Geneve), `mtu: 1400` → pod 1400 (работает).
---
## Результаты (кластер `iot-naeel`, 2026-07-14)
| Метрика | До (MTU 1450) | После (MTU 1400 + Connection: close) |
|---------|---------------|--------------------------------------|
| Upload из браузера | ERR_CONNECTION_RESET | <0.2s |
| 5 файлов подряд | первый сбой | все <0.1s |
| F5 при обработке | мёртвые соединения | чисто |
| Edge 7× подряд | на 5-й сбой | стабильно |
---
## Рекомендации для production-кластеров
На действующих кластерах с активными сервисами **НЕ рекомендуется** применять `kubectl edit configmap` + `rollout restart ds/cilium` вручную. Причины:
- `rollout restart ds/cilium` перезапускает сетевой слой на **всех** нодах одновременно (rolling update, но всё же)
- eBPF-датаплейн переживает рестарт, но возможны кратковременные блипы
- Ошибка в значении MTU затронет **все** сервисы кластера
- На production требуется окно обслуживания и мониторинг
**Рекомендуемый порядок для production:**
1. Замерить underlay MTU между нодами через `ping -M do`
2. Передать значение провайдеру/девопсу для включения в конфигурацию кластера
3. Применить при плановом обслуживании или при создании новых кластеров
4. Для существующих кластеров — согласовать окно и порядок отката
**Для новых кластеров:** включить `mtu: <измеренное значение>` в конфиг Cilium на этапе развёртывания, до деплоя приложений.