본문 바로가기
IT/Linux

[Linux] Linux 커널 튜닝으로 보는 네트워크 I/O 벤치마크 분석

by 수누다 2026. 7. 5.

[Linux] Linux 커널 튜닝으로 보는 네트워크 I/O 벤치마크 분석

고성능 Linux 서버를 만지다 보면 CPU는 남는데 응답 시간이 흔들리고, 대역폭은 충분한 것 같은데 실제 처리량(throughput, 초당 처리량)이 기대만큼 안 나오는 순간이 꼭 옵니다. 저도 홈랩에서 10GbE 환경을 붙여 놓고 Linux 커널 튜닝만 조금 바꿨을 뿐인데 체감이 확 달라지는 경험을 몇 번 했거든요. 특히 대량 연결이 몰리는 API 서버나 프록시(Proxy, 중계 서버), 로그 수집 노드에서는 네트워크 I/O 최적화가 생각보다 훨씬 중요합니다. 이번 글에서는 제가 실제로 자주 보는 항목들만 골라서, 근거 없는 튜닝이 아니라 벤치마크로 검증 가능한 sysctl 튜닝 중심으로 정리해보겠습니다.

중요한 포인트는 하나입니다. 튜닝은 숫자로 확인해야 합니다. 감으로 바꾸면 나중에 원인 추적이 더 어려워지더라고요. 그래서 이번 글은 개념 설명보다도, 기준선 측정(Baseline, 변경 전 기준값)과 비교 검증에 조금 더 무게를 두겠습니다.

Linux 커널 튜닝 기반 네트워크 I/O 최적화 아키텍처 개요 이미지

벤치마크 전후 비교를 위한 Linux 서버 네트워크 경로와 튜닝 포인트를 한눈에 보여주는 개요 이미지입니다.

왜 Linux 커널 튜닝이 네트워크 I/O에서 중요할까요

쉽게 말해 네트워크 패킷(Packet, 네트워크 데이터 조각)은 NIC(Network Interface Card, 네트워크 카드)에서 들어와 커널 큐(queue, 대기열)를 거치고, 소켓 버퍼(socket buffer, 통신 버퍼)를 통과해서 애플리케이션으로 올라갑니다. 이 과정 어디선가 큐가 넘치거나 버퍼가 너무 작으면 드롭(drop, 패킷 유실)이나 재전송(retransmission, 다시 보내기)이 생기고, 결국 처리량이 떨어지거나 지연 시간(latency, 응답 지연)이 튀게 됩니다.

처음엔 저도 "서버 스펙이 충분한데 왜 느리지?" 싶었는데, 실제로는 애플리케이션 문제가 아니라 커널 기본값이 워크로드에 안 맞는 경우가 꽤 있었습니다. 기본값은 보편적인 안정성을 위한 값이지, 고동시성 서버에 최적화된 값은 아니거든요.

  • 수신 큐 적체: 짧은 시간에 연결이 몰릴 때 backlog가 부족하면 초반부터 손해를 봅니다.
  • 소켓 버퍼 한계: 대역폭은 넓은데 송수신 버퍼가 작으면 링크를 다 못 씁니다.
  • 관측 부재: 튜닝 전 수치가 없으면 좋아진 건지 나빠진 건지 판단이 안 됩니다.

벤치마크 전에 먼저 보는 기준선 체크리스트

서버 성능 벤치마크는 설정을 바꾸기 전에 기준선을 잡는 게 핵심입니다. 제가 보통 하는 순서는 아래와 같습니다.

  1. 링크 속도와 duplex 확인
  2. NIC 드롭, 에러, 재전송 지표 확인
  3. 애플리케이션 없이 순수 네트워크 성능 측정
  4. 튜닝 적용
  5. 같은 조건으로 재측정

1. NIC 상태 확인

ip -br addr
ip -s link show dev eth0
ethtool eth0
ss -s
nstat -az | egrep 'Tcp|Ip|Udp'

여기서 <code>RX dropped, TX dropped, TCP 재전송 계열 카운터를 먼저 봅니다. 숫자가 이미 올라가 있다면 커널 내부 큐나 NIC 쪽 병목을 의심할 수 있습니다.

