Files
IoT/doc/thinking/nubes-devops-report-2026-08-16.md
T

9.5 KiB
Raw Blame History

Отчёт для DevOps Nubes — сетевые проблемы платформы

Кластер: iot-naeel Дата: 2026-08-16 Статус: предварительный, все факты подтверждены тестами и дампами. Готовы предоставить скрипты воспроизведения и провести совместные тесты.


Часть 1. Что не работает (наблюдения и доказательства)

1.1 Внешние WebSocket-соединения закрываются ~через 150 секунд

Симптом. WebSocket-соединения из внешних сетей (wss, TLS) закрываются в районе 150 секунд после установления. Для клиентов это выглядит как разрыв без видимой причины (close 1006/1005) с необходимостью переподключения.

Доказательства.

  • 5 из 5 внешних станций (разные сети) — разрыв ~150 с, стабильно.
  • Чистый VPS (Vultr) — 1 из 1, тот же разрыв ~150 с.
  • Внутри кластера (под → сервис) — 600 с без единого разрыва.
  • Машина за тем же шлюзом, но вне платформы — 300 с чисто.
  • Контрольные wss-соединения к внешним ресурсам (nginx, echo-серверы) с тех же станций — чисто.

Вывод. Обрывает именно edge-шлюз платформы (или балансировщик перед ним), по таймеру. Похоже на фиксированный лимит времени жизни соединения (idle/lifetime timeout) на проксирующем слое.

Влияние. Любой долгоживущий протокол поверх WebSocket (например, MQTT) вынужден переподключаться каждые ~150 с. Наши клиенты это переживают (автопереподключение), но это лишний трафик и окна потерь.

1.2 Зависание внешних соединений ~на 51 секунду при объёмном трафике (MTU)

Симптом. Из внешней сети операции с телом/ответом крупнее ~1,4 КБ периодически «зависают» ровно на ~51 секунду, после чего либо завершаются, либо обрываются. Внутри кластера те же операции проходят мгновенно.

Доказательства (три независимых контура).

  • Контур A (HTTP-сервис за платформой): POST снаружи — пауза ~51 с на любом объёме данных выше порога; изнутри — мгновенно. При внешнемTrafficPolicy: Cluster большие файлы (>5 МБ) дают TCP-stall ровно на 51 с.
  • Контур B (очередь с AWS-совместимым API): тела >~1,4 КБ виснут ~51 с (классическая экспонента TCP-ретрансмитов).
  • Контур C (REST API): HTTP-ответы крупнее ~15 КБ приходят частично и виснут (в тесте получено 15 672 байта — далее стоп); маленькие ответы (до ~15 КБ) проходят стабильно.

Диагностика (наша гипотеза, подтверждённая измерениями).

  • Underlay MTU = 1450.
  • Оверлей (Cilium Geneve) добавляет ~50 байт инкапсуляции.
  • MTU контейнеров остаётся дефолтным 1500.
  • Кросс-нодовый пакет полного размера = 1500 + 50 = 1550 > 1450 → фрагментация невозможна (DF) → пакет дропается → ядро ждёт ретрансмиссии по экспоненте → суммарная пауза ~51 с.
  • На внешнем пути наблюдается MSS 1448 при MTU 1400 — т.е. ICMP «fragmentation needed» до клиентов, вероятно, не доходит (PMTUD сломан на каком-то из хопов).

Влияние. Любой объёмный запрос/ответ снаружи рискует зависнуть на ~51 с; клиенты с таймаутами 30 с получают «таймаут», хотя сервис ответил мгновенно.

1.3 Периодические таймауты внешних запросов на малых объёмах (~5,5%)

Симптом. Даже запросы размером ~512 байт снаружи в ~5,5% случаев не получают ответ 30+ секунд; характерный паттерн — зависание на 31–33 с.

Доказательства.

  • Внутри кластера: 24 977 запросов подряд — 0 сбоев (порт-форвард).
  • Снаружи: периодические зависания подтверждены tcpdump-ом (запрос уходит, ответ не возвращается в течение 30+ с).
  • Маловероятно, что это наша прикладная логика: те же операции изнутри кластера стабильны.

Гипотеза. Связь с пунктом 1.2 (потеря крупных сегментов при переговорах TLS/подтверждениях) или лимиты conntrack/сессий на балансировщике.


Часть 2. Как это исправить (наши предложения)

2.1 К пункту 1.1 (wss ~150 с)

  1. Найти слой, который закрывает соединения: проверить настройки idle/lifetime timeout на edge-шлюзе и балансировщике перед кластером (nginx: proxy_read_timeout, proxy_send_timeout; LB: idle timeout).
  2. Для wss-трафика выставить таймауты ≥ 600 с (или «без таймаута»).
  3. Включить WebSocket ping/pong на прокси-слое (keepalive-фреймы каждые 30–60 с), чтобы соединение не считалось простаивающим.
  4. После изменения — прогнать наш контрольный тест (wss-клиент держит соединение 10 минут; критерий — 0 разрывов).

2.2 К пункту 1.2 (MTU / 51 с)

Варианты, в порядке предпочтения:

  1. Включить MSS-clamping на внешнем пути (на шлюзе/на узлах): iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN \ -j TCPMSS --clamp-mss-to-pmtu (или фиксированный MSS под MTU внешнего интерфейса). Это дешёвое и надёжное решение: клиент не будет слать сегменты крупнее, чем проходит путь.
  2. Привести MTU контейнеров к underlay: MTU подов 1450 − 50 (инкапсуляция) = 1400. Тогда кросс-нодовые пакеты перестанут превышать underlay, и дропы исчезнут в принципе.
  3. Проверить, что ICMP «fragmentation needed» не режется на всём внешнем пути (firewall/LB) — восстановление PMTUD решит проблему для больших ответов.
  4. Отключить запрет фрагментации на edge для исходящих сегментов (менее желательно).

Критерий приёмки: POST/GET с телами 1 КБ…1 МБ снаружи проходят без пауз >2 с (наш тест: 100 запросов разных размеров, 0 зависаний).

2.3 К пункту 1.3 (таймауты малых тел)

  1. Сначала применить 2.2 — вероятно, это тот же корень.
  2. Если проблема останется: проверить лимиты conntrack на шлюзе (nf_conntrack_max, table-full в логах) и пул/лимиты соединений балансировщика.
  3. Критерий: 10 000 запросов по 512 байт снаружи — 0 таймаутов.

Что мы уже сделали со своей стороны (обходы)

  • Клиенты переподключаются автоматически (переживают пункт 1.1).
  • Внутренние взаимодействия ходят по внутренним DNS, минуя внешний шлюз (таймауты 1.3 нас не блокируют).
  • Чтение больших списков разбито на страницы небольшого размера (обход пункта 1.2).

Что нужно от вас

  1. Подтверждение/опровержение по каждому пункту.
  2. Если нужны воспроизведения — дайте окно и среду, прогоним тесты совместно (скрипты готовы).
  3. Оценка сроков по пунктам 2.1–2.3.