8.7 KiB
DrHider — итоговая диагностика ERR_CONNECTION_RESET
Дата: 2026-07-15 (финал) Версия: v0.0.29
Резюме (моё мнение)
keep-alive: 10 — единственная причина ERR_CONNECTION_RESET. Всё остальное (conntrack, hostPort, ARP flapping, svc_election) — шум, который мы искали 2 дня.
Почему это так
VIP 185.247.187.151 жёстко привязан к control-plane-xb699 через аннотацию kube-vip.io/vipHost. Никакого flapping нет — трафик всегда приходит на одну ноду. Второй ingress-под на v8zq4 — избыточен, но не мешает.
Сценарий:
- Браузер загружает страницу — opens TCP
- Пользователь выбирает файлы — проходит >10 сек
- Nginx (keep-alive=10) закрывает idle соединение
- Браузер не замечает FIN (буферизация ОС)
- POST 63KB летит в мёртвый сокет → RST
Почему предыдущие гипотезы неверны
| Гипотеза | Опровержение |
|---|---|
| kube-vip ARP flapping | vipHost фиксирует VIP на одной ноде. Lease мёртв — плевать |
| hostPort на 2 из 4 нод | Трафик не на все 4 ноды, а только на control-plane |
| upstream keepalive stale | keepalive=0 не помог |
| MTU | Решено ещё 14-го, не при чём |
| conntrack | ETPolicy Cluster не при чём — трафик на одну ноду |
Что подтверждает keep-alive гипотезу
| Тест | Объяснение |
|---|---|
| Сразу после рестарта ingress → OK | Все соединения свежие |
| Через 5-10 мин → RST | Соединения постарели >10с |
| GET/health → POST → OK | Новое живое соединение |
| curl с ВМ → всегда OK | curl открывает новый SYN каждый раз |
keep-alive: 75 + JS warmup → OK на 10+ мин |
Увеличили окно, warmup греет |
Хронология
| Дата | Событие |
|---|---|
| 2026-07-11 | Исходный деплой |
| 2026-07-14 (день) | MTU fix (Cilium 1400) — решило 51с задержку |
| 2026-07-14 (вечер) | RST диагностика: keep-alive 75 + JS warmup — частично |
| 2026-07-15 (11:53) | Helm ревизия 9 — сброс ConfigMap на дефолты (keep-alive:10) |
| 2026-07-15 | podAffinity required на drhider (ручной kubectl edit) |
| 2026-07-15 (16:00) | Полная диагностика: hostPort vs cloud LB |
Две независимые проблемы
Проблема #1: MTU (решена 2026-07-14)
Симптом: 51с задержка при POST любого размера через внешний VIP.
Причина: Geneve +50, underlay 1450, дефолт 1500 → фрагментация/дроп.
Решение: Cilium mtu: 1400.
Проблема #2: ERR_CONNECTION_RESET (хост порт, решена 2026-07-15)
Симптом: Браузер получает TCP RST при POST 63KB через 5-10 мин после рестарта ingress.
Ранее считалось: keep-alive 10с или kube-vip conntrack.
Реальная причина: Штурвал балансирует трафик на все 4 ноды, но ingress стоит replicas: 2 с hostPort — порт 80/443 открыт только на 2 нодах.
Browser → cloud LB → любая из 4 нод
├── нода с ingress-подом → hostPort 80 → nginx → ✅
└── нода БЕЗ ingress-пода → порт 80 не слушается → RST ❌
50% запросов попадает на пустую ноду → RST.
Подтверждающие данные
Helm ревизии (дамп)
| Ревизия | Время | replicaCount | Другие изменения |
|---|---|---|---|
| 1 (деплой) | 2026-07-11 | 2 | — |
| 8 | 2026-07-15 02:00 | 2 | — |
| 9 | 2026-07-15 11:53 | 2 | — |
Helm не менял replicaCount. Всегда 2. Но ConfigMap сбрасывал на дефолты (keep-alive: 10, proxy-body-size: 8m и т.д.).
Ingress ConfigMap (последствия Helm upgrade)
# Было (ручные правки 2026-07-14) → Стало (после ревизии 9)
keep-alive: "75" → "10" # вернулось на дефолт Штурвала
proxy-body-size: "1024m" → "8m" # тоже сброшено
error-log-level: debug → info # сброшено
upstream-keepalive-connections: "0" → сохранилось
kube-vip svc_election — мёртв
# Lease ingress/kubevip-shturval-ingress-controller-controller
holderIdentity: "" # пусто — никто не держит
leaseDurationSeconds: 1 # аномально короткий (норма 15-30)
renewTime: 2026-07-11T14:55:28Z # 4 дня назад не обновлялся
leaseTransitions: 17 # но было 17 переходов
svc_election никогда не работал. VIP назначен через ipMode: VIP в статусе сервиса, работает на уровне облака (BGP/маршрутизация).
hostPort
# В шаблоне пода ingress (Helm template)
hostPort: 80
hostPort: 443
Включён в ревизиях 8 и 9. hostPort = порт слушается ТОЛЬКО на нодах где стоит под.
podAffinity на drhider — временный костыль
{
"podAffinity": {
"requiredDuringSchedulingIgnoredDuringExecution": [{
"labelSelector": {"matchLabels": {"app.kubernetes.io/name": "shturval-ingress-controller"}},
"namespaceSelector": {"matchLabels": {"name": "ingress"}},
"topologyKey": "kubernetes.io/hostname"
}]
}
}
Добавлен через kubectl edit, НЕ через Helm. Привязывает drhider к ноде с ingress-подом. Это НЕ лечит RST (RST на уровне VIP→нода, не на уровне нода→drhider). При следующем helm upgrade — исчезнет.
Kube-vip DaemonSet — не участвует
vip_arp: true # ARP включён
vip_leaderelection: false # нет CP election
svc_election: true # должен быть — но lease мёртв
Даемоны не логгируют VIP 185.247.187.151. Трафик распределяет облако, не kube-vip.
Решение
Единственное полное решение: ingress-под на каждой ноде куда приходит трафик.
Вариант 1 (рекомендуемый): увеличить replicas
# Helm values
shturval-ingress-controller:
replicaCount: 4
Или выяснить у DevOps сколько нод в LB-пуле Штурвала и поставить replicaCount = число нод.
Вариант 2 (если балансировка на все worker'ы): DaemonSet
# Helm values
shturval-ingress-controller:
kind: DaemonSet
Что делать сейчас (ручной воркараунд)
1. Починить ConfigMap (срочно)
kubectl patch configmap -n ingress shturval-ingress-controller-controller --type merge \
-p '{"data":{"keep-alive":"75","proxy-body-size":"1024m","client-body-timeout":"60","client-header-timeout":"30"}}'
2. Масштабировать ingress (временный фикс)
kubectl scale deploy -n ingress shturval-ingress-controller-controller --replicas=4
Поды раскидаются по всем 4 нодам → hostPort на всех → 0% RST.
⚠️ Предупреждение: после следующего helm upgrade:
- ConfigMap сбросится (нужно править Helm values)
- replicas вернётся на 2
- podAffinity на drhider исчезнет
Вопросы к DevOps
- Сколько нод в LB-пуле Штурвала? (на какие ноды облако направляет трафик ingress?)
- Можно ли увеличить
replicaCountдо числа нод? - Как правильно изменить Helm values для ingress? (чтобы ConfigMap не сбрасывался при upgrade)