목차
- 왜 Linux 커널 튜닝이 네트워크 I/O에서 중요할까요
- 벤치마크 전에 먼저 보는 기준선 체크리스트
- 1. NIC 상태 확인
- 2. 순수 대역폭 측정
- 3. 지연과 큐 상태 같이 보기
- 실전 Linux 커널 튜닝: sysctl 튜닝은 이렇게 접근합니다
- 핵심 sysctl 항목
- 실전 구현 2단계: 애플리케이션과 운영체제 한계도 같이 맞춰야 합니다
- ⚠️ 제가 실제로 겪었던 트러블슈팅 포인트
- 1. 버퍼를 너무 크게 잡았더니 메모리 압박
- 2. iperf3 수치만 좋고 실제 서비스는 그대로
- 3. 드롭은 줄었는데 지연 시간이 늘어남
- 4. NIC 오프로딩과 드라이버 변수
- 검증: 네트워크 I/O 최적화가 실제로 됐는지 확인하는 방법
- 비교 정리: 언제 어떤 sysctl 튜닝부터 볼까
- 자주 묻는 질문 FAQ
- Q1. sysctl 튜닝 값은 높을수록 좋은가요?
- Q2. iperf3 결과만 보면 충분한가요?
- Q3. 모든 서버에 같은 Linux 커널 튜닝을 적용해도 되나요?
- 마무리: 숫자로 확인되는 튜닝만 남기면 됩니다
[Linux] Linux 커널 튜닝으로 보는 네트워크 I/O 벤치마크 분석
고성능 Linux 서버를 만지다 보면 CPU는 남는데 응답 시간이 흔들리고, 대역폭은 충분한 것 같은데 실제 처리량(throughput, 초당 처리량)이 기대만큼 안 나오는 순간이 꼭 옵니다. 저도 홈랩에서 10GbE 환경을 붙여 놓고 Linux 커널 튜닝만 조금 바꿨을 뿐인데 체감이 확 달라지는 경험을 몇 번 했거든요. 특히 대량 연결이 몰리는 API 서버나 프록시(Proxy, 중계 서버), 로그 수집 노드에서는 네트워크 I/O 최적화가 생각보다 훨씬 중요합니다. 이번 글에서는 제가 실제로 자주 보는 항목들만 골라서, 근거 없는 튜닝이 아니라 벤치마크로 검증 가능한 sysctl 튜닝 중심으로 정리해보겠습니다.
중요한 포인트는 하나입니다. 튜닝은 숫자로 확인해야 합니다. 감으로 바꾸면 나중에 원인 추적이 더 어려워지더라고요. 그래서 이번 글은 개념 설명보다도, 기준선 측정(Baseline, 변경 전 기준값)과 비교 검증에 조금 더 무게를 두겠습니다.
벤치마크 전후 비교를 위한 Linux 서버 네트워크 경로와 튜닝 포인트를 한눈에 보여주는 개요 이미지입니다.
왜 Linux 커널 튜닝이 네트워크 I/O에서 중요할까요
쉽게 말해 네트워크 패킷(Packet, 네트워크 데이터 조각)은 NIC(Network Interface Card, 네트워크 카드)에서 들어와 커널 큐(queue, 대기열)를 거치고, 소켓 버퍼(socket buffer, 통신 버퍼)를 통과해서 애플리케이션으로 올라갑니다. 이 과정 어디선가 큐가 넘치거나 버퍼가 너무 작으면 드롭(drop, 패킷 유실)이나 재전송(retransmission, 다시 보내기)이 생기고, 결국 처리량이 떨어지거나 지연 시간(latency, 응답 지연)이 튀게 됩니다.
처음엔 저도 "서버 스펙이 충분한데 왜 느리지?" 싶었는데, 실제로는 애플리케이션 문제가 아니라 커널 기본값이 워크로드에 안 맞는 경우가 꽤 있었습니다. 기본값은 보편적인 안정성을 위한 값이지, 고동시성 서버에 최적화된 값은 아니거든요.
- 수신 큐 적체: 짧은 시간에 연결이 몰릴 때 backlog가 부족하면 초반부터 손해를 봅니다.
- 소켓 버퍼 한계: 대역폭은 넓은데 송수신 버퍼가 작으면 링크를 다 못 씁니다.
- 관측 부재: 튜닝 전 수치가 없으면 좋아진 건지 나빠진 건지 판단이 안 됩니다.
벤치마크 전에 먼저 보는 기준선 체크리스트
서버 성능 벤치마크는 설정을 바꾸기 전에 기준선을 잡는 게 핵심입니다. 제가 보통 하는 순서는 아래와 같습니다.
- 링크 속도와 duplex 확인
- NIC 드롭, 에러, 재전송 지표 확인
- 애플리케이션 없이 순수 네트워크 성능 측정
- 튜닝 적용
- 같은 조건으로 재측정
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개씩 묶어서 영향도를 보는 게 좋습니다.
제가 직접 해보니 somaxconn과 tcp_max_syn_backlog는 접속 급증 구간에서 효과가 보이기 쉬웠고, rmem/wmem 계열은 장거리 네트워크나 큰 전송에서 차이가 보이더라고요. 반대로 이미 애플리케이션이 병목인 상황에서는 기대만큼 변화가 없었습니다. 이건 꼭 기억하셔야 합니다.
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 최적화가 실제로 됐는지 확인하는 방법
튜닝 후에는 반드시 같은 조건으로 다시 측정합니다. 시간대가 다르거나 상대 서버 상태가 다르면 비교가 흐려지거든요.
- 같은 클라이언트와 같은 네트워크 경로 사용
- 같은 병렬 스트림 수 유지
- 같은 실행 시간 유지
- 튜닝 전후에
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 응답시간 악화 여부
이 세 가지가 같이 좋아져야 진짜 개선입니다. 하나만 좋고 나머지가 나빠지면 다시 조정해야 합니다.
튜닝 전후 처리량, 재전송, 드롭 카운터를 비교한 벤치마크 결과 시각화 이미지입니다.
비교 정리: 언제 어떤 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 코어 분산 관점의 네트워크 최적화를 다뤄볼 예정입니다. 이전 글에서 다룬 서비스 관측 지표와 함께 보시면 더 이해가 잘 되실 겁니다.
'IT > Linux' 카테고리의 다른 글
| [Linux] 컨테이너 없이 리눅스 네임스페이스로 프로세스 격리하기: 실제 사례 연구 (0) | 2026.07.07 |
|---|---|
| [Linux] rsyslog Loki 마이그레이션: 레거시 로그 시스템 현대화 경험 (0) | 2026.07.02 |
| [Linux] CentOS 7에서 AlmaLinux 9로: 성공적인 서버 마이그레이션 전략과 실제 경험 (1) | 2026.07.01 |
| [Linux] 리눅스 시스템 장애, 프로세스 비정상 종료 원인 분석 및 해결 사례 연구 (0) | 2026.07.01 |
| [Linux] Linux Namespace 격리 기술, 실제 적용과 검증 방법 (0) | 2026.06.27 |
| [Linux] cgroup vs namespace: 컨테이너 격리의 핵심 비교 분석 (0) | 2026.06.26 |