ss로 패킷 드롭·재전송 진단하기

애플리케이션 처리량이 갑자기 떨어졌는데 CPU/메모리는 멀쩡하다면 네트워크 재전송을 의심해봐야 한다. 그런데 netstat -snstat은 시스템 전체 누적 카운터만 보여줘서 정작 어떤 연결이 문제인지는 짚어주지 못한다. 이 글에서는 ss -i로 개별 TCP 소켓의 재전송·RTT를 확인하고, 실제로 패킷 손실을 유발해 그 값이 어떻게 변하는지 정리한다.

시스템 전체 재전송 카운터

$ nstat -az | grep -i retrans
TcpRetransSegs                  8                  0.0
TcpExtTCPFastRetrans             6                  0.0
$ netstat -s | grep -i retrans
    8 segments retransmitted
    6 fast retransmits

부팅 이후 누적치라 지금 문제가 되는 연결이 어느 것인지는 알 수 없다. 소켓 단위로 좁히려면 ss -i가 필요하다.

ss -i로 소켓별 진단

먼저 손실이 전혀 없는 정상 연결에서 4GB를 전송하며 같은 명령을 찍어 기준점을 만들었다.

$ ss -ti '( dport = :5201 or sport = :5201 )'
ESTAB 0 360554  127.0.0.1:36344  127.0.0.1:5201
	 cubic wscale:10,10 rto:201 rtt:0.118/0.082 mss:65483 cwnd:19 ssthresh:18
	 bytes_sent:4038590713 bytes_retrans:212 bytes_acked:4038590502
	 segs_out:81958 segs_in:30078 retrans:0/2 dsack_dups:1
	 reordering:10 rcv_space:65495 minrtt:0.004 snd_wnd:1901568

4GB(81958 세그먼트)를 보내는 동안 재전송은 단 2번(bytes_retrans:212)뿐이고 RTT도 0.1ms대로 안정적이다. 아래는 같은 명령을 손실이 있는 연결에서 찍은 결과다.

$ ss -ti '( dport = :5201 or sport = :5201 )'
ESTAB 0 3208667  127.0.0.1:41600  127.0.0.1:5201
	 cubic wscale:10,10 rto:243 rtt:42.583/2.6 mss:65483 cwnd:12 ssthresh:10
	 bytes_sent:20408989 bytes_retrans:949795 bytes_acked:18280501
	 segs_out:348 segs_in:153 retrans:0/17 sacked:6 dsack_dups:1
	 reordering:10 rcv_space:65495 minrtt:41 snd_wnd:20860928

전송량은 20MB로 훨씬 적은데도 재전송이 17번, RTT는 42ms대로 400배 이상 높다. 정상 연결과 나란히 비교하면 이 소켓만 유독 나쁘다는 게 숫자로 바로 드러난다.

필드의미
retrans:0/17현재 미확인 재전송 수 / 이 소켓의 누적 재전송 횟수(17회)
bytes_retrans재전송으로 다시 보낸 누적 바이트 수
rto재전송 타임아웃(ms). RTT 변동이 크면 값도 같이 튄다
rtt:42.583/2.6평균 RTT / RTT 변동폭(mdev), 둘 다 ms 단위
ssthresh혼잡 회피 진입 임계값. 손실 감지 후 cwnd가 이 값까지 줄어든다
dsack_dups중복 SACK 수. 재전송한 세그먼트가 사실은 도착해 있었던 경우(불필요한 재전송) 증가

손실 재현 — netem으로 5% loss 유발

루프백에 tc netem으로 5% 패킷 손실과 20ms 지연을 걸고 iperf3로 6초간 전송하며 위 ss -ti를 다시 확인했다.

$ sudo tc qdisc add dev lo root netem loss 5% delay 20ms
$ iperf3 -c 127.0.0.1 -t 6
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.40   sec  24.2 MBytes   145 Mbits/sec   20    448 KBytes
[  5]   1.40-2.88   sec  9.75 MBytes  55.3 Mbits/sec    2    703 KBytes
[  5]   4.00-5.11   sec  4.75 MBytes  36.1 Mbits/sec    3    384 KBytes
[  5]   5.11-6.35   sec  5.12 MBytes  34.5 Mbits/sec    6    256 KBytes
[SUM]   0.00-6.35   sec  48.8 MBytes  64.4 Mbits/sec   34             sender

$ nstat | grep -i retrans
TcpRetransSegs                  35                 0.0

loss 5%를 걸자 처리량이 145Mbps에서 34.5Mbps까지 떨어졌고, iperf3가 자체 집계한 Retr 합계(34)와 nstatTcpRetransSegs 증가분(35, 시작값 8 포함)이 거의 일치한다. 앞서 본 ss -tibytes_retrans:949795도 이 손실 구간에서 찍힌 값이다.

주의사항

항목내용
retrans 카운트는 연결마다 리셋같은 호스트 간이라도 소켓(4-tuple)이 다르면 별도 카운터다. 문제가 된 정확한 포트 조합으로 필터링해야 한다
root 권한 필요ss -i의 상세 필드는 일반 사용자도 보이지만, 다른 사용자의 소켓까지 보려면 sudo 필요
재전송 ≠ 항상 네트워크 문제수신 측 애플리케이션이 소켓 버퍼를 늦게 비워도(rwnd 소진) 송신 측엔 재전송처럼 보일 수 있다. rwnd_limited 필드로 구분할 것
netem은 테스트 후 반드시 제거tc qdisc del dev lo root로 원복하지 않으면 이후 모든 루프백 트래픽에 손실/지연이 계속 적용된다

마무리

시스템 전체 재전송 수치로 이상 징후를 감지했다면, ss -ti로 해당 소켓을 특정해 retrans·rto·ssthresh를 직접 확인하는 게 다음 단계다. 재전송이 실제 손실 때문인지 수신측 버퍼 문제인지까지 구분되면 튜닝 방향(네트워크 vs 애플리케이션)이 명확해진다.

참고

man7.org – ss(8)

답글 남기기