대역폭은 충분한데 전송 속도가 안 나오는 상황은 대부분 회선이 아니라 한 번에 띄울 수 있는 데이터 양(윈도) 과 손실 복구 방식에서 갈린다. 튜닝 전에 병목이 어디인지부터 갈라야 한다.
# 실제 처리량 측정 (양쪽에 iperf3 필요)
iperf3 -s # 수신 측
iperf3 -c <서버> -t 30 -P 4 # 송신 측
# 재전송 · 윈도 상태
ss -tin | head -40
# 인터페이스 드롭 · 오류
ip -s link show <IFNAME>
netstat -s | egrep -i 'retransmit|segments|listen|overflow'
ss -tin 출력의 retrans, cwnd, rtt 세 값이면 방향이 잡힌다. 재전송이 거의 없는데 느리면 버퍼(윈도)가 모자란 것이고, 재전송이 많으면 손실 구간을 먼저 고쳐야 한다.
한 흐름이 링크를 채우려면 버퍼가 대역폭 × 왕복 지연(BDP) 이상이어야 한다.
BDP(byte) = 대역폭(bit/s) / 8 x RTT(초)
예) 1 Gbps, RTT 40 ms
125,000,000 x 0.04 = 5,000,000 byte = 약 5 MB
즉 서울과 해외 구간처럼 RTT 가 큰 경로에서는 기본값(수백 KB)으로는 절대 회선을 못 채운다. 반대로 RTT 1 ms 인 사내망에서 버퍼만 키우는 것은 메모리 낭비이며 오히려 지연을 늘린다.
# /etc/sysctl.d/90-net.conf - 1 Gbps x 수십 ms 급 경로 기준
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.ipv4.tcp_rmem = 4096 131072 134217728
net.ipv4.tcp_wmem = 4096 65536 134217728
net.ipv4.tcp_window_scaling = 1
net.core.netdev_max_backlog = 8192
net.core.somaxconn = 8192
sysctl --system
sysctl net.ipv4.tcp_rmem net.core.rmem_max
tcp_rmem · tcp_wmem 은 최소 기본 최대 세 값이며 커널이 이 범위에서 자동 조절한다. rmem_default · wmem_default 를 크게 잡는 것은 TCP 에는 의미가 적고 연결마다 메모리를 미리 먹으므로, 최대값만 올리고 기본값은 두는 편이 낫다. tcp_window_scaling 이 꺼져 있으면 윈도가 64 KB 를 넘지 못해 다른 값을 아무리 키워도 소용없다.
tcp_sack 을 끄면 손실 구간 이후 데이터를 통째로 재전송하게 되어 처리량이 크게 떨어진다. 2019년 SACK Panic(CVE-2019-11477 계열) 대응으로 껐던 설정이 그대로 남아 있는 서버가 흔한데, 패치된 커널에서는 다시 켜는 것이 맞다.
net.ipv4.tcp_sack = 1
net.ipv4.tcp_dsack = 1
net.ipv4.tcp_timestamps = 1
tcp_dsack 은 중복 수신을 송신 측에 알려 불필요한 재전송과 잘못된 혼잡 판단을 줄인다. tcp_timestamps 는 RTT 추정과 순번 보호에 쓰이며 끄면 윈도 확장 판단이 나빠진다.
혼잡 제어를 BBR 로 바꾸면 손실률이 있는 장거리 구간에서 특히 효과가 크다.
sysctl net.ipv4.tcp_available_congestion_control
modprobe tcp_bbr
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
fq 큐 규율을 함께 지정해야 BBR 이 의도대로 동작한다.
somaxconn · netdev_max_backlog · tcp_max_syn_backlog 를 본다. 애플리케이션의 listen backlog 도 함께 올려야 의미가 있다.tcp_tw_reuse 는 나가는 연결에만 효과가 있다. tcp_tw_recycle 은 NAT 환경에서 연결이 끊기는 문제가 있어 커널 4.12 에서 제거됐다. 지금 문서에 남아 있다면 지운 설정이다.tcp_retries2 를 낮추면 죽은 상대를 빨리 포기하지만, 순간적인 경로 장애에도 연결을 끊는다. 클러스터 내부처럼 빠른 장애 감지가 필요한 곳에만 쓴다.disable_ipv6 는 성능과 무관하다. IPv6 우선 조회 때문에 느려 보이는 경우가 있을 뿐이다.ethtool -k <IFNAME> 로 GRO · TSO · GSO 상태를 본다. 가상화 환경에서 이 값 때문에 처리량이 반토막 나는 사례가 있어, 의심되면 하나씩 껐다 켜며 측정한다.메모리 계열(vm.swappiness, vm.dirty_ratio 등)은 네트워크 파라미터가 아니다. 디스크로 받아 쓰는 작업이라면 간접적으로 영향을 주지만 대역폭 문제의 원인은 아니다.
# 현재 전역 TCP 설정
netsh interface tcp show global
# 수신 창 자동 조정. 기본값이 normal 이며 대개 그대로 두는 것이 맞다
netsh interface tcp set global autotuninglevel=normal
# 어댑터 오프로딩 · 큐 설정 확인
Get-NetAdapterAdvancedProperty -Name "<어댑터>"
"QoS 패킷 스케줄러가 대역폭의 20%를 항상 예약하므로 그룹 정책에서 0으로 낮춰야 한다"는 설명이 오래 돌아다녔지만, 이 예약분은 QoS 를 쓰는 트래픽이 없을 때는 일반 트래픽이 그대로 쓴다. 일반적인 서버에서 이 값을 만져 처리량이 늘지는 않는다(확인 필요 - 이번에 Microsoft 공식 문서로 재확인하지 못했다).
실제로 효과가 있는 쪽은 다음이다.