이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
IO Wait와 Disk 병목에서 붙잡아야 할 핵심 판단 기준은 무엇인가?disk 병목은 응답 지연으로 번진다라는 기준은 어떤 JVM/OS 신호로 확인되는가?- Java/Spring 코드에서
IO Wait와 Disk 병목판단 기준은 어떤 장애 신호로 드러나는가?
개요
이 문서의 주제는 IO Wait와 Disk 병목이다. iowait, disk queue, latency를 보고 로그와 파일 I/O 병목을 판단하는 방법을 정리한다.
먼저 붙잡을 기준은 다음 문장이다. disk 병목은 CPU가 놀고 있는 것처럼 보이는 순간에도 응답 시간을 밀어 올릴 수 있다. 로그 flush, 파일 업로드, dump 생성, swap, 로컬 DB I/O가 같은 장치를 공유하면 Java 코드의 한 줄보다 storage latency가 API 지연을 더 크게 만든다.
핵심 모델
- 판단 기준: disk 병목은 응답 지연으로 번진다.
- iowait는 CPU가 I/O 완료를 기다린 시간 비율이지, 어떤 파일이나 코드가 느린지 직접 말해 주는 지표는 아니다.
await, queue depth, device utilization을 함께 봐야 장치 latency와 요청 적체를 분리할 수 있다.- 로그, heap dump, GC log, upload, swap, DB 파일이 같은 disk를 쓰면 한 작업의 I/O가 다른 요청의 tail latency로 전파된다.
- Java thread dump에서
RUNNABLE로 보이는 thread도 native I/O나 logging append 경로에서 실제로는 disk 완료를 기다릴 수 있다. - 장애 대응은 I/O를 만드는 작업을 줄이는 임시 완화와 storage 경로를 바꾸는 근본 수정을 분리한다.
문서별 핵심 구분
- iowait는 CPU가 I/O 완료를 기다리는 시간이다.
- disk await와 queue depth는 latency 원인을 좁히는 지표다.
- 로그, dump, DB, upload가 같은 disk를 공유하면 병목이 겹친다.
원리
Disk 병목은 CPU bound와 다르게 CPU 사용률이 낮아도 요청 시간이 길어질 수 있다. 커널이 block I/O 완료를 기다리는 동안 task가 runnable하게 계산하지 못하면 %wa와 load average가 같이 움직일 수 있다.
iostat의 await는 장치 관점의 평균 응답 시간이고, aqu-sz나 queue 관련 값은 밀린 요청의 크기를 보여 준다. %util이 높고 await도 같이 높으면 장치가 포화됐을 가능성이 커지지만, storage 종류와 가상화 계층에 따라 %util 하나만으로 결론을 내리면 위험하다.
Java/Spring 장애에서는 disk 병목이 단독으로 나타나기보다 logging, file upload, report export, heap dump, GC log, swap, 로컬 DB I/O와 얽힌다. 따라서 같은 시각의 iostat, vmstat, pidstat -d, thread dump를 맞춰 봐야 disk 대기가 어떤 요청 경로와 연결되는지 좁힐 수 있다.
Java/Spring 연결
- 동기 log appender가 느린 disk에 묶이면 요청 thread가 로그 기록 시간까지 함께 부담한다.
- 대용량 upload/download, report export, batch file write는 CPU보다 disk queue를 먼저 밀어 올릴 수 있다.
- heap dump, thread dump 대량 저장, GC log rotation은 장애 분석 도구이면서 동시에 같은 disk에 추가 부하를 만들 수 있다.
- Actuator의 JVM 지표만으로 disk 병목을 확정할 수 없으므로 OS disk 지표와 application log 시각을 반드시 맞춘다.
- async logging을 쓰더라도 queue가 가득 차면 drop, blocking, memory 증가 중 하나의 형태로 애플리케이션에 되돌아온다.
코드와 설정 예시
date -Is
iostat -xz 1 5
pidstat -d -p <pid> 1 5
vmstat 1 5
jcmd <pid> Thread.print > thread-$(date +%s).txt예시는 disk 지표와 Java 실행 위치를 같은 시간대에 묶어 남기기 위한 최소 묶음이다. pidstat -d로 해당 JVM이 실제 read/write를 만드는지 보고, thread dump에서 logging, upload, report, dump 생성 경로가 같은 시각에 묶여 있는지 확인한다.
관찰 명령
iostat -xz 1 5
vmstat 1 5
pidstat -d -p <pid> 1 5
jcmd <pid> Thread.printIO Wait와 Disk 병목 관찰 명령은 결론이 아니라 가설 확인 도구다. 운영 중에는 반복 간격, 대상 PID, 수집 시각을 함께 남기고 출력이 큰 명령은 범위를 제한한다.
지표 해석
| 신호 | 먼저 의심할 것 | 다음 확인 |
|---|---|---|
%wa와 load 동반 증가 | disk 또는 swap 대기 | iostat, vmstat si/so, D-state task |
await 증가 | storage latency 상승 | device, mount, cloud volume, 로그/DB 경로 |
pidstat -d write 급증 | 특정 JVM 또는 batch가 disk write 생성 | log rotation, dump, upload, export |
| thread dump에 logging/file 경로 집중 | 요청 thread가 I/O 경로에 묶임 | appender 설정, 파일 저장소, async queue |
Disk 분석에서는 로그 쓰기량, await, queue, API latency의 순서를 맞춘다. 쓰기 폭주가 먼저였는지, 이미 느려진 storage에 로그가 뒤따랐는지에 따라 조치가 달라진다.
장애 상황에서 보는 순서
- API 지연이 오른 시각과 disk 관련 작업이 시작된 시각을 맞춘다.
vmstat로%wa,si/so, runnable/block 상태를 먼저 본다.iostat -xz로 device별await, queue, utilization을 확인한다.pidstat -d와 로그 시각으로 어떤 프로세스가 read/write를 만드는지 좁힌다.- 같은 시각의 thread dump에서 logging, upload, dump, report, DB file I/O 경로를 찾는다.
- 임시 완화는 verbose log 축소, dump 중지, batch 지연, upload 제한, 다른 volume 사용 순서로 검토한다.
이 순서는 disk가 의심될 때의 루틴이다. %wa가 높다는 이유만으로 disk를 확정하지 말고, 해당 JVM이 실제 I/O를 만들었는지와 같은 시각의 요청 지연이 함께 움직였는지를 확인한다.
실전 팁
- iowait가 높을 때는 먼저 swap과 disk I/O를 분리한다. memory pressure가 원인인 swap I/O라면 disk 증설보다 heap, cache, batch 크기 조정이 먼저다.
- disk 지표는 평균값에 속기 쉽다. 짧은 쓰기 폭주가 p99 지연을 만들 수 있으므로 1초 간격 샘플과 애플리케이션 latency 타임라인을 같이 본다.
- 장애 중 heap dump를 떠야 한다면 dump 경로가 서비스 로그와 같은 volume인지 확인한다.
- 로그 레벨을 올리는 대응은 원인 분석에는 도움이 되지만, disk 병목 상황에서는 장애를 키울 수 있다.
- 같은 서버에 DB나 message broker가 같이 있으면 JVM만 보지 말고 장치 공유 관계를 먼저 그린다.
주니어 팁
%wa가 높다는 말은 “CPU가 느리다”가 아니라 “I/O 완료를 기다린 시간이 있다”에 가깝다.iostat출력에서 어느 device가 문제인지 보지 않고 전체 평균만 보면 원인을 놓친다.- thread dump에 파일 경로가 직접 보이지 않을 수 있으므로 로그 시각, 파일 크기 증가,
pidstat -d를 함께 본다.
시니어 팁
- 서비스 로그, audit log, access log, dump, upload 임시 파일의 volume을 분리하면 장애 시 쓰기 폭주가 한 지점에 몰리는 위험을 줄일 수 있다.
- async appender는 성능 기능이 아니라 backpressure 정책까지 포함한 운영 설계다. queue overflow 때 block, drop, fail 중 무엇을 선택할지 정해야 한다.
- container 환경에서는 host disk, overlay filesystem, persistent volume의 latency 특성이 다르므로 배포 형태별 기준선을 따로 둔다.
Guru급 팁
- Disk 병목은 mount, volume type, container overlay, host 공유 관계를 알아야 해석된다. device 이름만 기록하면 애플리케이션 경로와 연결하기 어렵다.
- Log, dump, upload, batch output은 volume을 나누고 quota를 둬야 한 작업의 쓰기 폭주가 전체 서비스 latency를 흔들지 않는다.
- Storage를 빠르게 바꾼 뒤에는 iowait가 줄었는지뿐 아니라 bottleneck이 CPU, DB, network로 이동했는지도 다시 확인한다.
위험 신호!
%wa만 보고 disk 병목이라고 확정한다.await가 높은데 어떤 device와 어떤 mount가 서비스 경로인지 모른다.- 장애 중 heap dump와 debug log를 같은 disk에 추가로 쓴다.
- 로그 write 폭주를 줄이지 않고 작업자 스레드 풀 크기만 늘린다.
- storage latency가 계속 높은데 API timeout만 길게 늘린다.
iostat,pidstat, thread dump의 수집 시각이 서로 맞지 않는다.
확인 질문
확인 질문
- iowait가 높을 때 CPU 증설보다 먼저 확인해야 할 것은 무엇인가?
- 어떤 device의
await와 queue가 증가했는지, swap I/O가 있는지, 해당 JVM이 실제 read/write를 만들었는지 확인해야 한다.- Java/Spring 서비스에서 disk 병목이 요청 지연으로 번지는 대표 경로는 무엇인가?
- 동기 logging, file upload/download, report export, heap dump, GC log, 로컬 DB I/O가 요청 thread나 shared disk를 점유하는 경로다.
- disk 병목 상황에서 debug log 확대가 위험한 이유는 무엇인가?
- 원인 관찰을 위해 쓰기량을 늘리는 순간 같은 disk의 queue와 latency가 더 커져 장애를 증폭할 수 있기 때문이다.