2. 순수 대역폭 측정

# 서버 측
iperf3 -s

# 클라이언트 측
iperf3 -c 192.0.2.10 -t 30 -P 4
iperf3 -c 192.0.2.10 -t 30 -P 8 -R

-P는 병렬 스트림(parallel stream, 동시 전송 수)입니다. 단일 스트림이 아니라 병렬 스트림까지 봐야 실제 서비스 패턴에 더 가깝습니다. -R은 reverse 모드라서 반대 방향 성능도 확인할 수 있고요.

3. 지연과 큐 상태 같이 보기

ping -c 20 192.0.2.10
sar -n DEV 1 10
sar -n TCP,ETCP 1 10

대역폭만 보면 오해하기 쉽습니다. 처리량이 조금 올라갔는데 재전송이 급증하면 오히려 서비스 품질은 나빠질 수 있거든요.

확인 항목 도구 왜 보나
링크 상태 ethtool 속도/duplex 협상 문제 확인
에러/드롭 ip -s link 커널/NIC 큐 병목 확인
소켓 상태 ss -s 연결 수와 TCP 상태 확인
처리량 iperf3 튜닝 전후 비교의 기준값
재전송 nstat, sar 겉보기 성능 상승의 부작용 점검

실전 Linux 커널 튜닝: sysctl 튜닝은 이렇게 접근합니다

여기서부터는 제가 비교적 자주 쓰는, 그리고 의미를 설명할 수 있는 항목만 보겠습니다. 무조건 숫자를 크게 넣는 방식은 추천하지 않습니다. 메모리 사용량이 늘고, 오히려 큐 지연(bufferbloat, 버퍼 과적체)이 생길 수 있거든요.

핵심 sysctl 항목

항목 의미 체크 포인트
net.core.somaxconn listen backlog 상한 동시 접속 급증 서버
net.ipv4.tcp_max_syn_backlog SYN 대기 큐 크기 연결 폭주 시 유용
net.core.netdev_max_backlog NIC 수신 패킷 백로그 RX 적체 완화
net.core.rmem_max / wmem_max 소켓 최대 버퍼 고대역폭 전송
net.ipv4.tcp_rmem / tcp_wmem TCP 자동 버퍼 범위 대용량 흐름 최적화
sudo cp /etc/sysctl.conf /etc/sysctl.conf.bak
sudo tee /etc/sysctl.d/99-network-tuning.conf > /dev/null <<'EOF'
net.core.somaxconn = 4096
net.ipv4.tcp_max_syn_backlog = 8192
net.core.netdev_max_backlog = 4096
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.ipv4.tcp_rmem = 4096 262144 134217728
net.ipv4.tcp_wmem = 4096 262144 134217728
EOF

sudo sysctl --system

이 설정은 "무조건 정답"이 아니라 출발점입니다. 예를 들어 작은 패킷이 많이 들어오는 워크로드와 대용량 파일 전송 워크로드는 반응이 다릅니다. 그래서 한 번에 다 바꾸기보다 2~3개씩 묶어서 영향도를 보는 게 좋습니다.

제가 직접 해보니 somaxconntcp_max_syn_backlog는 접속 급증 구간에서 효과가 보이기 쉬웠고, rmem/wmem 계열은 장거리 네트워크나 큰 전송에서 차이가 보이더라고요. 반대로 이미 애플리케이션이 병목인 상황에서는 기대만큼 변화가 없었습니다. 이건 꼭 기억하셔야 합니다.

Linux 커널 튜닝의 sysctl 튜닝과 네트워크 큐 흐름 설명 이미지

sysctl 항목이 수신 큐와 소켓 버퍼에 어떤 영향을 주는지 설명하는 구성 이미지입니다.

실전 구현 2단계: 애플리케이션과 운영체제 한계도 같이 맞춰야 합니다

Linux 커널 튜닝만 해놓고 애플리케이션의 파일 디스크립터(file descriptor, 파일/소켓 핸들) 한계가 그대로면 금방 막힙니다. 특히 Nginx, HAProxy, Java 기반 서버를 운영하시면 이 부분을 꼭 같이 보셔야 해요.

