26A23a

Young-Kyoo Kim·2026년 4월 22일

これは非常に重要な発見です。問題の方向が完全に逆でした。


問題の再定義

지금까지 의심했던 것:
  Client → SYN → Server → [여기서 drop?]

실제 발생하는 것:
  Client → SYN ──────────────→ Server  ✓
  Client ←── SYN-ACK ←─────── Server
       ↑
       Client에 도달하지 않음 ✗

tcp_check_req DROP은 결과였음:
  Client가 SYN-ACK를 못 받음
  → Client가 SYN 재전송
  → Server에 이미 다른 request_sock 존재
  → 뒤늦게 도착한 ACK → tcp_check_req silent DROP

진짜 문제는 SYN-ACK의 귀환 경로(return path)입니다.


BGP + ECMP 환경에서 return path drop의 핵심 원인

Server가 SYN-ACK를 보낼 때, 목적지(Client IP)에 대한 라우팅을 다시 계산합니다. ECMP 환경에서는 이 경로가 SYN이 들어온 경로와 다를 수 있습니다.

정상 케이스:
  Client ──[경로A, 스위치P]──→ Server
  Client ←─[경로A, 스위치P]── Server (같은 경로 역방향)

문제 케이스 (1% 발생):
  Client ──[경로A, 스위치P]──→ Server
  Client ←─[경로B, 스위치Q]── Server
              ↑
              스위치Q에서 Client IP를 모르거나
              엉뚱한 노드로 전달하거나
              drop

확인해야 할 지점들

1. Server 송신 경로 확인 (가장 먼저)

# Server에서 Client IP로의 라우팅 확인
ip route get <client_IP>

# ECMP multipath가 있는 경우 여러 next-hop이 보임
ip route show <client_노드_CIDR>

# 실제 SYN-ACK가 어느 인터페이스로 나가는지
# pwru를 Server의 TX 방향으로 실행
pwru --filter-dst-ip <client_IP> --filter-port 22 \
     --output-tuple 2>&1 | grep -E 'dev_queue_xmit|fib_select_path|neigh_output'

2. ECMP next-hop이 매번 바뀌는지 확인

# 같은 Client IP로 반복 조회 시 next-hop이 동일한지 확인
for i in $(seq 1 10); do
  ip route get <client_IP> | grep via
done
# 결과가 매번 동일하면 ECMP per-flow
# 다르면 per-packet ECMP → 이게 원인

3. BGP 라우팅 테이블에서 Client IP 경로 확인

# Client 노드 IP에 대한 BGP 경로
cilium bgp routes available ipv4 unicast | grep <client_노드_IP>

# 경로가 여러 개이면서 next-hop이 다른 경우 문제
# ClusterMesh 환경에서 같은 IP가 여러 클러스터에서 광고되는 경우도 있음

4. Server 측 TX에서 pwru로 SYN-ACK 추적

# Server에서 실행 - SYN-ACK가 어디까지 가는지 TX 방향 추적
pwru --filter-src-ip <server_IP> \
     --filter-dst-ip <client_IP> \
     --filter-port 22 \
     --output-tuple

# 확인 포인트:
# ip_output → ip_finish_output → dev_queue_xmit 까지 보이는지
# 어느 dev(인터페이스)로 나가는지

5. Client 측에서 SYN-ACK 도달 여부 NIC 레벨 확인

# Client의 물리 NIC 각각에서 캡처 (bond 아닌 슬레이브 직접)
tcpdump -i ens5f0np0 -nn \
  'src host <server_IP> and tcp[tcpflags] & (tcp-syn|tcp-ack) == (tcp-syn|tcp-ack)' &

tcpdump -i ens5f1np1 -nn \
  'src host <server_IP> and tcp[tcpflags] & (tcp-syn|tcp-ack) == (tcp-syn|tcp-ack)' &

# NIC 레벨에서도 안 보이면 → 스위치까지 못 간 것
# 특정 슬레이브에서만 보이면 → bond 수신 분산 문제

6. Server NIC 카운터로 실제 TX 확인

# SYN-ACK가 실제로 wire에 나갔는지
watch -n 1 'ethtool -S ens5f1np1 | grep -E "tx_packets|tx_bytes"'

# 연결 실패 시점에 TX 카운터가 올라가는지 확인

가장 가능성 높은 원인 두 가지

원인 A — L3 스위치의 per-packet ECMP

스위치가 flow 단위가 아닌 packet 단위로 ECMP 분산을 하면, SYN-ACK가 Client와 연결되지 않은 다른 uplink로 나가 소실됩니다. 1%라는 확률은 특정 패킷 크기나 타이밍에서만 해싱 결과가 달라지는 경우와 일치합니다.

원인 B — BGP route flap 또는 광고 경쟁

ClusterMesh 환경에서 Client 노드 IP에 대한 BGP 경로가 순간적으로 변경되거나, 두 클러스터에서 동일 IP 대역을 광고 중이라면 SYN-ACK가 엉뚱한 노드로 전달될 수 있습니다.

# BGP route 변동 확인
cilium bgp routes available ipv4 unicast 2>&1 | \
  grep <client_노드_CIDR> | sort | uniq -c
# 같은 CIDR에 next-hop이 2개 이상이면 경쟁 상태

ip route get <client_IP> 결과와 cilium bgp routes 결과를 공유해 주시면 어느 쪽인지 바로 특정할 수 있습니다.

0개의 댓글