Files
loadtest/History/2026-07-11-sonnet-review.md
2026-07-11 15:16:41 +04:00

89 lines
5.8 KiB
Markdown
Raw Permalink 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.
# Расследование: загрузка файлов через 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, будет иметь ту же проблему.