ulimit -n
cat /proc/sys/fs/file-max
sysctl fs.file-max
systemctl show your-service-name | grep LimitNOFILE

systemd 서비스를 쓰는 경우엔 서비스 단위로 제한이 걸리는 경우가 많습니다.

sudo systemctl edit your-service-name
[Service]
LimitNOFILE=1048576

적용 후에는 재시작이 필요합니다.

sudo systemctl daemon-reload
sudo systemctl restart your-service-name

여기서 중요한 포인트! 커널 backlog를 키워도 애플리케이션이 실제로 listen()에서 작은 backlog를 쓰고 있으면 기대한 만큼 안 나옵니다. 그러니까 OS와 애플리케이션 설정을 같이 봐야 한다는 거죠.

⚠️ 제가 실제로 겪었던 트러블슈팅 포인트

이 파트는 이론보다 훨씬 중요합니다. 저도 처음엔 숫자만 올리면 빨라질 줄 알았는데, 삽질 좀 했습니다 ㅎㅎ

1. 버퍼를 너무 크게 잡았더니 메모리 압박

rmem_max, wmem_max를 크게 올리면 좋을 것 같지만, 연결 수가 많아지면 전체 메모리 사용량이 빠르게 커질 수 있습니다. 특히 컨테이너(Container, 격리 실행 환경) 밀도가 높은 서버에서는 더 민감합니다.

  • 증상: 처리량은 약간 늘었는데 메모리 사용량이 불안정해짐
  • 해결: 최대값보다 자동 조정 범위의 중간값을 먼저 조정하고, 연결 수와 함께 관찰

2. iperf3 수치만 좋고 실제 서비스는 그대로

이건 정말 흔합니다. iperf3는 네트워크 자체 성능을 보기엔 좋지만, TLS 종료나 애플리케이션 로직이 있는 실제 서비스와는 다릅니다.

  • 증상: 벤치마크는 상승, 실서비스 p95 응답시간은 변화 없음
  • 해결: 애플리케이션 로그, CPU softirq, 컨텍스트 스위치까지 같이 확인

3. 드롭은 줄었는데 지연 시간이 늘어남

큐를 크게 키우면 순간 유실은 줄 수 있지만, 대신 대기열이 길어져서 지연이 늘어날 수 있습니다. 이른바 버퍼블로트(bufferbloat, 과도한 버퍼링) 느낌이 나죠.

  • 증상: 대역폭은 증가, 응답성은 악화
  • 해결: 무조건 큰 값 대신 단계적 조정, ping과 애플리케이션 응답시간 동시 측정

4. NIC 오프로딩과 드라이버 변수

환경에 따라 TCP segmentation offload 같은 NIC 기능이 영향을 줄 수 있습니다. 다만 이 부분은 NIC 모델, 드라이버, 커널 버전에 따라 차이가 있어요. 그래서 저는 이 단계는 마지막에 봅니다. 확실한 근거 없이 건드리면 오히려 더 헷갈리더라고요.

검증: 네트워크 I/O 최적화가 실제로 됐는지 확인하는 방법

튜닝 후에는 반드시 같은 조건으로 다시 측정합니다. 시간대가 다르거나 상대 서버 상태가 다르면 비교가 흐려지거든요.

  1. 같은 클라이언트와 같은 네트워크 경로 사용
  2. 같은 병렬 스트림 수 유지
  3. 같은 실행 시간 유지
  4. 튜닝 전후에 ss -s, nstat, ip -s link 저장
mkdir -p ~/benchmarks/network

iperf3 -c 192.0.2.10 -t 30 -P 4 --json > ~/benchmarks/network/iperf3-before.json
ss -s > ~/benchmarks/network/ss-before.txt
ip -s link show dev eth0 > ~/benchmarks/network/link-before.txt
nstat -az > ~/benchmarks/network/nstat-before.txt

# 튜닝 후 동일하게 다시 수행
iperf3 -c 192.0.2.10 -t 30 -P 4 --json > ~/benchmarks/network/iperf3-after.json
ss -s > ~/benchmarks/network/ss-after.txt
ip -s link show dev eth0 > ~/benchmarks/network/link-after.txt
nstat -az > ~/benchmarks/network/nstat-after.txt

