tc/netem으로 네트워크 장애 재현하기

애플리케이션이 네트워크 장애 상황(지연 증가, 패킷 유실, 순서 뒤바뀜)에서도 제대로 재시도·타임아웃 처리를 하는지 확인하려면 실제로 그런 상황을 만들어봐야 하는데, 막상 회선을 끊거나 라우터를 조작하는 식으로 재현하기는 번거롭다. 리눅스 커널에는 이런 상황을 소프트웨어적으로 주입할 수 있는 네트워크 에뮬레이터 netemtc(traffic control)의 큐잉 규칙(qdisc) 중 하나로 내장돼 있다. 이 글에서는 tc qdisc로 지연·유실·중복·손상을 직접 주입하고 실제 ping/iperf3로 그 효과를 측정한 결과를 정리한다.

netem 기본 사용법

특정 인터페이스에 qdisc로 추가·수정·삭제하는 게 기본 흐름이다.

# 추가
sudo tc qdisc add dev <iface> root netem <옵션>
# 옵션 변경(재추가 없이)
sudo tc qdisc change dev <iface> root netem <새 옵션>
# 상태 확인
tc -s qdisc show dev <iface>
# 제거(원상 복구)
sudo tc qdisc del dev <iface> root

주요 옵션은 다음과 같다.

옵션예시효과
delaydelay 100ms 20ms고정 지연(+ 지터). 뒤에 distribution normal 등을 붙여 분포 지정 가능
lossloss 1%지정 확률로 패킷 드롭
duplicateduplicate 1%지정 확률로 패킷 복제 전송
corruptcorrupt 0.1%패킷 내용 임의 비트 반전
reorderdelay 10ms reorder 25% 50%일부 패킷만 지연 없이 먼저 보내 순서를 뒤섞음(반드시 delay와 함께 사용)

실측: 지연 주입

VM 루프백(lo)에 100ms 지연을 걸고 ping 127.0.0.1로 확인했다. 루프백은 송신·수신 양방향 모두 같은 lo 인터페이스를 거치므로 왕복(RTT)에는 설정값의 2배가 더해진다.

$ sudo tc qdisc add dev lo root netem delay 100ms
$ ping -c 5 127.0.0.1
rtt min/avg/max/mdev = 210.686/746.466/1023.973/298.922 ms

최소값(210ms)은 예상대로 설정값의 2배(200ms) 근처에서 나왔지만, 평균·최대값이 훨씬 크고 편차도 심하다. 원인은 dmesg에서 바로 확인됐다.

$ dmesg | grep -i tsc
tsc: Measured 119319 cycles TSC warp between CPUs, turning off TSC clock.
tsc: Marking TSC unstable due to check_tsc_sync_source failed
clocksource: Switched to clocksource kvm-clock

중첩 가상화(호스트 Hyper-V/WSL2 위의 VirtualBox VM) 환경이라 TSC가 코어 간 동기화되지 않아 커널이 정밀도가 낮은 kvm-clock으로 폴백했고, netem의 지연 스케줄링이 그 영향을 그대로 받았다. 실제 물리 서버(TSC 안정)에서는 이런 편차 없이 설정값에 훨씬 가깝게 나온다 — 이 환경에서는 “지연이 걸린다”는 정성적 확인 용도로 쓰고, 정밀한 지연 수치 검증이 필요하면 클라우드 인스턴스처럼 TSC가 안정적인 호스트에서 재현하는 게 좋다.

실측: 유실·중복·손상

유실은 확률이 훨씬 예측 가능하게 재현된다. 30% loss를 걸면 왕복 모두 살아남을 확률은 0.7 × 0.7 ≈ 49%이므로 약 51% 손실이 기대되는데, 실측도 정확히 그 근처로 나왔다.

$ sudo tc qdisc add dev lo root netem loss 30%
$ ping -c 20 -i 0.2 127.0.0.1
--- 127.0.0.1 ping statistics ---
20 packets transmitted, 10 received, 50% packet loss, time 4032ms

중복은 pingDUP! 표시로 바로 알려준다.

