Späť na blog

PMTU Blackholes: Keď Iba Veľké Odpovede Visia

|
| kubernetes, networking, mtu, debugging, overlay-networks, tcp

Toto bola cista networking legenda, kym nas to netrafilo. “API funguje pre malé odpovede ale visí pre veľké.” Strávili sme dni pridávaním timeoutov a retries, kým sme si uvedomili že problém je PMTU blackholing—ICMP správy sú filtrované, čo spôsobuje tiché zlyhanie Path MTU Discovery.

Prostredie: Kubernetes 1.28, Calico overlay (VXLAN), cloud provider s defaultnými security groups

Problém

Symptómy Ktoré Nedávajú Zmysel

Vzor ktorý sme pozorovali:

✓ GET /health           → 200 OK (50 bytes)     - funguje
✓ GET /api/user/1       → 200 OK (500 bytes)    - funguje
✗ GET /api/users        → visí navždy (15KB)    - zlyháva
✗ POST /api/upload      → visí (veľké telo)     - zlyháva

Indície:
- Rovnaký endpoint, rôzne veľkosti payloadu
- Hranica okolo 1400-1500 bytes
- Cross-node traffic postihnutý viac
- V dev prostredí fungovalo

Prečo Je Toto Ťažké Debugovať

# Aplikačné logy neukazujú nič užitočné
# Len timeout po 30 sekundách

# tcpdump na odosielateľovi ukazuje pakety odchádzajúce
tcpdump -i eth0 host $DEST_IP
# 10:00:00 IP src > dst: TCP ... length 1460
# 10:00:00 IP src > dst: TCP ... length 1460
# Pakety sa posielajú... ale žiadna odpoveď

# Problém je neviditeľný na aplikačnej vrstve
# Pretože ICMP správy ktoré by signalizovali problém
# sú dropované firewallom niekde po ceste

Príčina

Path MTU Discovery 101

Normálne PMTU Discovery:

┌──────────┐   1500B paket     ┌──────────┐   Nemôže fragmentovať ┌──────────┐
│  Sender  │──────────────────▶│  Router  │────────────────────▶│   Drop   │
└──────────┘                   └──────────┘                     └──────────┘

                                    │ ICMP "Fragmentation Needed"
                                    │ MTU = 1400

┌──────────┐   1400B pakety    ┌──────────┐
│  Sender  │──────────────────▶│  Router  │──────────────────▶ Doručené!
└──────────┘  (po ICMP)        └──────────┘


PMTU Blackhole (pokazené):

┌──────────┐   1500B paket     ┌──────────┐   Nemôže fragmentovať ┌──────────┐
│  Sender  │──────────────────▶│  Router  │────────────────────▶│   Drop   │
└──────────┘                   └──────────┘                     └──────────┘

                                    │ ICMP "Fragmentation Needed"

                              ┌──────────┐
                              │ Firewall │──▶ DROPPED (ICMP filtrované)
                              └──────────┘

Výsledok: Sender sa nikdy nedozvie o MTU probléme
          Stále posiela 1500B pakety
          Spojenie visí navždy

Overlay Sieť To Zhoršuje

Fyzické MTU: 1500 bytes

S VXLAN overlay:
┌─────────────────────────────────────────────────┐
│ Originálna IP hlavička (20B) + TCP (20B) + Data │
└─────────────────────────────────────────────────┘

                    │ VXLAN enkapsulácia pridáva:
                    │ - Vonkajšia IP hlavička: 20 bytes
                    │ - UDP hlavička: 8 bytes
                    │ - VXLAN hlavička: 8 bytes
                    │ - Vonkajší Ethernet: 14 bytes

┌─────────────────────────────────────────────────┐
│ Vonkajšie hlavičky (50B) + Originálny paket (1450B) │
│ = 1500 bytes (akurát sa zmestí!)                     │
└─────────────────────────────────────────────────┘

Ale ak je originálny paket mierne väčší:
┌─────────────────────────────────────────────────┐
│ Vonkajšie hlavičky (50B) + Originál (1460B) = 1510B │
│ PREKRAČUJE MTU → potrebuje fragmentáciu alebo ICMP  │
└─────────────────────────────────────────────────┘

