네트워크 성능 분석: iperf3, fio 연동, 병목 진단
대역폭은 10Gbps인데 왜 백업은 300MB/s에서 멈출까
새 데이터베이스 서버의 NIC와 스위치 포트는 모두 10Gbps로 연결됐다. 그런데 원격 백업은 300MB/s 부근에서 더 올라가지 않는다. ping은 0.4ms이고 패킷 손실도 없다. 이때 곧바로 네트워크를 의심해 TCP 버퍼를 키우면 문제를 풀기보다 측정 조건만 바꿀 수 있다. 병목은 디스크 읽기, 단일 TCP 흐름, 한 CPU의 softirq, 가상 머신의 PPS 한도, 반대 방향 링크 중 어디에나 있을 수 있다.
네트워크 성능 분석의 핵심은 도구 하나가 아니라 경로를 분해하고, 한 번에 변수 하나만 바꾸고, 양 끝단을 동시에 관찰하는 절차다. 이 장에서는 iperf3로 순수 네트워크 상한을 측정하고, fio로 스토리지 상한을 분리한 뒤, 둘을 함께 실행해 공유 자원 병목을 찾는 방법을 다룬다.
먼저 구분해야 할 성능 지표
Gbps 숫자 하나로는 병목을 설명할 수 없다. 적어도 다음 지표를 나눠 봐야 한다.
| 지표 | 의미 | 운영에서 묻는 질문 |
|---|---|---|
| Link speed | NIC와 상대 장비가 협상한 물리 링크 속도 | 정말 10Gbps로 링크가 올라왔는가 |
| Throughput | 단위 시간에 경로가 처리한 전체 비트 | 링크와 장비가 얼마나 운반했는가 |
| Goodput | 애플리케이션이 실제로 받은 유효 데이터 | 백업이나 복제가 실제로 얼마나 전진했는가 |
| PPS | 초당 패킷 수 | 작은 패킷에서 CPU나 클라우드 PPS 한도를 넘는가 |
| RTT | 요청이 왕복하는 시간 | 단일 흐름의 TCP window가 경로를 채우기에 충분한가 |
| Jitter | 패킷 지연의 변동 | 실시간·UDP 트래픽이 흔들리는가 |
| Loss | 손실된 패킷 비율 | 큐, 물리 링크, 정책, 클라우드 한도에서 드롭되는가 |
| Retransmit | TCP가 다시 보낸 세그먼트 | 손실 때문에 congestion window가 줄어드는가 |
| CPU/softirq | 패킷 처리를 위해 소비한 CPU | 네트워크보다 한 코어가 먼저 포화되는가 |
iperf3가 보여 주는 전송률은 애플리케이션 관점의 측정값이다. 이더넷, IP, TCP 헤더와 재전송 비용이 있으므로 물리 링크 속도와 정확히 같을 필요는 없다. 특히 작은 패킷에서는 비트 전송률보다 PPS와 CPU가 먼저 한계에 닿을 수 있다.
측정 전에 고정할 조건
성능 테스트는 재현 가능해야 비교할 수 있다. 명령을 실행하기 전에 다음 조건을 기록한다.
- 송신·수신 호스트, 인터페이스, IP, 가용 영역, 라우팅 경로
- NIC link speed와 duplex, 드라이버·펌웨어, MTU
iperf3,fio, 커널 버전- 테스트 시작 시각, 지속 시간, 반복 횟수
- 정방향과 역방향, 단일 스트림과 다중 스트림
- 같은 시간에 실행 중인 백업, compaction, checkpoint, 배치 작업
- CPU governor, 가상 머신 크기, 클라우드 네트워크·스토리지 한도
프로덕션에서는 대역폭을 전부 쓰는 테스트를 바로 실행하지 않는다. 낮은 목표 속도부터 단계적으로 올리고, 변경 승인과 중단 기준을 정한다. iperf3의 UDP -b 0은 bitrate 제한을 없애므로 특히 위험하다.
iperf3로 순수 네트워크 상한 측정하기
기본 TCP 테스트
수신 측에서 서버를 실행한다. 기본 제어·데이터 포트는 TCP 5201이다.
# 수신 측
iperf3 -s
# 송신 측: 3초 워밍업을 통계에서 제외하고 30초 측정
iperf3 -c 10.20.0.12 -t 30 -O 3 -i 1 --get-server-output-O 3은 TCP slow start 초반 3초를 통계에서 제외한다. 짧은 테스트는 순간 burst나 slow start의 영향을 크게 받으므로 30초 이상 실행하고 여러 번 반복한다. 결과에서 송신 측 Retr와 송신·수신 측 bitrate 차이를 함께 본다.
자동 수집에는 JSON을 사용한다.
iperf3 -c 10.20.0.12 -t 30 -O 3 -J \
> iperf3-forward-$(date +%Y%m%d-%H%M%S).json실시간 파이프라인에는 line-delimited JSON을 내보내는 --json-stream도 사용할 수 있다. 다만 옵션 지원 여부는 설치된 버전으로 확인한다.
방향과 흐름 수를 바꿔 병목을 좁힌다
# 서버가 클라이언트로 전송: 반대 방향 경로 확인
iperf3 -c 10.20.0.12 -R -t 30 -O 3
# 양방향을 동시에 사용
iperf3 -c 10.20.0.12 --bidir -t 30 -O 3
# 4개 TCP 흐름
iperf3 -c 10.20.0.12 -P 4 -t 30 -O 3
# 특정 출발 IP/인터페이스 경로 고정
iperf3 -c 10.20.0.12 -B 10.10.0.21 -t 30 -O 3해석은 숫자의 크기보다 비교 관계가 중요하다.
- 정방향만 느리면 송신 측 egress, 수신 측 ingress, 해당 방향 라우팅을 본다.
- 역방향만 느리면 반대쪽 NIC offload, CPU, 클라우드 방향별 대역폭 한도를 의심한다.
- 단일 스트림은 느리고
-P 4가 빠르면 한 흐름의 TCP window, congestion control, CPU 또는 RSS 분산 문제가 후보가 된다. - 양방향 동시 실행에서만 급락하면 full-duplex 자체보다 CPU, 메모리 대역폭, shared virtual NIC 한도를 먼저 확인한다.
iperf3 3.16부터는 테스트 스트림마다 별도 스레드를 사용한다. 그보다 오래된 버전은 단일 스레드 제약 때문에 고속 링크에서 한 CPU가 먼저 포화될 수 있다. 결과를 비교할 때 양 끝의 iperf3 --version을 반드시 남긴다.
UDP는 손실이 시작되는 지점을 찾는 테스트다
# 100Mbps부터 시작해 단계적으로 올린다
iperf3 -c 10.20.0.12 -u -b 100M -t 30 -O 3
iperf3 -c 10.20.0.12 -u -b 500M -t 30 -O 3
iperf3 -c 10.20.0.12 -u -b 1G -t 30 -O 3UDP의 기본 목표 bitrate는 1Mbit/s이므로 -b를 빠뜨리면 고속 경로를 거의 시험하지 못한다. 반대로 -b 0은 제한을 해제한다. 운영망에서는 목표 속도를 정해 올리면서 loss와 jitter가 어느 구간에서 증가하는지 찾는 편이 안전하다.
TCP와 UDP 결과는 같은 의미가 아니다. TCP는 손실을 재전송하고 congestion control로 속도를 줄인다. UDP는 재전송하지 않으므로 큐와 PPS 한계를 더 직접적으로 드러내지만 애플리케이션이 허용할 수 없는 손실을 만들 수 있다.
RTT와 TCP window: BDP를 계산해야 하는 이유
한 TCP 흐름이 경로를 가득 채우려면 ACK를 기다리는 동안 충분한 데이터를 전송 중 상태로 유지해야 한다. 필요한 양을 Bandwidth-Delay Product라고 한다.
BDP(bytes) = bandwidth(bits/s) × RTT(seconds) ÷ 8예를 들어 10Gbps 경로의 RTT가 20ms라면 BDP는 25,000,000bytes, 약 25MB다. 송수신 socket buffer나 TCP가 확장할 수 있는 window가 이보다 작으면 패킷 손실이 없어도 단일 흐름은 10Gbps를 채우지 못한다.
# 실제 연결의 RTT, rttvar, cwnd, pmtu, retrans 상태
ss -tin dst 10.20.0.12
# iperf3에서 socket buffer/window를 실험적으로 지정
iperf3 -c 10.20.0.12 -w 32M -t 30 -O 3-w는 양쪽 socket buffer 크기에 영향을 준다. Linux에서는 요청값과 커널이 보고하는 유효 크기가 다를 수 있으므로 ss -ti와 sysctl net.ipv4.tcp_rmem net.ipv4.tcp_wmem을 함께 확인한다. 한 번의 결과만 보고 전역 TCP buffer를 크게 올리지 않는다. 메모리 소비와 수많은 동시 연결에 미치는 영향을 먼저 계산해야 한다.
fio와 연동해 네트워크와 스토리지를 분리한다
백업, 복제 초기 적재, 오브젝트 업로드는 디스크와 네트워크를 동시에 사용한다. 애플리케이션 처리량만 측정하면 어느 쪽이 병목인지 알 수 없다.
1단계: 생성 데이터로 네트워크만 측정
일반적인 iperf3는 메모리에서 테스트 데이터를 생성하고 수신 측에서 버린다. 이 결과를 네트워크 경로의 기준선으로 삼는다.
iperf3 -c 10.20.0.12 -t 60 -O 5 -P 4 -J > network-only.json2단계: fio로 스토리지 상한 측정
다음 예시는 미리 생성한 전용 테스트 파일을 순차 읽기한다. 프로덕션 데이터 파일이나 raw block device를 대상으로 실행하지 않는다. 테스트 전에 파일 크기, 여유 공간, 캐시 효과, 스토리지 비용을 확인한다.
fio --name=seqread \
--filename=/mnt/benchmark/fio-test.bin \
--size=20G \
--rw=read \
--bs=1M \
--ioengine=libaio \
--iodepth=32 \
--direct=1 \
--time_based=1 \
--runtime=60 \
--group_reporting \
--output-format=json \
--output=fio-storage-only.jsondirect=1은 가능한 환경에서 page cache 우회를 요청해 디바이스 성능을 보기 쉽게 한다. 그러나 파일시스템과 엔진에 따라 지원과 의미가 다르므로 fio 출력과 오류를 확인한다. 평균 bandwidth뿐 아니라 completion latency의 p95, p99, p99.9와 시간대별 변동을 본다.
3단계: 파일 경로 또는 동시 부하로 경계를 연결
iperf3 -F는 생성 데이터 대신 파일을 송신 source 또는 수신 sink로 사용한다. 공식 문서는 이 기능을 스토리지가 파일 전송의 병목인지 확인하는 용도로 설명하면서, 완전한 파일 전송 도구가 아니며 길이·속성·내용이 원본과 일치한다고 보장하지 않는다고 명시한다.
# 송신 측 파일 읽기와 네트워크를 한 경로에 포함
iperf3 -c 10.20.0.12 -F /mnt/benchmark/fio-test.bin -t 60더 명확한 방법은 fio와 iperf3를 각각 JSON으로 기록하면서 동시에 실행하는 것이다.
# 터미널 A: 네트워크 부하
iperf3 -c 10.20.0.12 -t 120 -O 5 -P 4 -J > combined-iperf3.json
# 터미널 B: 같은 시간에 스토리지 부하
fio --name=seqread --filename=/mnt/benchmark/fio-test.bin \
--size=20G --rw=read --bs=1M --ioengine=libaio --iodepth=32 \
--direct=1 --time_based=1 --runtime=120 --group_reporting \
--output-format=json --output=combined-fio.json비교 순서는 다음과 같다.
iperf3만 실행한 goodputfio만 실행한 bandwidth와 latency- 둘을 동시에 실행했을 때 두 결과의 변화
각각 실행할 때는 빠른데 동시 실행에서 둘 다 느려지면 NIC나 디스크만의 문제가 아닐 가능성이 높다. 같은 CPU socket에 몰린 interrupt, NUMA 원격 메모리, memory bandwidth, 가상 머신의 aggregate bandwidth, cgroup CPU quota를 확인한다.
fio에는 net과 Linux의 splice(2)를 사용하는 netsplice I/O engine도 있다. TCP, UDP, UNIX socket 등을 job 형태로 구성할 수 있지만, 네트워크 기준선에는 TCP/UDP 진단 출력이 풍부한 iperf3가 보통 더 적합하다. fio의 network engine은 스토리지·네트워크 작업을 동일한 fio job 모델로 자동화해야 할 때 제한적으로 사용한다.
양 끝단에서 동시에 수집할 증거
테스트 결과 파일만 보면 원인을 놓친다. 송신·수신 호스트에서 다음 명령을 같은 시간에 실행한다.
TCP 상태
# rtt/rttvar, cwnd, pmtu, congestion algorithm, retrans 확인
ss -tin '( dport = :5201 or sport = :5201 )'
# 시스템 TCP 재전송과 오류
sar -n TCP,ETCP 1
# 프로토콜 카운터 변화
nstat -az | grep -E 'TcpRetransSegs|TcpExtTCPTimeouts|IpReasmFails'ss -i는 커널 내부 TCP 정보인 RTT, RTT 편차, congestion window, path MTU 등을 노출한다. Retr가 증가하면 경로 손실만 보지 말고 NIC drop, qdisc drop, MTU, 클라우드 allowance를 함께 확인한다.
인터페이스와 큐
# 인터페이스 drop/error
ip -s link show dev eth0
sar -n DEV,EDEV 1
# NIC 드라이버별 상세 통계
ethtool -S eth0
# link, offload, channel, ring, coalescing 설정
ethtool eth0
ethtool -k eth0
ethtool -l eth0
ethtool -g eth0
ethtool -c eth0
# qdisc drop과 backlog
tc -s qdisc show dev eth0ethtool -S의 카운터 이름은 드라이버마다 다르다. 절대값 하나보다 테스트 전후 delta를 기록한다. CRC·frame error는 물리 계층을, missed·no buffer·FIFO drop은 수신 큐나 처리 지연을, qdisc drop은 송신 queue 압력을 의심하게 한다.
CPU, interrupt, RSS
mpstat -P ALL 1
sar -n SOFT 1
cat /proc/interrupts
cat /proc/net/softnet_statLinux의 RSS는 NIC가 flow hash를 사용해 여러 receive queue와 CPU로 패킷 처리를 분산한다. RPS는 소프트웨어에서 CPU를 선택하고, RFS는 애플리케이션이 실행되는 CPU와 flow 처리를 가깝게 하며, XPS는 송신 queue 선택을 조정한다. 전체 CPU 사용률이 25%여도 한 코어의 softirq가 100%면 단일 흐름이나 queue가 병목일 수 있다.
설정 변경 전에는 다음을 비교한다.
ethtool -l eth0 # 현재/최대 channel 수
ethtool -x eth0 # RSS indirection table
cat /proc/irq/*/smp_affinity_listchannel, ring, coalescing, offload를 한꺼번에 바꾸지 않는다. 한 항목씩 변경하고 동일한 테스트를 반복해야 인과관계를 설명할 수 있다.
클라우드 인스턴스 한도
AWS ENA 드라이버는 ethtool -S를 통해 allowance 초과 카운터를 제공한다.
ethtool -S eth0 | grep -E \
'bw_(in|out)_allowance_exceeded|pps_allowance_exceeded|conntrack_allowance'bw_in_allowance_exceeded,bw_out_allowance_exceeded: 인스턴스 aggregate bandwidth 한도를 넘어 queue 또는 drop된 패킷pps_allowance_exceeded: 양방향 PPS 한도를 넘어 queue 또는 drop된 패킷conntrack_allowance_exceeded: connection tracking 한도를 넘어 신규 연결을 만들지 못해 drop된 패킷
NIC link가 10Gbps로 보이더라도 인스턴스 타입의 baseline, burst, 방향별·aggregate 한도는 더 낮을 수 있다. 클라우드에서는 guest OS 지표와 provider allowance 지표를 같은 사건의 증거로 묶어야 한다.
증상별 병목 판단표
| 관찰 | 가능성이 높은 원인 | 다음 확인 |
|---|---|---|
단일 TCP만 느리고 -P 4는 빠름 | BDP/window, 한 흐름 CPU, RSS 분산 | ss -ti, 코어별 softirq, iperf3 버전 |
| 정방향만 느리고 역방향은 정상 | 방향별 경로·한도, 송신/수신 NIC 설정 차이 | -R, 양쪽 ethtool -S, route |
| TCP retransmit가 급증하며 goodput 하락 | loss, queue drop, MTU, congestion | sar -n EDEV,ETCP, tc -s, pmtu |
| UDP bitrate를 올릴 때 loss가 갑자기 시작 | bandwidth 또는 PPS ceiling, queue 부족 | 패킷 크기 변경, ENA allowance, ring drop |
| iperf3는 빠르고 fio만 느림 | 스토리지·파일시스템 병목 | fio latency, iostat, device queue |
| fio와 iperf3가 각각은 빠르고 동시 실행만 느림 | CPU, NUMA, memory bandwidth, VM aggregate 한도 | mpstat, interrupts, NUMA 배치, cgroup |
ping은 정상인데 TCP 처리량이 낮음 | ICMP와 다른 queue/policy, BDP, 단일 흐름 | iperf3 정·역방향, ss -ti, firewall/qdisc |
| retransmit 없이 앱만 느림 | DB query, TLS, connection pool, 애플리케이션 backpressure | 앱 tracing, DB wait, pool queue |
ping 성공은 경로가 완전히 건강하다는 뜻이 아니다. ICMP가 애플리케이션 TCP와 다른 정책·queue를 통과하거나 낮은 부하에서만 정상일 수 있다. 반대로 ICMP가 rate limit되어도 TCP는 정상일 수 있다.
MTU와 offload를 의심할 때
큰 전송만 실패하거나 VPN·overlay 경로에서 처리량이 불안정하면 path MTU를 확인한다.
# 커널이 현재 연결에 추정한 pmtu
ss -tin dst 10.20.0.12
# IPv4에서 단편화 금지 상태로 크기를 단계적으로 확인
ping -M do -s 1472 10.20.0.12
# iperf3 MSS를 낮춰 비교
iperf3 -c 10.20.0.12 -M 1360 -t 30 -O 3표준 Ethernet MTU 1500에서 IPv4 ICMP payload 1472는 IP 20bytes와 ICMP 8bytes를 더해 1500이 된다. 터널 헤더가 추가되면 허용 payload는 더 작아진다. 숫자를 그대로 복사하지 말고 실제 overlay와 IPv4/IPv6 헤더를 기준으로 계산한다.
TSO/GSO/GRO 같은 offload는 CPU 사용량을 줄이고 처리량을 높일 수 있다. 패킷 캡처에서 큰 세그먼트가 보인다는 이유만으로 장애라고 판단하지 않는다. 테스트를 위해 offload를 끌 때는 영향 범위와 원복 명령을 준비하고 한 기능씩 비교한다.
ethtool -k eth0
# 예: 검증된 점검 창에서만 한 항목 변경
ethtool -K eth0 tso off
# 반드시 원복
ethtool -K eth0 tso on재현 환경에서 지연과 손실을 주입하기
복제나 백업이 지연·손실에 어떻게 반응하는지 확인할 때 Linux netem으로 test namespace 또는 전용 테스트 호스트에 조건을 만들 수 있다.
# 테스트 환경 전용: 20ms 지연, 5ms jitter, 0.1% loss
sudo tc qdisc add dev eth0 root netem delay 20ms 5ms loss 0.1%
# 상태 확인
sudo tc -s qdisc show dev eth0
# 즉시 원복
sudo tc qdisc del dev eth0 rootnetem은 delay, loss, corruption, duplication, reordering, rate 제한을 지원한다. SSH 관리 인터페이스에 잘못 적용하면 접속을 잃을 수 있으므로 프로덕션에서 즉흥적으로 실행하지 않는다. 테스트 namespace, 별도 NIC, 콘솔 접근, 자동 원복 타이머를 준비한다.
30분 병목 진단 런북
0~5분: 범위 고정
- 느린 작업의 source, destination, 방향, 시작 시각을 확인한다.
- 기대 성능이 link speed인지, 클라우드 보장치인지, 과거 기준선인지 구분한다.
- 양쪽 버전, MTU, route, link speed를 기록한다.
5~12분: 네트워크 기준선
iperf3단일 TCP 정방향과 역방향을 실행한다.-P 4를 추가해 단일 흐름과 다중 흐름 차이를 본다.- 양 끝의
mpstat,sar -n DEV,EDEV,TCP,ETCP,ethtool -S를 수집한다.
12~20분: 경계 분리
- 네트워크 기준선이 정상이라면 fio로 스토리지 기준선을 측정한다.
- 둘을 동시에 실행해 공유 자원 간섭을 확인한다.
- 애플리케이션 처리량과 iperf3/fio 기준선을 같은 단위와 시간축으로 비교한다.
20~27분: 가설 검증
- 정·역방향 차이,
-P차이, retransmit, drop, softirq, allowance counter를 근거로 후보를 1~2개로 줄인다. - window, channel, offload, queue를 한 번에 하나만 변경한다.
- 변경 전과 동일한 명령·지속 시간으로 다시 측정한다.
27~30분: 원복과 기록
- 임시 qdisc, offload, sysctl, 방화벽 변경을 원복한다.
- 명령, 원시 JSON, 시스템 지표, 변경 전후 결과를 보존한다.
- 원인이 확정되지 않았으면 관찰 사실과 가설을 분리해 다음 실험을 적는다.
흔한 실패 패턴
iperf3 -P 16 결과만 보고 링크가 정상이라고 결론
다중 흐름은 aggregate 상한을 보여 주지만 실제 DB 복제나 한 개 백업 stream의 성능을 보장하지 않는다. 단일 흐름과 다중 흐름을 둘 다 남긴다.
평균 처리량만 저장
60초 평균이 같아도 5초간 0에 가까운 구간이 있으면 복제 lag와 tail latency는 크게 증가한다. 1초 interval, JSON, 시스템 metric을 함께 저장한다.
양 끝 중 한쪽만 본다
송신 측 retransmit만으로 drop 위치를 알 수 없다. 수신 NIC의 no-buffer, 중간 qdisc, 클라우드 allowance, 반대편 CPU를 함께 봐야 한다.
fio 결과를 네트워크 성능으로 해석
fio는 storage workload를 정밀하게 만드는 도구다. net engine이 있어도 TCP 경로 진단은 iperf3가 더 직접적이다. 두 도구의 역할을 섞지 말고 경계를 분리한 뒤 결합한다.
테스트가 프로덕션 장애를 만든다
대역폭·PPS·디스크 queue를 모두 채우는 테스트는 실제 서비스와 복제 트래픽을 밀어낼 수 있다. 목표 bitrate, 실행 시간, 중단 조건, 원복 절차를 먼저 정한다.
핵심 요약
- 네트워크 성능은 goodput, PPS, RTT, jitter, loss, retransmit, CPU를 함께 봐야 한다. link speed 하나로는 설명할 수 없다.
iperf3는 정방향·역방향, 단일·다중 stream, TCP·UDP를 비교해 네트워크 상한과 방향성 병목을 찾는 도구다.- 10Gbps·20ms 경로의 BDP는 약 25MB다. 단일 TCP stream이 느리면 손실뿐 아니라 window와 CPU 분산을 확인한다.
- fio로 스토리지 기준선을 따로 측정하고 iperf3와 동시 실행하면 디스크, 네트워크, 공유 CPU·메모리 병목을 구분할 수 있다.
ss -ti,sar,ethtool -S,tc -s, 코어별 softirq, 클라우드 allowance counter를 양 끝에서 같은 시간축으로 수집한다.- channel, offload, buffer를 한꺼번에 바꾸지 않는다. 한 변수씩 변경하고 동일한 실험으로 전후를 비교한다.
References
- ESnet, "Invoking iperf3", https://software.es.net/iperf/invoking.html
- ESnet, "iperf3 FAQ", https://software.es.net/iperf/faq.html
- fio project, "fio Documentation", https://fio.readthedocs.io/en/latest/fio_doc.html
- Linux kernel documentation, "Scaling in the Linux Networking Stack", https://docs.kernel.org/networking/scaling.html
- Michael Kerrisk, "ss(8) — another utility to investigate sockets", https://man7.org/linux/man-pages/man8/ss.8.html
- ethtool project, "ethtool(8)", https://man7.org/linux/man-pages/man8/ethtool.8.html
- sysstat project, "sar(1)", https://man7.org/linux/man-pages/man1/sar.1.html
- Linux iproute2, "tc-netem(8)", https://man7.org/linux/man-pages/man8/tc-netem.8.html
- AWS, "Monitor network performance for your EC2 instance", https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/monitoring-network-performance-ena.html