$ sudo tc qdisc add dev lo root netem duplicate 50%
$ ping -c 8 -i 0.3 127.0.0.1
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=15.0 ms
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=15.0 ms (DUP!)
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.041 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.041 ms (DUP!)
...
--- 127.0.0.1 ping statistics ---
8 packets transmitted, 8 received, +7 duplicates, 0% packet loss, time 3235ms

손상(corrupt)은 겉보기에는 유실과 똑같이 보인다.

$ sudo tc qdisc add dev lo root netem corrupt 10%
$ ping -c 15 -i 0.2 127.0.0.1
--- 127.0.0.1 ping statistics ---
15 packets transmitted, 14 received, 6.66667% packet loss, time 3747ms

손상시켰는데 “loss”로 집계된 이유는, 비트가 뒤집힌 패킷 대부분이 IP/ICMP 체크섬 검증에서 걸러져 커널이 애초에 상위 계층으로 올리지 않고 드롭하기 때문이다. 즉 corrupt는 애플리케이션 입장에서 유실과 구분하기 어려운 경우가 많고, 체크섬을 우회하는 극히 드문 케이스에서만 진짜 손상된 데이터가 그대로 전달된다.

실측: TCP 처리량에 대한 영향

패킷 유실이 TCP 처리량에 미치는 영향은 체감보다 훨씬 크다. 같은 루프백에서 iperf3로 기준치를 잰 다음, 유실률을 단 1%만 걸고 다시 측정했다.

$ iperf3 -c 127.0.0.1 -t 4
[  5]   0.00-4.00   sec  13.6 GBytes  29.3 Gbits/sec    1             sender

$ sudo tc qdisc add dev lo root netem loss 1%
$ iperf3 -c 127.0.0.1 -t 4
[  5]   0.00-4.00   sec  3.98 GBytes  8.55 Gbits/sec  966             sender

1%의 유실만으로 처리량이 29.3Gbps에서 8.55Gbps로 3분의 1 이하로 줄고 재전송(Retr)이 966회 발생했다. TCP 혼잡 제어(cubic)가 유실을 감지할 때마다 혼잡 윈도를 큰 폭으로 줄이기 때문에, 대역폭-지연 곱이 큰 고속 구간일수록 적은 유실률로도 처리량이 급격히 무너진다 — 장거리 회선에서 체감 속도가 이론 대역폭보다 훨씬 낮게 나오는 흔한 원인 중 하나다.

주의사항

항목내용
루프백은 왕복 기준 2배lo는 송신·수신이 같은 인터페이스를 거치므로 RTT에는 설정값의 2배가 반영된다. 실제 네트워크 인터페이스(예: eth0)에서는 편도로만 적용된다
중첩 가상화의 타이머 정밀도TSC가 불안정한 VM에서는 delay 수치가 설정값과 다르게 튈 수 있다(이번 실측에서 확인). 정밀 측정은 물리 서버나 클라우드 인스턴스에서 진행할 것
모듈 미탑재 커널 주의sch_netem 모듈이 빌드에서 빠진 커널(커스텀 빌드 등)에서는 tc qdisc add ... netem이 “Specified qdisc kind is unknown”으로 실패한다. modprobe sch_netem 성공 여부를 먼저 확인할 것
실험 후 반드시 원복tc qdisc del dev <iface> root를 빼먹으면 해당 인터페이스 전체 트래픽에 지연·유실이 계속 남는다. 서버·프로덕션 환경에서는 특히 주의
reorder는 delay 필수reorder만 단독으로는 효과가 없고, 일부 패킷만 지연시켜 앞지르게 하는 방식이라 delay와 함께 설정해야 한다

마무리

tc netem은 별도 장비나 네트워크 조작 없이 커널 레벨에서 지연·유실·중복·손상을 재현할 수 있어, 장애 상황에서 애플리케이션의 재시도/타임아웃 로직이 실제로 동작하는지 검증하는 용도로 유용하다. 특히 유실률 1%만으로 처리량이 3분의 1로 줄어드는 걸 직접 보고 나면, 장거리 구간에서 재전송 튜닝이나 혼잡 제어 알고리즘 선택이 왜 중요한지 체감하기 쉽다.

참고

man7.org – tc-netem(8)

답글 남기기