Files
contracts/History/sonnet-question-tcp-stall-2026-07-16.md
T

117 lines
6.1 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.
# Вопрос для 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. Есть ли ещё что посмотреть в кластере чего мы не проверили?