Files
SQS-service/doc/thinking/nubes-network-bug-plan-v3-sonnet.md
T

10 KiB
Raw Blame History

Plan: Доказательство сетевого бага Nubes — v3 (СОННЕТ)

ПОМЕТКА: это план от Соннета (пользователь передал ответ Соннета). Сохранено 2026-08-15. Сравнение с v1 (DeepSeek Flash) и v2 (DeepSeek Pro) — в конце файла и в HISTORY.

TL;DR

Два РАЗНЫХ явления с разными причинами — нельзя объяснять одной гипотезой и нельзя ловить одним методом. Цепочка: nginx-логи → port-forward (бесплатное доказательство невиновности приложения) → PMTUD-зонд → tcpdump → cilium monitor → echo-сервис (только если предыдущих данных не хватает).

Разделение двух явлений (критически важно)

Явление A — большие POST (~51с). Причина: MSS=1448, underlay MTU=1450, сегменты 1488 → дроп. PMTUD сломан (ICMP Type 3 Code 4 заблокирован шлюзом) → TCP ретрасмитит по экспоненте: 1с,2с,4с,8с,16с ≈ 31с, ещё попытка → ~51с. Задача: подтвердить ping-зондом и зафиксировать в тикете.

Явление B — малые POST 512 байт (~1/37с). MTU-моделью НЕ объясняется. Ключевая улика: РЕГУЛЯРНОСТЬ 1/37с — это таймер, не случайные потери. Кандидаты: сессионный таймер gateway на VIP 185.247.187.151 (вероятнее всего), kube-vip ARP keepalive цикл (флап при смене лидера), conntrack GC с агрессивным idle-таймером.

1. Echo vs сырые TCP

Сырые TCP на 32391 — недостаточны (проверяют только фазу установления; проблемы в фазе ДАННЫХ; nginx ответит RST/400 на мусор — неинформативно).

Правильный порядок:

  1. kubectl port-forward (бесплатно, 5 минут) — чище и быстрее echo.
  2. Echo NodePort без ingress — если port-forward и логи не дали ответа.

Echo: принимает POST, возвращает тело; ?bytes=N для размера ответа (обратный путь отдельно от прямого). Деплой БЕЗ ingress, прямой NodePort.

2. Как поймать таймаут: tcpdump

Клиент (запустить ДО теста, параллельно): tcpdump -i <iface> -s 0 -w /tmp/sqs.pcap 'host 185.247.187.151 and port 32391'

Явление A (51с, большой POST): ICMP Type 3 Code 4 от 185.x → PMTUD работает; нет ICMP, ретрасмиты 1/2/4/8/16с → PMTUD сломан (основная гипотеза); кто шлёт RST в конце и через сколько — чей таймаут (клиент vs nginx/шлюз).

Явление B (30с, 512 байт): ACK на запрос получен → запрос дошёл, ответ потерян на обратном пути; ACK нет → потеря до nginx (kube-vip/NodePort/gateway); точный интервал между таймаутами (~37с ± 1с → таймер подтверждён); RST от сервера — чей (nginx idle keepalive или gateway).

Ключевой приём: корреляция по TCP seq/ack, не по времени.

3. kube-vip / NodePort / nginx — только kubectl

nginx access log: kubectl logs -n ingress <nginx-pod> --since=35m | grep -E " (499|5[0-9][0-9]) ". Нужны поля $request_time $upstream_response_time. Если нет — проверить ConfigMap nginx.

Запись в nginx log Вывод
499 + большой request_time клиент ушёл до ответа (наш ReadTimeout)
200 + upstream <100мс, клиент получил таймаут ответ потерян между nginx и клиентом
записи нет потеря ДО nginx: kube-vip/NodePort/gateway

kube-vip: kubectl get pods -A | grep -i vip, логи (leader/ARP/flap/error). ARP-флап во время теста → кратковременная потеря VIP → дропы.

Cilium (на ПОДЕ, не на operator): kubectl get pods -n kube-system -l k8s-app=cilium -o wide; kubectl exec -n kube-system <cilium-pod> -- cilium monitor --type drop параллельно с тестом.

