v2.0.5: честный счётчик загрузки + вопрос Sonnet про TCP stall
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
# Вопрос для Sonnet — TCP stall 51s на первом upload (2026-07-16)
|
||||
|
||||
## Ситуация
|
||||
|
||||
Два сервиса на одном кластере (contractor + drhider), оба Flask на Штурвале.
|
||||
После миграции contractor на Flask: **первый upload файла из браузера зависает на ~51 секунду**, потом проходит. Второй и последующие файлы — мгновенно.
|
||||
|
||||
## Симптомы
|
||||
|
||||
- **Браузер**: первый POST /upload (~63KB) висит 51 секунду на ~95% прогресса, потом ок
|
||||
- **Логи ingress (nginx)**: ВСЕ запросы (включая успешные) проходят за 0.06-0.16 сек
|
||||
- **Зависание происходит ДО того как запрос попадает в nginx** — на уровне TCP
|
||||
- **51 секунда** — стабильно, воспроизводится периодически
|
||||
- **Затрагивает оба сервиса**: и contractor и drhider
|
||||
|
||||
## Топология кластера
|
||||
|
||||
```
|
||||
Клиент (браузер)
|
||||
↓
|
||||
185.247.187.151 (kube-vip, LoadBalancer, externalTrafficPolicy: Cluster)
|
||||
↓ на ЛЮБУЮ из 4 нод
|
||||
shturval-ingress-controller (4 реплики, по одной на каждой ноде)
|
||||
↓ upstream к поду сервиса
|
||||
pythonk8s (1 под, нода workers-6f74n, IP 172.16.1.221:5000)
|
||||
```
|
||||
|
||||
4 ноды (k8s 1.34.1):
|
||||
- control-plane-xb699 (10.10.102.2) — поды: 172.16.0.0/24
|
||||
- workers-6f74n (10.10.102.3) — поды: 172.16.1.0/24 ← **contractor здесь**
|
||||
- workers-bhbvs (10.10.102.4) — поды: 172.16.2.0/24
|
||||
- workers-v8zq4 (10.10.102.5) — поды: 172.16.3.0/24
|
||||
|
||||
Ingress-поды (4 шт):
|
||||
- 172.16.0.73 на control-plane (.2)
|
||||
- 172.16.1.65 на workers-6f74n (.3) ← **локально с contractor**
|
||||
- 172.16.2.144 на workers-bhbvs (.4)
|
||||
- 172.16.3.149 на workers-v8zq4 (.5)
|
||||
|
||||
Cilium CNI: Geneve-энкапсуляция, MTU 1400, encryption Disabled.
|
||||
|
||||
kube-vip: DaemonSet (по одному на ноду .2 .3 .4 .5) + provider.
|
||||
|
||||
## КЛЮЧЕВАЯ НАХОДКА
|
||||
|
||||
```yaml
|
||||
# Ingress ConfigMap:
|
||||
upstream-keepalive-connections: "0"
|
||||
upstream-keepalive-time: 60s
|
||||
```
|
||||
|
||||
**Каждый HTTP-запрос от ингресса к бэкенду — новый TCP-рукопожатие.**
|
||||
|
||||
75% запросов проходят через Geneve-туннель (ingress на .2/.4/.5 → backend на .3).
|
||||
|
||||
51 секунда ≈ `tcp_syn_retries=5`: 1+2+4+8+16 = 31с или 3+6+12+24+48 = 93с.
|
||||
Точнее — зависит от начального RTO ядра.
|
||||
|
||||
## Вторая подозрительная находка
|
||||
|
||||
Cilium на ноде .3 (`cilium bpf tunnel list`) показывает Geneve-туннели:
|
||||
|
||||
```
|
||||
172.16.3.0 → 10.10.102.5 (нода .5)
|
||||
172.16.1.0 → 10.10.102.3 (себя)
|
||||
172.16.0.0 → 10.10.102.2 (нода .2)
|
||||
```
|
||||
|
||||
**Туннель к ноде .4 (172.16.2.0 → 10.10.102.4) ОТСУТСТВУЕТ в списке!**
|
||||
|
||||
Всего 3 туннеля при 4 нодах.
|
||||
|
||||
## Что проверено
|
||||
|
||||
- `cilium status --verbose`: все healthy, 0 ошибок
|
||||
- `cilium monitor -t drop`: дропов нет
|
||||
- `cilium bpf ct list global`: 4682 записи, conntrack не переполнен
|
||||
- `cilium encrypt status`: Disabled
|
||||
- `cilium bpf lb list`: сервис pythonk8s (10.98.50.130:80 → 172.16.1.221:5000) — OK
|
||||
- Cilium endpoint 1010 (pythonk8s): Disabled/Disabled, ready
|
||||
- Логи ingress (последние 100 запросов): все 200 OK, 0.007-0.162 сек
|
||||
- Ошибок `upstream timed out`, `connect failed`, `502/504` в логах ingress НЕТ
|
||||
- kube-vip поды: все Running
|
||||
- kube-vip provider: Running (рестарт 46ч назад)
|
||||
|
||||
## Гипотезы
|
||||
|
||||
1. **Первый TCP-рукопожатие ingress→backend через Geneve теряет SYN**
|
||||
- Причина: eBPF-программа Cilium для нового соединения не готова мгновенно
|
||||
- Причина: neighbour discovery для Geneve endpoint
|
||||
- Вопрос: почему именно 51 секунда, а не мгновенный RST или 1-2 секунды?
|
||||
|
||||
2. **Отсутствующий Geneve-туннель к ноде .4**
|
||||
- Если запрос попадает на ingress на .4 → трафик к backend на .3 должен идти через туннель
|
||||
- Если туннеля нет в bpf map — куда идёт трафик? Через хост-сеть? Через другую ноду?
|
||||
|
||||
3. **externalTrafficPolicy: Cluster + 4 ingress реплики**
|
||||
- kube-vip может отправить трафик на ЛЮБУЮ ноду
|
||||
- Если на ноду без ingress-пода → kube-proxy/Cilium форвардит на другую ноду → двойной Geneve
|
||||
- Может ли это добавлять задержку?
|
||||
|
||||
4. **client-body-timeout: 60 + первый upload = гонка**
|
||||
- Если TCP-рукопожатие занимает 51 секунду, а body upload начинается после
|
||||
- Может ли общее время превысить 60 секунд и вызвать 408/413?
|
||||
|
||||
## Вопросы
|
||||
|
||||
1. Почему `upstream-keepalive-connections: "0"` + Geneve вызывает 51-секундный TCP-stall именно на ПЕРВОМ запросе, а последующие идут мгновенно?
|
||||
|
||||
2. Отсутствие Geneve-туннеля к ноде .4 (172.16.2.0) в `cilium bpf tunnel list` на ноде .3 — это норма или баг Cilium? Может ли это быть причиной потери SYN-пакетов?
|
||||
|
||||
3. Как диагностировать ГДЕ именно теряется SYN: между kube-vip и ingress, или между ingress и backend? Какие инструменты Cilium/ядренные использовать?
|
||||
|
||||
4. Рекомендация: менять `upstream-keepalive-connections` на 2-4 вместо 0 — решит ли это проблему первого запроса?
|
||||
|
||||
5. Есть ли ещё что посмотреть в кластере чего мы не проверили?
|
||||
Reference in New Issue
Block a user