# Sonnet: найти точку отказа внутри k8s — загрузка >48KB падает ## КОНТЕКСТ Сервис: Flask loadtest (замер скорости загрузки) на Shturval managed k8s. POST /upload с base64-телом >48KB случайно обрывается (TimeoutError на TCP-коннекте). Мелкие файлы — всегда 100%. Требование: починить ВНУТРИ k8s. Без ВМ, без внешнего роутера, без ретраев/костылей. ## ИНФРАСТРУКТУРА ``` Shturval 2.12.1, Cilium CNI, 4 ноды: - control-plane (10.10.102.2) — kube-vip ✅, ingress ❌ - worker-6f74n (10.10.102.3) — kube-vip ✅, ingress ❌ - worker-bhbvs (10.10.102.4) — kube-vip ✅, ingress ✅ (hostPort 443) - worker-v8zq4 (10.10.102.5) — kube-vip ✅, ingress ✅ (hostPort 443) kube-vip: DaemonSet, ARP mode, vip_leaderelection=false, hostNetwork=true Все 4 ноды добавляют VIP 185.247.187.151 на интерфейс и шлют gratuitous ARP nginx ingress: Deployment, 2 реплики, hostPort 443 Service type LoadBalancer, external IP = 185.247.187.151 Ингресс-аннотации: proxy-body-size=1024m, timeouts 120-600s, proxy-request-buffering=off ModSecurity: OWASP CRS 4.10, SecRuleEngine DetectionOnly, SecRequestBodyNoFilesLimit=131072 Flask: dev-сервер (python app.py), порт 5000, НЕ Gunicorn ``` ## ВСЕ ТЕСТЫ ### 1. Базовые (изоляция компонентов) | # | Источник | Цель | Размер | Результат | |---|----------|------|--------|-----------| | 1 | Под pythonk8s | свой localhost:5000 | 100KB base64 (136KB POST) | ✅ 10/10, ~4ms | | 2 | Под pythonk8s | ingress ClusterIP:443 | 136KB POST | ✅ 10/10, ~2ms | | 3 | Под pythonk8s | VIP 185.247.187.151:443 | 136KB POST | ✅ 10/10, 1-6ms | | 4 | Мы (интернет) | VIP 185.247.187.151:443 | 136KB POST | ❌ 3-5/10, TimeoutError | | 5 | Мы (интернет) | прямой IP ВМ 5.172.178.213:443 | 136KB POST | ✅ 0 таймаутов, но "no file" | | 6 | ВМ (10.10.102.21) | VIP 185.247.187.151:443 | 136KB POST | ❌ 1/10 | | 7 | ВМ | worker node :443 | любой | ❌ ConnectionRefused | ### 2. Бинарный поиск предела | Размер | Результат (снаружи→VIP) | |--------|------------------------| | 1-48 KB | ✅ всегда | | 50 KB | ❌ | | 52 KB | ✅ | | 55-80 KB | ❌ | | 100 KB | иногда ✅, иногда ❌ | Жёсткого предела НЕТ — случайность. ### 3. HTTP/1.1 keep-alive (одно соединение) | Тест | Результат | |------|-----------| | 10 запросов на одном TCP-соединении | ✅ 8/10 (первые 2 таймаута при установке, остальные OK) | ### 4. Rapid-fire vs delayed (проверка ARP-стабильности) Rapid (без пауз): сначала 5 таймаутов, потом 3 OK подряд 10s пауза: снова миксуется ## ЧТО ПРОБОВАЛИ (внутри k8s) | Действие | Результат | |----------|-----------| | `proxy_request_buffering: off` (ingress annotation) | было 3/10 → стало 5/10 | | `keep-alive: 10→75` (ingress ConfigMap) | ❌ ухудшило 5→2/10, откачено | | `iptables TCPMSS --set-mss 1200-1360` на ноде | ❌ 3-4/10, откачено | | `vip_leaderelection: true` (kube-vip DS) | ❌ 5→3/10, откачено | | ingress replicaCount 2→3 (scale) | ❌ 3/10, откачено | | Dockerfile Gunicorn field_size | ❌ бесполезно (Gunicorn не используется) | | conntrack проверка | ✅ чистый (54/262K), не проблема | | ModSecurity | ✅ DetectionOnly, не проблема | ## ЧТО НЕ ПРОБОВАЛИ - kube-vip BGP вместо ARP - NodePort/LoadBalancer вместо hostPort - Убрать kube-vip с нод без ingress (nodeSelector/taints) - Увеличить ARP-интервал в kube-vip - Включить strict ARP (kube-vip only on leader node) ## КЛЮЧЕВОЙ ВЫВОД Внутри k8s ВСЁ работает идеально (тесты 1-3). Снаружи — нет (тест 4). Разница: внешний роутер получает ARP от ВСЕХ 4 нод (kube-vip без лидера), включая 2 ноды БЕЗ ingress. Когда роутер выбирает MAC ноды без ingress → connection timeout. ## KUBE-VIP КОНФИГУРАЦИЯ (полностью) ```yaml # DaemonSet: shturval-vip (namespace: kube-vip) Image: r.shturval.tech/kube-vip:v1.0.0 hostNetwork: true env: cp_enable: "false" enable_endpointslices: "true" enable_node_labeling: "true" lb_enable: "true" # LoadBalancer включён lb_port: "6443" svc_election: "true" # выборы для сервисов svc_enable: "true" vip_arp: "true" # ARP-режим (не BGP) vip_interface: "" # пусто = все интерфейсы vip_leaderelection: "false" # ВСЕ ноды анонсят VIP vip_leasename: "sht-uservip-cp-lock" vip_subnet: "32" vip_nodename: "" # из spec.nodeName # ShturvalServiceConfig: shturval-vip spec: chart: shturval-vip version: 2.12.1 mode: auto iscritical: true namespace: kube-vip # Ingress Service (namespace: ingress) type: LoadBalancer externalTrafficPolicy: Cluster status.loadBalancer.ingress.ip: 185.247.187.151 status.loadBalancer.ingress.ipMode: VIP # Lease: kubevip-shturval-ingress-controller-controller (ingress ns) holderIdentity: iot-naeel-workers-vqphm-v8zq4 (с 2026-04-20) ``` ## ВОПРОС kube-vip v1.0.0 ARP mode, vip_leaderelection=false, DaemonSet на 4 нодах, ingress hostPort только на 2. Как заставить VIP 185.247.187.151 всегда вести на ноды с ingress? - `vip_leaderelection: true` не применился через DaemonSet (Shturval не подхватил customvalues) - `svc_election: true` уже есть — почему не работает? - Есть ли способ через `customvalues` в ShturvalServiceConfig правильно включить лидера? - Или нужен `nodeSelector` на DaemonSet чтобы kube-vip был только на нодах с ingress?