# Варианты решения ingress replicas + TCP stall **Дата:** 2026-07-13 **Источники:** Sonnet analysis, Opus analysis **Связано:** [KUBEVIP-EXTERNAL-TRAFFIC-POLICY.md](KUBEVIP-EXTERNAL-TRAFFIC-POLICY.md) --- ## Ключевой вывод (оба согласны) Проблемы **две независимые**, решать по отдельности: 1. **TCP stall 51с** — PMTU Blackhole на Geneve-туннеле, а не свойство `Cluster`-политики. `Local` лечит как побочный эффект (убирает cross-node). 2. **Сброс реплик** — `kubectl scale` обходит Helm, поэтому реконсиляция откатывает. > ⚠️ **Важно (Opus):** `kubectl scale` вы обходите Helm, поэтому и сбрасывается. Любое итоговое решение должно быть в **values чарта**, а не через `kubectl`. --- ## Варианты ### 1. DaemonSet (рекомендуется) Одно изменение в Helm values: ```yaml controller: kind: DaemonSet # replicaCount игнорируется ``` - ✅ Автоматически 1 под/ноду - ✅ Helm не ломает - ✅ Стандарт для bare-metal / kube-vip - ✅ Не требует менять сетевую конфигурацию ### 2. Починить MTU (убрать stall в корне) 51с — это классический Path MTU Discovery Blackhole. Geneve: MTU 1500 → ~1450. **Диагностика:** ```bash ip link show | grep -E "cilium|genev" iptables -t mangle -L FORWARD -n -v | grep TCPMSS ``` **Исправление:** ```bash iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu ``` Если stall устранён → можно вернуть `externalTrafficPolicy: Cluster` и replicaCount 2. ### 3. Cilium DSR (Direct Server Return) Ответный трафик идёт напрямую от пода к клиенту, без cross-node SNAT. ```yaml # cilium-config (требует kube-proxy replacement = strict) loadBalancer: mode: dsr kubeProxyReplacement: strict ``` - ❌ Долго, меняет всю сетевую конфигурацию ### 4. replicaCount: 4 + podAntiAffinity Если Штурвал не даёт сменить `kind` на DaemonSet: ```yaml controller: replicaCount: 4 affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchLabels: app.kubernetes.io/name: ingress-nginx topologyKey: kubernetes.io/hostname ``` - ✅ Гарантирует 1 под/ноду - ✅ Helm values зафиксированы, не сбрасывается --- ## Рекомендуемый путь (Sonnet + Opus) | Приоритет | Действие | Эффект | |-----------|----------|--------| | 🟢 Сразу | `controller.kind: DaemonSet` в Helm values | Убирает костыль с репликами навсегда | | 🟡 Параллельно | Диагностика MTU + MSS clamping | Устраняет stall в корне | | 🔵 Потом | Если MTU починен — вернуть `Cluster` | Можно уйти от `Local` и требования под/ноду | ## Как передать девопсу (Opus) Для чарта `shturval-ingress-controller-2.12.1`: ```bash # Вариант A: DaemonSet helm upgrade -n ingress shturval-ingress-controller <чарт> \ --set controller.kind=DaemonSet # Вариант B: replicaCount + antiAffinity (если DaemonSet недоступен) helm upgrade -n ingress shturval-ingress-controller <чарт> \ --set controller.replicaCount=4 \ --set controller.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution[0].labelSelector.matchLabels.app\.kubernetes\.io/name=ingress-nginx \ --set controller.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution[0].topologyKey=kubernetes.io/hostname ``` Либо через Штурвал UI — изменить `replicaCount: 4` в настройках чарта. ## Диагностика MTU (Opus) ```bash # реальный MTU туннеля ip link show | grep -E "cilium|genev" # есть ли MSS clamping iptables -t mangle -L FORWARD -n -v | grep MSS ``` Варианты фикса: - MSS clamping: `iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu` - Cilium DSR: `loadBalancer.mode: dsr` + `kubeProxyReplacement: strict` - Выставить MTU в Cilium под физическую сеть (Geneve overhead ~50 байт) --- ## Текущий статус `externalTrafficPolicy: Local` — не сбрасывается. `replicas: 4` — ручной scale, периодически сбрасывается Helm на 2.