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

6.1 KiB
Raw Blame History

Вопрос для 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.

КЛЮЧЕВАЯ НАХОДКА

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