docs: review response — methodology critique and lessons learned
Deploy loadtest / validate (push) Waiting to run
Deploy loadtest / validate (push) Waiting to run
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
# Расследование: загрузка файлов через Managed Flask на Shturval
|
||||
|
||||
## 1. Исходная проблема
|
||||
|
||||
Приложение: Flask 3.1.3 + flask-sock, загрузка файлов через WebSocket (64KB чанки).
|
||||
Платформа: Shturval 2.12.1 (managed Kubernetes), kube-vip ARP mode, Cilium Geneve.
|
||||
|
||||
Симптом: файл 3.7 MB — первые 90% загружаются мгновенно, затем пауза ровно 51 секунда.
|
||||
В ~80% соединений. Оставшиеся ~20% проходят мгновенно.
|
||||
|
||||
## 2. Что проверили (и исключили)
|
||||
|
||||
### Код приложения
|
||||
- Пробовали flow control на клиенте (bufferedAmount) — не помогло
|
||||
- Пробовали server-side ACK (чанк → подтверждение → следующий) — сломалось полностью
|
||||
- Создали минимальный Flask (13 строк кода, без логики) — та же пауза
|
||||
- HTTP POST вместо WebSocket — та же пауза для тел >50KB
|
||||
- Gunicorn с gevent вместо Werkzeug — не влияет
|
||||
|
||||
### Платформа
|
||||
- nodeSelector (под на одной ноде с ingress) — не влияет
|
||||
- Scale ingress до 4 реплик (на всех нодах) — не влияет
|
||||
- hostNetwork на python-поде (исключает Cilium Geneve) — не влияет
|
||||
- keepalive=0 к upstream (исключает nginx connection pool) — не влияет
|
||||
- vip_leaderelection=true на kube-vip (пробовали ранее) — не влияет
|
||||
- MSS clamping (пробовали ранее) — не влияет
|
||||
|
||||
### nginx ingress
|
||||
- proxy_request_buffering on/off — не влияет
|
||||
- proxy_buffering on/off — не влияет
|
||||
- ModSecurity в DetectionOnly — не блокирует
|
||||
- Таймауты: proxy_send_timeout 600s, proxy_read_timeout 600s
|
||||
|
||||
## 3. Ключевой эксперимент: изоляция точки сбоя
|
||||
|
||||
Тестировали WebSocket 2MB с разных точек:
|
||||
|
||||
| Источник | Цель | Результат |
|
||||
|---|---|---|
|
||||
| Pod внутри кластера | ingress pod IP (та же нода, 172.16.1.51) | **5/5 FAST** |
|
||||
| Pod внутри кластера | ingress pod IP (другая нода, 172.16.0.9) | **5/5 FAST** |
|
||||
| Pod внутри кластера | ingress ClusterIP (10.105.13.158) | **5/5 FAST** |
|
||||
| Pod внутри кластера | свой же ClusterIP (без ingress) | **3/3 FAST** |
|
||||
| SSH-хост (5.172.178.213) | VIP (185.247.187.151) | **2/5 FAST, 3/5 SLOW** |
|
||||
| Внешняя машина | VIP | **~15% FAST, ~85% SLOW** |
|
||||
|
||||
**Вывод:** внутри кластера всё мгновенно всегда. Пауза только при проходе через VIP извне.
|
||||
|
||||
## 4. Корень проблемы
|
||||
|
||||
Kube-vip в ARP-режиме с `vip_leaderelection=false` (отдельно от `svc_election=true`):
|
||||
|
||||
- Все 4 ноды анонсируют VIP через ARP
|
||||
- Маршрутизатор выбирает ноду (вероятно, ECMP или последний ARP-ответ)
|
||||
- Kube-vip проводит leader election **для сервиса** и назначает VIP на интерфейс ноды-лидера
|
||||
- Лидером стала нода `iot-naeel-workers-vqphm-bhbvs` (10.10.102.4)
|
||||
- На этой ноде **не было** ingress-пода (ingress был только на 2 из 4 нод)
|
||||
- Трафик шёл: клиент → VIP → bhbvs → cross-node forward → ingress-нода → python-под
|
||||
|
||||
Cross-node форвардинг (через Geneve или IPIP) вызывает TCP stall:
|
||||
- Первые ~1MB данных проходят в TCP-буфер
|
||||
- Буфер заполняется → TCP объявляет zero window
|
||||
- Начинается stop-and-go oscillation: сервер читает порцию → окно приоткрывается → пара пакетов проходит → окно закрывается
|
||||
- Эффективная скорость падает до ~8 KB/s
|
||||
- Оставшиеся данные дрейнуются за ~51 секунду
|
||||
|
||||
Сервис имел `externalTrafficPolicy: Cluster` (дефолт), что разрешает kube-vip форвардить трафик на **любую** ноду, даже без локальных endpoints.
|
||||
|
||||
## 5. Решение
|
||||
|
||||
```bash
|
||||
kubectl patch svc -n ingress shturval-ingress-controller-controller \
|
||||
-p '{"spec":{"externalTrafficPolicy":"Local"}}'
|
||||
```
|
||||
|
||||
Плюс `kubectl scale deploy -n ingress shturval-ingress-controller-controller --replicas=4` — чтобы на каждой ноде был локальный ingress-под.
|
||||
|
||||
С `Local` трафик идёт только на ноды с локальными ingress-подами. Cross-node форвардинг исключён.
|
||||
|
||||
**Результат:** 5/5 FAST (2MB WebSocket через VIP с внешней машины).
|
||||
|
||||
## 6. Что должно быть сделано на стороне платформы
|
||||
|
||||
Kube-vip с `externalTrafficPolicy: Cluster` должен форвардить трафик **только** на ноды с живыми endpoints сервиса, а не на все ноды подряд. Либо Shturval должен по умолчанию ставить `externalTrafficPolicy: Local` для LoadBalancer-сервисов, либо kube-vip должен корректно обрабатывать `Cluster`-режим без TCP stall.
|
||||
|
||||
## 7. Вердикт
|
||||
|
||||
Баг платформы. Код приложения корректен. Любое приложение на этом кластере, принимающее >50KB данных через VIP, будет иметь ту же проблему.
|
||||
Reference in New Issue
Block a user