Diagnostika

Krok 1: Identifikuj Hranicu

# Nájdi presnú veľkosť kde veci prestávajú fungovať
for size in 1000 1200 1400 1450 1480 1500; do
  echo -n "Veľkosť $size: "
  timeout 5 curl -s -o /dev/null -w "%{http_code}" \
    "http://$SERVICE_IP/api/generate?size=$size" || echo "TIMEOUT"
done

# Výstup:
# Veľkosť 1000: 200
# Veľkosť 1200: 200
# Veľkosť 1400: 200
# Veľkosť 1450: TIMEOUT  <-- Hranica nájdená!
# Veľkosť 1480: TIMEOUT
# Veľkosť 1500: TIMEOUT

Krok 2: Skontroluj ICMP Filtrovanie

# Z podu, skús či ICMP je dosiahnuteľné
kubectl exec -it $POD -- ping -c 3 -s 1472 -M do $DEST_IP

# Ak vidíš:
# ping: local error: message too long, mtu=1450
# To je dobré - PMTU funguje lokálne

# Ale ak pakety len miznú cez nody:
kubectl exec -it $POD -- tracepath $DEST_IP
# Hľadaj "asymm" alebo "no reply" záznamy

Krok 3: Zachyť Chýbajúce ICMP

# Na cieľovom node, zachytávaj ICMP
tcpdump -i any icmp

# Na zdrojovom node, pošli veľký ping
kubectl exec -it $POD -- ping -c 1 -s 1472 -M do $DEST_IP

# Ak sa žiadne ICMP neobjaví na zdroji, je filtrované niekde

Krok 4: Skontroluj MTU Po Ceste

#!/bin/bash
# pmtu-probe.sh - Nájdi skutočné MTU

TARGET_IP=$1
MAX_SIZE=1500
MIN_SIZE=1000

while [ $((MAX_SIZE - MIN_SIZE)) -gt 1 ]; do
  MID=$(( (MAX_SIZE + MIN_SIZE) / 2 ))

  # -M do = nefragmentuj, -s = veľkosť payloadu (mínus 28 pre IP+ICMP hlavičky)
  if ping -c 1 -W 2 -M do -s $((MID - 28)) $TARGET_IP > /dev/null 2>&1; then
    MIN_SIZE=$MID
    echo "Veľkosť $MID: OK"
  else
    MAX_SIZE=$MID
    echo "Veľkosť $MID: FAIL"
  fi
done

echo "Skutočné MTU: $MIN_SIZE"

Riešenie

Možnosť 1: MSS Clamping na CNI

# Pre Calico - nakonfiguruj MSS clamping
apiVersion: crd.projectcalico.org/v1
kind: FelixConfiguration
metadata:
  name: default
spec:
  # Obmedz MSS aby sa predišlo priveľkým paketom
  mtuIfacePattern: "^(eth|en).*"
  # Nastav MTU explicitne pre overlay
  ipipMTU: 1440
  vxlanMTU: 1450
  wireguardMTU: 1420

# Toto povie TCP session aby vyjednali menšie segmenty
# Žiadne ICMP nie je potrebné!

Možnosť 2: Oprav Security Group Pravidlá

# AWS Security Group - povol ICMP type 3 (Destination Unreachable)
aws ec2 authorize-security-group-ingress \
  --group-id sg-xxx \
  --protocol icmp \
  --port 3 \
  --cidr 10.0.0.0/8

# GCP Firewall - povol ICMP
gcloud compute firewall-rules create allow-icmp-pmtu \
  --direction=INGRESS \
  --priority=1000 \
  --network=default \
  --action=ALLOW \
  --rules=icmp \
  --source-ranges=10.0.0.0/8

Možnosť 3: Nastav MTU Rozhrania Explicitne

# DaemonSet na nastavenie MTU na všetkých nodoch
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: set-mtu
  namespace: kube-system
