13년차의 서버실

서버, 인프라, 홈랩 기술 블로그

[태그:] Linux 커널 튜닝

  • [Linux] 커널 튜닝 ext4 파일 시스템 성능 최적화 실전 사례

    [Linux] 커널 튜닝 ext4 파일 시스템 성능 최적화 실전 사례

    [Linux] 커널 튜닝 ext4 파일 시스템 성능 최적화 실전 사례

    리눅스 서버를 오래 보다 보면 CPU나 메모리보다도 디스크 I/O가 먼저 발목을 잡는 순간이 꼭 옵니다. 특히 로그가 많이 쌓이거나 작은 파일이 계속 생성되는 워크로드에서는 커널 튜닝 ext4 한 번으로 체감이 확 달라지기도 하거든요. 저도 홈랩과 운영 환경에서 비슷한 문제를 몇 번 겪었는데, 처음엔 애플리케이션 문제인 줄 알고 한참 돌아갔었습니다. 근데 막상 뜯어보니 ext4 마운트 옵션과 writeback(라이트백, 커널의 지연 쓰기) 동작이 핵심이더라고요. 이번 글에서는 Linux 성능 최적화 관점에서 ext4 파일 시스템을 어떻게 점검하고, 어떤 순서로 조정해야 덜 위험하게 성능을 끌어올릴 수 있는지 실제 사례 흐름으로 정리해보겠습니다.

    특히 이 글은 벤치마크 숫자를 과장해서 보여주는 방식이 아니라, 제가 실제로 자주 쓰는 확인 절차와 안전한 튜닝 기준 위주로 풀어갑니다. 혹시 디스크 사용률은 높지 않은데 응답이 한 번씩 뚝 끊기는 경험 있으신가요? 여기서 중요한 포인트가 바로 파일 시스템 자체보다도 파일 시스템 튜닝과 커널 writeback 정책을 같이 봐야 한다는 점입니다.

    커널 튜닝 ext4와 I/O 경로를 설명하는 개요 다이어그램

    애플리케이션 쓰기 요청이 페이지 캐시와 ext4 저널을 거쳐 블록 디바이스로 내려가는 흐름을 보여주는 개요 이미지입니다.

    왜 ext4 성능 개선이 생각보다 큰 차이를 만들까

    쉽게 말해 ext4는 그냥 디스크에 바로 쓰는 시스템이 아닙니다. 애플리케이션이 파일을 쓰면 먼저 page cache(페이지 캐시, 메모리 기반 캐시)에 반영되고, 이후 커널이 적절한 시점에 실제 저장 장치로 내립니다. 여기에 journaling(저널링, 메타데이터 일관성을 위한 기록)까지 얹히니까, 같은 SSD를 써도 마운트 옵션과 커널 파라미터에 따라 체감이 꽤 달라질 수 있어요.

    제가 처음 이걸 제대로 체감한 건 로그 수집 서버였습니다. 디스크는 NVMe였고 CPU도 여유가 있었는데, 특정 시간대만 되면 flush(플러시, 메모리의 변경 내용을 디스크로 밀어내는 작업)가 몰리면서 응답이 출렁이더라고요. 처음엔 “스토리지가 느린가?” 싶었는데 사실 스토리지보다는 쓰기 패턴과 ext4 기본 동작이 workload(워크로드, 실제 작업 특성)과 안 맞았던 겁니다.

    ext4와 커널 튜닝 개념 먼저 정리해보겠습니다

    ext4에서 먼저 봐야 할 핵심 포인트

    • atime: 파일 읽기 때 접근 시간(access time)을 갱신할지 여부입니다.
    • commit: 저널 커밋 주기 관련 옵션입니다. 너무 공격적으로 늘리면 장애 시 최근 변경 유실 범위가 커질 수 있습니다.
    • journaling mode: <code>data=ordered, data=writeback, data=journal 같은 모드가 있습니다.
    • discard: SSD/TRIM 처리 방식입니다. 연속 discard가 편할 때도 있지만, 주기적 fstrim이 더 나은 경우가 많습니다.
    • dirty page: 커널이 메모리에 쌓아두는 더티 페이지 비율입니다. 이게 높아지면 한 번에 밀어내는 양이 커질 수 있어요.

    자주 쓰는 옵션 비교

    항목 의미 장점 주의할 점
    noatime 읽기 시 access time 갱신 비활성화 불필요한 메타데이터 쓰기 감소 atime 의존 프로그램이 있으면 영향 확인 필요
    lazytime 시간 정보 갱신을 지연 반영 메타데이터 쓰기 감소 일부 운영 정책과 충돌 없는지 확인 필요
    commit=30 저널 커밋 간격 증가 잦은 커밋 부담 완화 가능 장애 시 최근 변경 유실 범위 증가 가능
    data=ordered 일반적인 기본 저널링 모드 안정성과 성능 균형 쓰기 특성에 따라 병목 가능
    data=writeback 데이터 기록 순서 보장 완화 일부 쓰기 성능 유리 가능 일관성 측면 주의, 무조건 권장 아님

    여기서 제가 강조하고 싶은 건, ext4 성능 개선은 옵션 몇 개 붙인다고 끝나는 작업이 아니라는 점이에요. 파일 생성이 많은지, 순차 쓰기가 많은지, fsync(에프싱크, 즉시 디스크 반영 요청)가 잦은지에 따라 접근이 달라져야 하거든요.

    실전 사례: 로그 적재 서버에서 지연 스파이크를 줄인 과정

    상황은 이랬습니다. 작은 로그 파일이 자주 쓰이고, 1분 단위 배치 작업이 돌 때 응답 지연이 크게 튀었습니다. 시스템을 보면 CPU는 넉넉했고 메모리도 부족하지 않았습니다. 그런데 iostat와 vmstat를 같이 보니 특정 시점마다 writeback이 몰리더라고요. 저도 처음엔 애플리케이션 쓰레드 문제인 줄 알고 삽질 좀 했습니다 ㅎㅎ 결국 원인은 “작은 쓰기 + 메타데이터 갱신 + flush 집중” 조합이었습니다.

    1. 현재 ext4 마운트 상태부터 확인

    1. 대상 파일 시스템의 마운트 옵션을 확인합니다.
    2. 저널링 모드와 예약 블록, 파일 시스템 특성을 확인합니다.
    3. 실제 장치의 readahead와 I/O 패턴을 함께 봅니다.
    findmnt -no TARGET,SOURCE,FSTYPE,OPTIONS /data
    mount | grep ' /data '
    sudo tune2fs -l /dev/nvme0n1p1 | egrep 'Filesystem features|Reserved block count|Block size|Journal'
    sudo dumpe2fs -h /dev/nvme0n1p1 2>/dev/null | egrep 'Filesystem state|Errors behavior|Journal mode'

    이 단계에서 중요한 건 “이미 어떤 옵션이 걸려 있는지”예요. 의외로 relatime 기본값으로 충분한 환경도 있고, 반대로 불필요한 discard가 항상 켜져 있어서 쓰기 경로를 괜히 복잡하게 만드는 경우도 있었습니다.

    2. 병목이 파일 시스템인지, 블록 계층인지 분리

    iostat -xz 1
    vmstat 1
    pidstat -d 1
    sar -b 1

    제가 실제로 자주 보는 건 이 네 가지입니다. iostat로 디바이스 지연과 큐를 보고, vmstat로 dirty page와 writeback 타이밍 감을 잡고, pidstat로 어떤 프로세스가 얼마나 쓰는지 확인합니다. 여기서 애플리케이션 fsync 호출이 잦으면 ext4 튜닝보다 앱 설정 조정이 먼저일 수 있어요. 그러니까 커널 튜닝 ext4는 언제나 계층 분리를 먼저 해야 합니다.

    3. 안전한 범위의 1차 조정

    제가 처음부터 무리하게 건드리지 않는 항목은 대체로 아래 정도입니다.

    sudo cp /etc/fstab /etc/fstab.bak
    sudo editor /etc/fstab
    UUID=xxxx-xxxx  /data  ext4  defaults,noatime,lazytime,commit=30  0  2

    noatime은 읽기 위주의 접근에서도 불필요한 메타데이터 갱신을 줄이는 데 꽤 도움이 돼요. lazytime은 시간 정보 반영을 지연시켜서 메타데이터 쓰기 부담을 줄일 수 있고요. commit=30은 저널 커밋 빈도를 낮춰 writeback이 너무 자주 발생하는 상황을 완화하는 데 써볼 만합니다. 다만 이 값은 서비스 특성에 따라 보수적으로 잡아야 합니다. 데이터 안전성이 아주 민감하면 함부로 늘리면 안 돼요.

    커널 튜닝 ext4 설정 변경 과정을 보여주는 시스템 점검 이미지

    fstab 편집, findmnt 확인, sysctl 조정 흐름을 한눈에 보여주는 구성 이미지입니다.

    4. 커널 writeback 파라미터도 함께 조정

    파일 시스템 옵션만 바꾸고 끝내면 절반만 본 셈입니다. 실제로 써보니까 flush가 몰리는 서버에서는 이쪽 영향도 꽤 크더라고요.

    sysctl vm.dirty_background_ratio
    sysctl vm.dirty_ratio
    sysctl vm.dirty_expire_centisecs
    sysctl vm.dirty_writeback_centisecs
    sudo tee /etc/sysctl.d/99-ext4-tuning.conf >/dev/null <<'EOF'
    vm.dirty_background_ratio = 5
    vm.dirty_ratio = 20
    vm.dirty_expire_centisecs = 3000
    vm.dirty_writeback_centisecs = 500
    EOF
    sudo sysctl --system

    이 수치는 만능 정답이 아닙니다. 다만 dirty page가 너무 많이 쌓였다가 한꺼번에 내려가는 패턴을 줄이는 출발점으로는 꽤 무난했어요. 저는 메모리가 큰 서버에서 기본값 그대로 두었다가 writeback 몰림이 생긴 적이 있었는데, 이 값을 조정하고 나서 지연 스파이크가 훨씬 덜해졌습니다.

    5. SSD라면 discard 운영 방식 점검

    여기서 한 번 더 많이 헷갈립니다. SSD니까 discard를 무조건 마운트 옵션에 넣어야 할 것 같죠? 저도 예전엔 그렇게 했었는데, 실제론 주기적 TRIM이 더 나은 경우가 적지 않아요.

    systemctl status fstrim.timer
    sudo systemctl enable --now fstrim.timer
    systemctl list-timers | grep fstrim

    즉시 discard보다 주기적 fstrim을 선호하는 이유는, 연속 discard가 쓰기 경로에 부담을 줄 수 있기 때문입니다. 물론 환경에 따라 다르니 확인은 꼭 필요해요.

    ⚠️ 실무에서 자주 만나는 트러블슈팅

    문제 1: noatime 넣었더니 일부 프로그램 동작이 달라졌습니다

    아주 드문 편이긴 하지만 atime을 실제로 참조하는 도구가 있을 수 있습니다. 이런 경우 noatime 대신 기본 relatime 유지가 더 안전해요. 무조건 성능만 보고 밀어붙이면 안 됩니다.

    문제 2: commit 값을 늘렸더니 찜찜합니다

    이건 정상적인 감각입니다. commit 간격을 늘리면 메타데이터 커밋 빈도는 줄 수 있지만, 장애 발생 시 최근 변경이 디스크에 완전히 반영되지 않았을 가능성은 커집니다. 그래서 데이터 무결성이 중요한 DB 데이터 디렉터리에는 저는 보수적으로 접근해요. 로그, 캐시, 재생성 가능한 데이터인지 먼저 구분하세요.

    문제 3: data=writeback으로 바꾸면 더 빠른가요?

    경우에 따라 다를 수는 있어도, 이걸 기본 추천으로 보시면 안 돼요. data=writeback은 데이터 기록 순서 보장이 더 약하므로 장애 시 예상 못 한 상태를 만날 수 있습니다. 제가 운영 서버에서는 대부분 data=ordered를 유지합니다. 성능 때문에 일관성을 희생하는 선택은 언제나 마지막이어야 하거든요.

    문제 4: reserved block이 너무 크게 잡혀 있습니다

    대용량 데이터 볼륨이라면 예약 블록(reserved block) 비율이 과한 경우가 있습니다. 루트 파일 시스템이 아니라 일반 데이터 파티션이면 점검해볼 만해요.

    sudo tune2fs -m 1 /dev/nvme0n1p1

    다만 이것도 서비스 성격을 봐야 합니다. 시스템 영역과 일반 데이터 볼륨은 판단 기준이 다르니까요.

    검증: 바꿨으면 반드시 재현 테스트를 해봐야 합니다

    튜닝은 느낌으로 하면 안 돼요. 저도 처음엔 “체감상 좋아진 것 같은데?” 하고 넘어가려다가 다시 문제를 만난 적이 있습니다. 그래서 지금은 꼭 동일한 패턴의 테스트를 다시 돌립니다.

    fio로 쓰기 패턴 재현

    fio --name=ext4-logtest \
        --directory=/data/fiotest \
        --size=2G \
        --bs=4k \
        --rw=randwrite \
        --iodepth=16 \
        --ioengine=libaio \
        --direct=0 \
        --numjobs=4 \
        --runtime=60 \
        --time_based \
        --group_reporting

    여기서 포인트는 숫자 한 줄만 보지 않는 거예요. IOPS만 보고 “성공!” 하면 나중에 다시 당합니다. 저는 아래 항목을 같이 확인합니다.

    • 지연(latency) 분포가 덜 튀는지
    • 테스트 중 writeback 몰림이 줄었는지
    • 애플리케이션 응답이 실제로 안정적인지
    • 마운트 옵션 변경 후 오류 로그가 없는지
    dmesg -T | tail -n 50
    journalctl -k -n 50
    findmnt -no OPTIONS /data
    ext4 성능 개선 결과를 검증하는 대시보드 이미지

    튜닝 전후의 지연 분포, 쓰기 대기열, writeback 패턴을 비교하는 성능 검증 이미지입니다.

    실제로 해보니까 단순 최고 성능보다 최악 구간이 얼마나 줄었는지가 훨씬 중요했어요. 운영 환경은 평균값보다 꼬리 지연(tail latency, 최악 구간 지연)이 문제를 만들거든요. 이 부분은 다음 글에서 XFS와 ext4를 워크로드별로 비교하면서 더 자세히 다뤄볼 예정입니다.

    정리: 제가 ext4 튜닝할 때 지키는 순서

    1. 현재 마운트 옵션과 저널 상태를 확인합니다.
    2. 디스크 병목인지, 앱의 fsync 패턴인지 먼저 분리합니다.
    3. noatime, lazytime, commit 같은 안전한 범위부터 조정합니다.
    4. 커널 dirty page 정책을 함께 봅니다.
    5. SSD라면 discard 대신 fstrim.timer 운영도 검토합니다.
    6. 반드시 재현 테스트와 커널 로그 확인으로 검증합니다.
    튜닝 대상 먼저 볼 상황 제가 보통 취하는 액션
    메타데이터 쓰기 많음 작은 파일, 잦은 읽기/쓰기 noatime, lazytime 검토
    주기적 지연 스파이크 writeback 몰림 dirty page 관련 sysctl 점검
    SSD 지속 쓰기 부담 discard 상시 사용 fstrim.timer 전환 검토
    데이터 안정성 민감 DB, 중요 서비스 공격적 journaling 변경 지양

    자주 묻는 질문

    ext4보다 다른 파일 시스템이 항상 더 빠른가요?

    항상 그렇진 않아요. 워크로드에 따라 다릅니다. ext4는 여전히 범용성과 안정성 면에서 아주 강한 선택지입니다.

    모든 서버에 noatime을 넣어도 되나요?

    대부분 문제 없지만, atime 의존 프로그램이 아예 없는지 확인은 필요합니다. 운영 표준으로 넣기 전에는 테스트 서버에서 먼저 검증하세요.

    커널 튜닝 ext4는 어느 정도까지 해야 하나요?

    제 기준은 간단해요. 관찰 가능한 병목이 있을 때만, 그리고 되돌릴 수 있는 범위부터입니다. 근거 없는 튜닝은 그냥 리스크죠.

    커널 튜닝 ext4 적용 전후를 요약한 비교 인포그래픽

    운영 반영 전 점검 항목과 튜닝 우선순위를 한 장으로 정리한 요약 이미지입니다.

    마무리

    커널 튜닝 ext4는 화려한 작업은 아닙니다. 그런데 운영 서버에서 한 번 병목이 터졌을 때 가장 현실적으로 효과를 보는 지점이기도 합니다. 제가 직접 해보니 핵심은 “옵션 많이 넣기”가 아니라 “현재 쓰기 패턴을 이해하고, 안전한 범위부터 검증하는 것”이었어요. 드디어 됐다 싶을 때도 로그와 지연 분포를 꼭 다시 보셔야 합니다. 진짜 편하더라고요.

    이번 글에서는 ext4 기준으로 정리했지만, 다음 글에서는 Linux 성능 최적화 관점에서 XFS와 ext4를 어떤 워크로드에서 나눠서 볼지 이어서 다뤄보겠습니다. 이전 글에서 다룬 I/O 스케줄링과 블록 디바이스 점검 내용도 함께 보시면 흐름이 더 잘 잡히실 겁니다. 혹시 지금 운영 중인 서버에서 파일 시스템 튜닝을 고민하고 계시다면, 먼저 현재 마운트 옵션과 iostat 출력부터 확인해보세요. 거기서 절반은 이미 답이 보입니다.

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

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

    [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개씩 묶어서 영향도를 보는 게 좋습니다.

    제가 직접 해보니 somaxconn과 tcp_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 코어 분산 관점의 네트워크 최적화를 다뤄볼 예정입니다. 이전 글에서 다룬 서비스 관측 지표와 함께 보시면 더 이해가 잘 되실 겁니다.