목차
- 왜 ext4 성능 개선이 생각보다 큰 차이를 만들까
- ext4와 커널 튜닝 개념 먼저 정리해보겠습니다
- ext4에서 먼저 봐야 할 핵심 포인트
- 자주 쓰는 옵션 비교
- 실전 사례: 로그 적재 서버에서 지연 스파이크를 줄인 과정
- 1. 현재 ext4 마운트 상태부터 확인
- 2. 병목이 파일 시스템인지, 블록 계층인지 분리
- 3. 안전한 범위의 1차 조정
- 4. 커널 writeback 파라미터도 함께 조정
- 5. SSD라면 discard 운영 방식 점검
- ⚠️ 실무에서 자주 만나는 트러블슈팅
- 문제 1: noatime 넣었더니 일부 프로그램 동작이 달라졌습니다
- 문제 2: commit 값을 늘렸더니 찜찜합니다
- 문제 3: data=writeback으로 바꾸면 더 빠른가요?
- 문제 4: reserved block이 너무 크게 잡혀 있습니다
- 검증: 바꿨으면 반드시 재현 테스트를 해봐야 합니다
- fio로 쓰기 패턴 재현
- 정리: 제가 ext4 튜닝할 때 지키는 순서
- 자주 묻는 질문
- ext4보다 다른 파일 시스템이 항상 더 빠른가요?
- 모든 서버에 noatime을 넣어도 되나요?
- 커널 튜닝 ext4는 어느 정도까지 해야 하나요?
- 마무리
[Linux] 커널 튜닝 ext4 파일 시스템 성능 최적화 실전 사례
리눅스 서버를 오래 보다 보면 CPU나 메모리보다도 디스크 I/O가 먼저 발목을 잡는 순간이 꼭 옵니다. 특히 로그가 많이 쌓이거나 작은 파일이 계속 생성되는 워크로드에서는 커널 튜닝 ext4 한 번으로 체감이 확 달라지기도 하거든요. 저도 홈랩과 운영 환경에서 비슷한 문제를 몇 번 겪었는데, 처음엔 애플리케이션 문제인 줄 알고 한참 돌아갔었습니다. 근데 막상 뜯어보니 ext4 마운트 옵션과 writeback(라이트백, 커널의 지연 쓰기) 동작이 핵심이더라고요. 이번 글에서는 Linux 성능 최적화 관점에서 ext4 파일 시스템을 어떻게 점검하고, 어떤 순서로 조정해야 덜 위험하게 성능을 끌어올릴 수 있는지 실제 사례 흐름으로 정리해보겠습니다.
특히 이 글은 벤치마크 숫자를 과장해서 보여주는 방식이 아니라, 제가 실제로 자주 쓰는 확인 절차와 안전한 튜닝 기준 위주로 풀어갑니다. 혹시 디스크 사용률은 높지 않은데 응답이 한 번씩 뚝 끊기는 경험 있으신가요? 여기서 중요한 포인트가 바로 파일 시스템 자체보다도 파일 시스템 튜닝과 커널 writeback 정책을 같이 봐야 한다는 점입니다.
애플리케이션 쓰기 요청이 페이지 캐시와 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 마운트 상태부터 확인
- 대상 파일 시스템의 마운트 옵션을 확인합니다.
- 저널링 모드와 예약 블록, 파일 시스템 특성을 확인합니다.
- 실제 장치의 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이 너무 자주 발생하는 상황을 완화하는 데 써볼 만합니다. 다만 이 값은 서비스 특성에 따라 보수적으로 잡아야 합니다. 데이터 안전성이 아주 민감하면 함부로 늘리면 안 돼요.
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
튜닝 전후의 지연 분포, 쓰기 대기열, writeback 패턴을 비교하는 성능 검증 이미지입니다.
실제로 해보니까 단순 최고 성능보다 최악 구간이 얼마나 줄었는지가 훨씬 중요했어요. 운영 환경은 평균값보다 꼬리 지연(tail latency, 최악 구간 지연)이 문제를 만들거든요. 이 부분은 다음 글에서 XFS와 ext4를 워크로드별로 비교하면서 더 자세히 다뤄볼 예정입니다.
정리: 제가 ext4 튜닝할 때 지키는 순서
- 현재 마운트 옵션과 저널 상태를 확인합니다.
- 디스크 병목인지, 앱의 fsync 패턴인지 먼저 분리합니다.
noatime,lazytime,commit같은 안전한 범위부터 조정합니다.- 커널 dirty page 정책을 함께 봅니다.
- SSD라면
discard대신fstrim.timer운영도 검토합니다. - 반드시 재현 테스트와 커널 로그 확인으로 검증합니다.
| 튜닝 대상 | 먼저 볼 상황 | 제가 보통 취하는 액션 |
|---|---|---|
| 메타데이터 쓰기 많음 | 작은 파일, 잦은 읽기/쓰기 | noatime, lazytime 검토 |
| 주기적 지연 스파이크 | writeback 몰림 | dirty page 관련 sysctl 점검 |
| SSD 지속 쓰기 부담 | discard 상시 사용 | fstrim.timer 전환 검토 |
| 데이터 안정성 민감 | DB, 중요 서비스 | 공격적 journaling 변경 지양 |
자주 묻는 질문
ext4보다 다른 파일 시스템이 항상 더 빠른가요?
항상 그렇진 않아요. 워크로드에 따라 다릅니다. ext4는 여전히 범용성과 안정성 면에서 아주 강한 선택지입니다.
모든 서버에 noatime을 넣어도 되나요?
대부분 문제 없지만, atime 의존 프로그램이 아예 없는지 확인은 필요합니다. 운영 표준으로 넣기 전에는 테스트 서버에서 먼저 검증하세요.
커널 튜닝 ext4는 어느 정도까지 해야 하나요?
제 기준은 간단해요. 관찰 가능한 병목이 있을 때만, 그리고 되돌릴 수 있는 범위부터입니다. 근거 없는 튜닝은 그냥 리스크죠.
운영 반영 전 점검 항목과 튜닝 우선순위를 한 장으로 정리한 요약 이미지입니다.
마무리
커널 튜닝 ext4는 화려한 작업은 아닙니다. 그런데 운영 서버에서 한 번 병목이 터졌을 때 가장 현실적으로 효과를 보는 지점이기도 합니다. 제가 직접 해보니 핵심은 "옵션 많이 넣기"가 아니라 "현재 쓰기 패턴을 이해하고, 안전한 범위부터 검증하는 것"이었어요. 드디어 됐다 싶을 때도 로그와 지연 분포를 꼭 다시 보셔야 합니다. 진짜 편하더라고요.
이번 글에서는 ext4 기준으로 정리했지만, 다음 글에서는 Linux 성능 최적화 관점에서 XFS와 ext4를 어떤 워크로드에서 나눠서 볼지 이어서 다뤄보겠습니다. 이전 글에서 다룬 I/O 스케줄링과 블록 디바이스 점검 내용도 함께 보시면 흐름이 더 잘 잡히실 겁니다. 혹시 지금 운영 중인 서버에서 파일 시스템 튜닝을 고민하고 계시다면, 먼저 현재 마운트 옵션과 iostat 출력부터 확인해보세요. 거기서 절반은 이미 답이 보입니다.
'IT > Linux' 카테고리의 다른 글
| [리눅스 스토리지] LVM 볼륨 확장 및 축소: 실전 마이그레이션 시나리오 분석 (0) | 2026.07.19 |
|---|---|
| [Linux] tmux 세션 관리 실패 사례: 복구 및 재발 방지 전략 (1) | 2026.07.18 |
| [Linux] zsh vs bash: 쉘 스크립팅 생산성 1년 실측 비교 (0) | 2026.07.18 |
| [Linux] openSUSE 비교: Leap 15 vs Tumbleweed 1년 운영 후기 (0) | 2026.07.18 |
| [Linux 보안] SELinux vs AppArmor: 선택 가이드와 실전 비교 (0) | 2026.07.15 |
| [Linux] journald 로그 관리: systemd 실전 트러블슈팅 5가지 (0) | 2026.07.15 |