NodePort/SNAT: externalTrafficPolicy — Cluster → любая нода + SNAT (лишний хоп, conntrack); Local → только нода-держатель VIP (миграция VIP → brief outage).

port-forward: kubectl port-forward -n <ns> svc/<sqs-svc> 14100:4100 — тот же скрипт на localhost:14100. 0 таймаутов → приложение невиновно.

4. Пошаговый план: дешевле → дороже

Фаза 0 — бесплатно, уже есть данные:

  1. nginx access log за период прошлого теста (499, request_time/upstream_response_time).
  2. kube-vip логи: флапы в тот же промежуток.
  3. Тикет Nubes: два явления отдельно — (A) MTU/PMTUD порог 1448, (B) таймер ~37с.

Фаза 1 — клиентские тесты, 30 минут: 4. PMTUD-зонд: ping -M do -s N 185.247.187.151 для N=1300,1400,1420,1422, 1448,1450,1460,1472 — точный порог. Нет ответа ≥1448 → ICMP заблокирован → тикет с числами. 5. tcpdump + тот же 30-мин тест. Wireshark: tcp.analysis.retransmission || icmp. Точный интервал между таймаутами.

Фаза 2 — kubectl, без деплоя: 6. port-forward тест ПЕРВЫМ: 15-мин прогон через туннель. 0 таймаутов → приложение доказанно невиновно. 7. cilium monitor параллельно со свежим тестом против внешнего IP. 8. Если нет upstream_response_time в формате — обновить ConfigMap, повторить.

Фаза 3 — echo (только если 0–2 не дали ответа): 9. Minimal Go echo (NodePort, без ingress), тот же тест. Воспроизвелось → nginx и приложение исключены → kube-vip/NodePort/gateway. Нет → echo через ingress → изоляция nginx. 10. Матрица размеров 512/1000/1400/1448/1460/1500 × 10 мин, порог бинарным поиском.

Фаза 4 — финальная корреляция для тикета: 11. Одновременно tcpdump + nginx access log + cilium monitor. Корреляция по seq: «запрос ушёл, ACK получен, ответа нет» = потеря между nginx и клиентом. Артефакты: pcap + логи + ping-зонд → тикет.

Решения о приоритетах:

  • port-forward — самый дешёвый и чистый способ изолировать приложение. ДО echo.
  • Сырые TCP на 32391 — пропустить.
  • Echo — только если port-forward + nginx-логи не дали ответа.
  • Явления A и B документировать в тикете РАЗДЕЛЬНО.

Поправки основного агента (GitHub Copilot)

  1. ICMP может быть закрыт на шлюзе — тогда ping-зонд не даст порога. Нужен fallback: mtr --tcp / tcptraceroute или TCP-сегменты переменного размера (то, что Соннет отбросил как «сырые TCP» — но для определения ПОРОГА размера они валидны).
  2. Второй клиент с другого ISP (был у Pro) — отсутствует. Дешёво исключает локальную сеть пользователя. Добавить в фазу 1.
  3. Параллельный контрольный канал YMQ — стоит сохранить во всех тестах: отделяет «плохой интернет вообще» от «плох путь до Nubes».
  4. port-forward идёт через API-сервер — не тот сетевой путь; чистый результат исключает приложение, но НЕ доказывает шлюз. Формулировка Соннета «доказательство невиновности приложения» — корректна, использовать её.
  5. Точный интервал таймаутов надо измерять новым тестом с timestamp на каждый таймаут (в прошлом тесте тайминги не логировались поштучно).
  6. Смена nginx log format (ConfigMap) на managed может требовать релода/помощи платформы — держать как запасной шаг.

Сравнение трёх планов

Flash (v1) Pro (v2) Соннет (v3)
Разделение явлений A/B нет частично явно, с причиной B=таймер
Ошибки команд cilium через operator нет нет
PMTU-зонд нет есть есть, точный список размеров
port-forward нет есть первым kubectl-тестом
Сырые TCP недооценены да отброшены (спорно для порога)
Второй ISP нет есть нет
Критерии доказательства слабые таблица таблицы + фазы
Объяснение 51с нет «прикладной таймер» экспонента RTO 1-2-4-8-16

Вердикт: план Соннета — лучший из трёх. Принять за основу с поправками 1–6.