JSON 결과를 간단히 비교하고 싶다면 jq를 써도 편합니다.

jq '.end.sum_sent.bits_per_second, .end.sum_received.bits_per_second' ~/benchmarks/network/iperf3-before.json
jq '.end.sum_sent.bits_per_second, .end.sum_received.bits_per_second' ~/benchmarks/network/iperf3-after.json

제가 실제로는 아래 세 가지를 같이 봅니다.

  • 처리량 증가: bits per second 상승 여부
  • 안정성: 재전송과 드롭 감소 여부
  • 응답성: ping, p95 응답시간 악화 여부

이 세 가지가 같이 좋아져야 진짜 개선입니다. 하나만 좋고 나머지가 나빠지면 다시 조정해야 합니다.

네트워크 I/O 최적화 후 서버 성능 벤치마크 결과 시각화 이미지

튜닝 전후 처리량, 재전송, 드롭 카운터를 비교한 벤치마크 결과 시각화 이미지입니다.

비교 정리: 언제 어떤 sysctl 튜닝부터 볼까

상황 먼저 볼 항목 이유
접속 폭주 시 연결 누락 somaxconn, tcp_max_syn_backlog listen/SYN 큐 부족 가능성
RX 드롭 증가 netdev_max_backlog 수신 백로그 적체 가능성
고대역폭 전송이 기대보다 낮음 rmem_max, wmem_max, tcp_rmem, tcp_wmem 소켓 버퍼 한계 확인
실서비스 변화 없음 애플리케이션 backlog, LimitNOFILE OS 밖 병목 가능성

혹시 이런 경험 있으신가요? 벤치마크는 멋지게 올라갔는데 사용자 체감은 그대로인 경우요. 그럴 땐 커널보다 애플리케이션 계층을 먼저 봐야 할 때가 많습니다. 저도 초반엔 커널 숫자만 붙잡고 있었는데, 결국 원인은 워커 수(worker, 처리 프로세스 수)나 연결 풀(connection pool) 설정인 경우가 더 많았어요.

어떤 상황에서 어떤 항목을 먼저 조정할지 빠르게 판단할 수 있는 요약 인포그래픽입니다.

자주 묻는 질문 FAQ

Q1. sysctl 튜닝 값은 높을수록 좋은가요?

아닙니다. 너무 크게 잡으면 메모리 사용량 증가나 지연 시간 증가로 이어질 수 있습니다. 작게 시작해서 측정하면서 올리는 방식이 안전합니다.

Q2. iperf3 결과만 보면 충분한가요?

부족합니다. iperf3는 네트워크 경로 성능을 보기엔 좋지만, 실제 서비스의 응답시간과 애플리케이션 병목까지 설명해주지는 못합니다.

Q3. 모든 서버에 같은 Linux 커널 튜닝을 적용해도 되나요?

권장하지 않습니다. 웹 서버, 스토리지 노드, 로그 수집기, 프록시는 패턴이 다릅니다. 같은 Linux 커널 튜닝이라도 워크로드에 따라 결과가 달라집니다.

마무리: 숫자로 확인되는 튜닝만 남기면 됩니다

이번 글에서는 Linux 커널 튜닝을 네트워크 I/O 관점에서 어떻게 보고, 어떤 순서로 서버 성능 벤치마크를 해야 하는지 정리해봤습니다. 핵심은 단순합니다. 기준선 측정 → 작은 변경 → 같은 조건으로 재측정 → 부작용 확인. 이 루틴만 지켜도 대부분의 삽질은 줄일 수 있습니다.

제가 직접 해보니, 화려한 튜닝보다 기본 지표를 꾸준히 저장하고 비교하는 습관이 더 오래 갑니다. 드디어 됐다! 싶은 순간도 결국 로그와 수치가 말해주더라고요. 다음 글에서는 IRQ affinity(인터럽트 분산), RSS(Receive Side Scaling, 수신 분산), RPS/RFS까지 확장해서 CPU 코어 분산 관점의 네트워크 최적화를 다뤄볼 예정입니다. 이전 글에서 다룬 서비스 관측 지표와 함께 보시면 더 이해가 잘 되실 겁니다.