これは非常に重要な発見です。問題の方向が完全に逆でした。
지금까지 의심했던 것:
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)입니다.
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
# 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'
# 같은 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 → 이게 원인
# Client 노드 IP에 대한 BGP 경로
cilium bgp routes available ipv4 unicast | grep <client_노드_IP>
# 경로가 여러 개이면서 next-hop이 다른 경우 문제
# ClusterMesh 환경에서 같은 IP가 여러 클러스터에서 광고되는 경우도 있음
# 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(인터페이스)로 나가는지
# 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 수신 분산 문제
# 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 결과를 공유해 주시면 어느 쪽인지 바로 특정할 수 있습니다.