애플리케이션 처리량이 갑자기 떨어졌는데 CPU/메모리는 멀쩡하다면 네트워크 재전송을 의심해봐야 한다. 그런데 netstat -s나 nstat은 시스템 전체 누적 카운터만 보여줘서 정작 어떤 연결이 문제인지는 짚어주지 못한다. 이 글에서는 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)와 nstat의 TcpRetransSegs 증가분(35, 시작값 8 포함)이 거의 일치한다. 앞서 본 ss -ti의 bytes_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 애플리케이션)이 명확해진다.