spec:
  selector:
    matchLabels:
      app: set-mtu
  template:
    metadata:
      labels:
        app: set-mtu
    spec:
      hostNetwork: true
      initContainers:
        - name: set-mtu
          image: alpine
          securityContext:
            privileged: true
          command:
            - /bin/sh
            - -c
            - |
              # Nájdi pod interface (zvyčajne vxlan.calico alebo podobné)
              for iface in vxlan.calico flannel.1 cilium_vxlan; do
                if ip link show $iface 2>/dev/null; then
                  ip link set $iface mtu 1450
                  echo "Nastavené $iface MTU na 1450"
                fi
              done
      containers:
        - name: pause
          image: gcr.io/google_containers/pause:3.2

Možnosť 4: TCP MSS Prepisovanie cez iptables

# Na každom node, obmedz MSS pre všetku pod traffic
iptables -t mangle -A POSTROUTING \
  -p tcp --tcp-flags SYN,RST SYN \
  -o vxlan.calico \
  -j TCPMSS --clamp-mss-to-pmtu

# Alebo nastav explicitnú hodnotu
iptables -t mangle -A POSTROUTING \
  -p tcp --tcp-flags SYN,RST SYN \
  -o vxlan.calico \
  -j TCPMSS --set-mss 1360

Monitoring

Prometheus Pravidlá

groups:
  - name: pmtu
    rules:
      # Alert na vysoké TCP retransmisie (symptóm PMTU problémov)
      - alert: HighTCPRetransmissions
        expr: |
          rate(node_netstat_Tcp_RetransSegs[5m]) /
          rate(node_netstat_Tcp_OutSegs[5m]) > 0.01
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "Vysoké TCP retransmisie na {{ $labels.instance }}"
          description: "Môže indikovať PMTU blackholing"

      # Alert na ICMP unreachables (dobré - znamená že PMTU funguje)
      - alert: ICMPUnreachableSpike
        expr: |
          rate(node_netstat_Icmp_InDestUnreachs[5m]) > 100
        for: 5m
        labels:
          severity: info
        annotations:
          summary: "ICMP Destination Unreachable špička na {{ $labels.instance }}"

Checklist

## PMTU Blackhole Diagnostika

### Symptómy
- [ ] Veľké odpovede visia, malé fungujú
- [ ] Hranica okolo 1400-1500 bytes
- [ ] Cross-node traffic horší ako same-node
- [ ] Funguje v non-overlay prostrediach

### Diagnostika
- [ ] Nájdi presnú veľkostnú hranicu
- [ ] Skontroluj či ICMP type 3 je povolené
- [ ] Over MTU na overlay rozhraniach
- [ ] Skontroluj cloud security groups pre ICMP

### Riešenia
- [ ] Zapni MSS clamping na CNI úrovni
- [ ] Povol ICMP type 3 v security groups
- [ ] Nastav explicitné MTU na overlay rozhraniach
- [ ] Pridaj iptables MSS prepisovanie

### Verifikácia
- [ ] Testuj s veľkými payloadmi po oprave
- [ ] Monitoruj TCP retransmisie
- [ ] Over že ICMP správy prúdia

Záver

PMTU blackholes sú zákerné pretože:

  1. Malé requesty fungujú - dáva falošnú istotu
  2. Žiadne chyby sa neobjavia - len timeouty
  3. ICMP filtrovanie je neviditeľné - nevidíš čo je dropnuté
  4. Overlay enkapsulácia znižuje efektívne MTU - problém sa objaví až po deployi

Fix je jednoduchý (MSS clamping alebo povoliť ICMP), ale diagnostika vyžaduje pochopenie interakcie medzi overlay sieťami, PMTU discovery a firewall pravidlami.


Súvisiace články

Súvisiace články

Citujte tento článok

Ak na článok odkazujete, pridajte pôvodnú URL a uveďte autora.

Michal Drozd. "PMTU Blackholes: Keď Iba Veľké Odpovede Visia". https://www.michal-drozd.com/sk/blog/pmtu-blackhole-velke-odpovede/ (Publikované 7. novembra 2024).