Files
loadtest/History/sonnet-question-kubevip-detailed.md
T
naeel 91d717585a
Deploy loadtest / validate (push) Waiting to run
fix: debug=False, force read body, bump 1.0.6→1.0.7
2026-07-10 22:59:56 +04:00

128 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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?