CPU iowait가 높은데 CPU는 놀고 있는 이유
서버가 느려졌을 때 가장 먼저 확인하는 지표 중 하나가 CPU 사용률입니다.
그런데 top이나 vmstat을 확인하다 보면 조금 이상한 상황을 만날 때가 있습니다.
CPU의 user와 system 사용률은 높지 않은데 서버 응답은 느리고, 대신 wa 또는 iowait 값이 비정상적으로 높게 나타나는 경우입니다.
예를 들어 다음과 같은 상태입니다.
%Cpu(s): 4.2 us, 2.1 sy, 0.0 ni, 55.3 id, 38.4 wa
CPU는 절반 이상이 idle 상태인데 iowait가 38%에 달합니다.
이 상황에서 단순히 CPU 사용률만 보면 CPU에는 여유가 있다고 판단하기 쉽습니다. 하지만 실제 병목은 CPU가 아니라 CPU가 처리할 데이터를 기다리는 과정에 있을 가능성이 높습니다.
특히 디스크 I/O가 많은 데이터베이스 서버, 로그 서버, 파일 서버에서는 반드시 이해해야 하는 지표가 iowait입니다.
CPU iowait가 높아지는 원인
iowait는 CPU 자체의 처리 능력이 부족해서 발생하는 현상이 아닙니다.
CPU가 실행해야 할 프로세스가 디스크와 같은 블록 장치의 I/O 완료를 기다리면서 실행되지 못하는 상황과 관련된 지표입니다.
예를 들어 애플리케이션이 파일을 읽어야 한다고 가정해보겠습니다.
Application
↓
read()
↓
Linux Kernel
↓
Page Cache 확인
↓
Cache Miss
↓
Disk I/O 요청
↓
SSD / HDD
필요한 데이터가 Page Cache에 존재한다면 메모리에서 빠르게 가져올 수 있습니다.
하지만 Page Cache에 데이터가 없다면 커널은 실제 저장장치에 I/O 요청을 보내야 합니다.
이때 디스크에서 데이터가 도착할 때까지 해당 프로세스는 실행을 계속할 수 없습니다.
프로세스 입장에서는 CPU가 필요한 것이 아니라 데이터가 도착하기를 기다리는 상태가 됩니다.
따라서 서버가 느린데 CPU 사용률이 낮다고 해서 반드시 CPU 자원이 충분한 것은 아닙니다.
iowait는 CPU가 디스크를 직접 기다린다는 뜻이 아니다
iowait를 이해할 때 가장 흔한 오해가 있습니다.
iowait 30% = CPU가 디스크를 기다리느라 30% 동안 멈춰 있었다.
엄밀하게는 이렇게 단순하게 해석하면 안 됩니다.
리눅스에서 CPU는 특정 프로세스의 디스크 I/O가 끝날 때까지 가만히 기다리지 않습니다.
프로세스 A가 I/O를 요청했다고 가정해보겠습니다.
Process A
↓
Disk Read 요청
↓
I/O Wait 상태
A가 기다리는 동안 실행 가능한 Process B가 있다면 CPU는 B를 실행합니다.
CPU
├─ Process A → I/O Wait
└─ Process B → Running
즉 다른 runnable task가 충분하다면 CPU는 계속 일할 수 있습니다.
문제는 I/O를 기다리는 작업은 존재하지만 CPU에서 실행할 다른 작업이 부족한 경우입니다.
이때 CPU가 idle 상태에 머무르게 되고 커널의 CPU accounting에서 일부 시간이 iowait로 계산될 수 있습니다.
따라서 iowait는 단순한 디스크 사용률이라기보다 I/O 대기와 CPU 유휴 상태가 함께 나타나는 시스템 상태를 보여주는 지표로 보는 편이 정확합니다.
실제 서버에서는 어떤 증상이 나타날까?
iowait가 높아지는 서버에서는 CPU 사용률과 체감 성능이 서로 맞지 않는 현상이 자주 나타납니다.
대표적으로 API 응답 시간이 갑자기 증가할 수 있습니다.
정상
Client
↓
Application
↓
DB / Storage
↓
100ms 응답
스토리지 병목이 발생하면 다음과 같이 바뀝니다.
Client
↓
Application
↓
DB / Storage
↓
I/O Wait
↓
I/O Wait
↓
2~3초 응답
CPU는 여전히 여유가 있기 때문에 CPU 그래프만 보면 장애 원인이 보이지 않을 수도 있습니다.
데이터베이스에서는 쿼리 처리 시간이 증가하고 파일 서버에서는 읽기·쓰기 지연이 발생합니다.
로그를 대량으로 기록하는 서버에서는 로그 쓰기가 밀리면서 애플리케이션까지 영향을 받을 수도 있습니다.
심한 경우 Load Average도 함께 증가합니다.
여기서 중요한 점은 Load Average가 CPU 사용률만 의미하지 않는다는 것입니다.
리눅스에서는 CPU 실행을 기다리는 task뿐 아니라 특정 I/O 대기 상태의 task도 Load Average에 영향을 줄 수 있습니다.
따라서 다음과 같은 상황도 가능합니다.
CPU Usage : 20%
CPU iowait : 40%
Load Average : 15
CPU 사용률은 낮은데 Load Average는 높은 상황입니다.
이 경우 CPU 증설부터 생각하면 문제의 핵심을 놓칠 수 있습니다.
iowait가 높을 때 무엇부터 분석해야 할까?
가장 먼저 전체 CPU 상태를 확인합니다.
top
또는
vmstat 1
vmstat에서는 다음 항목을 함께 보는 것이 좋습니다.
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa
1 12 0 523000 32000 800000 0 0 12000 18000 3000 5000 5 3 42 50
여기서 중요한 값은 b, bi, bo, wa입니다.
wa가 지속적으로 높고 b 값까지 증가한다면 I/O 때문에 대기하는 task가 많아지고 있는지 의심해볼 수 있습니다.
다음 단계에서는 실제 블록 장치 상태를 확인합니다.
iostat -x 1
환경과 버전에 따라 표시되는 항목은 조금 다르지만 일반적으로 다음 값을 중점적으로 봅니다.
Device r/s w/s await aqu-sz %util
sda 500 800 35.2 8.4 98.5
특히 await는 I/O 요청이 완료되는 데 걸리는 평균 시간을 판단하는 데 도움이 됩니다.
aqu-sz 같은 큐 관련 지표가 지속적으로 커진다면 요청이 저장장치에서 밀리고 있을 가능성을 살펴봐야 합니다.
%util도 참고할 수 있지만 이것 하나만 보고 디스크 포화 여부를 단정해서는 안 됩니다. SSD, NVMe, RAID, 가상화 스토리지처럼 병렬 처리가 가능한 장치에서는 %util의 의미를 장치 특성과 함께 해석해야 합니다.
어떤 프로세스가 I/O를 발생시키는지도 찾아야 한다
디스크가 느리다는 사실만 알아서는 문제를 해결하기 어렵습니다.
결국 누가 I/O를 발생시키고 있는가를 찾아야 합니다.
대표적으로 사용할 수 있는 도구가 iotop입니다.
iotop
프로세스별 읽기·쓰기 I/O를 확인할 수 있기 때문에 특정 프로세스가 디스크 부하를 집중적으로 발생시키는지 찾는 데 유용합니다.
예를 들어 다음과 같은 상황이 발견될 수 있습니다.
mysqld DISK READ 120M/s
backup.sh DISK READ 350M/s
java DISK WRITE 80M/s
서비스가 느려진 시간이 백업 작업과 겹친다면 단순히 디스크 성능 부족이라고 결론 내리기 전에 백업 프로세스가 서비스 I/O와 경쟁하고 있는지 확인해야 합니다.
프로세스 상태도 함께 볼 수 있습니다.
ps -eo state,pid,comm,wchan:32
특정 프로세스가 반복적으로 D 상태에 머무는지도 중요한 단서입니다.
D는 일반적으로 uninterruptible sleep 상태이며 I/O 완료 등을 기다릴 때 관찰될 수 있습니다.
다만 D 상태 프로세스가 존재한다는 사실 하나만으로 디스크 장애라고 판단해서는 안 됩니다.
iowait가 높다고 무조건 디스크가 느린 것은 아니다
운영 환경에서 가장 주의해야 할 부분입니다.
iowait가 높으면 가장 먼저 디스크를 의심하게 되지만 원인은 다양할 수 있습니다.
예를 들어 가상머신이라면 실제 물리 스토리지를 여러 VM이 공유할 수 있습니다.
VM A ─┐
VM B ─┼─ Hypervisor ─ Storage
VM C ─┘
VM 내부에서는 자신의 디스크 문제처럼 보이지만 실제 원인은 호스트 또는 공유 스토리지에 있을 수 있습니다.
클라우드 환경에서는 볼륨의 IOPS 또는 throughput 제한에 도달했는지도 확인해야 합니다.
또 다른 원인은 메모리 부족입니다.
메모리가 부족하면 Page Cache를 충분히 유지하지 못하고 필요한 데이터를 저장장치에서 반복적으로 읽게 될 수 있습니다.
더 심해지면 Swap I/O까지 발생합니다.
Memory 부족
↓
Page Cache 감소
↓
Disk Read 증가
↓
Swap 발생 가능
↓
Storage I/O 증가
↓
Application 지연
따라서 iowait 문제를 해결할 때는 CPU와 디스크만 보는 것이 아니라 메모리와 Page Cache까지 함께 분석해야 합니다.
iowait를 낮추는 해결 방법
가장 먼저 해야 할 일은 저장장치를 무조건 더 빠른 제품으로 교체하는 것이 아닙니다.
I/O가 왜 발생하는지부터 찾아야 합니다.
데이터베이스 서버라면 비효율적인 쿼리가 대량의 데이터를 읽고 있지 않은지 확인해야 합니다.
예를 들어 인덱스를 사용하지 못하는 쿼리는 필요한 데이터보다 훨씬 많은 블록을 읽을 수 있습니다.
잘못된 쿼리
↓
Full Scan
↓
대량 Disk Read
↓
I/O Queue 증가
↓
Latency 증가
로그 서버라면 지나치게 많은 로그를 동기 방식으로 기록하고 있지 않은지 살펴봐야 합니다.
백업 작업이 원인이라면 서비스 피크 시간과 백업 시간을 분리하거나 I/O 우선순위를 조절하는 방법도 고려할 수 있습니다.
메모리가 부족해서 Page Cache 효율이 떨어지는 경우라면 메모리 증설이 오히려 스토리지 교체보다 효과적일 수도 있습니다.
결국 해결 방법은 다음과 같은 방향으로 나뉩니다.
- 불필요한 Disk I/O 자체를 줄인다.
- 데이터베이스 쿼리와 인덱스를 최적화한다.
- Page Cache를 활용할 수 있도록 충분한 메모리를 확보한다.
- 백업·배치 작업과 서비스 I/O 시간을 분리한다.
- 스토리지 IOPS와 throughput 한계를 확인한다.
- 필요하다면 더 빠른 SSD/NVMe 또는 적절한 스토리지 구조로 변경한다.
- 가상화 환경에서는 호스트와 공유 스토리지까지 확인한다.
중요한 것은 iowait 수치를 낮추는 것 자체가 목적이 아니라 실제 I/O 병목의 원인을 제거하는 것입니다.
정리
CPU iowait는 서버 성능 분석에서 자주 오해되는 지표입니다.
iowait가 높다고 해서 CPU 성능이 부족하다는 뜻도 아니고, 단순히 CPU가 디스크 앞에서 아무 작업도 하지 못한 채 기다리고 있다는 의미로 해석해서도 안 됩니다.
서버 운영에서는 다음 흐름으로 분석하는 것이 좋습니다.
서버 응답 지연
↓
CPU us / sy / id / wa 확인
↓
iowait 증가 확인
↓
vmstat로 blocked task와 I/O 확인
↓
iostat로 장치 latency / queue 확인
↓
iotop 등으로 I/O 발생 프로세스 확인
↓
DB / Memory / Page Cache / Swap 확인
↓
Storage 구조와 한계 확인
특히 기억해야 할 핵심은 이것입니다.
CPU가 놀고 있다고 해서 서버에 여유가 있는 것은 아닙니다.
CPU가 처리할 데이터가 제때 도착하지 않는다면 CPU 사용률이 낮은 상태에서도 서비스는 충분히 느려질 수 있습니다.
그래서 실제 서버 장애 분석에서는 CPU 사용률 하나만 보는 것이 아니라 iowait, Load Average, 블록 장치 latency, I/O queue, 메모리, Page Cache를 하나의 흐름으로 연결해서 봐야 합니다.
이 관점을 이해하면 **“CPU 사용률은 낮은데 왜 서버는 느리지?”**라는 상황에서 CPU 증설이라는 잘못된 방향으로 가는 대신 실제 병목이 발생한 지점을 훨씬 빠르게 찾아낼 수 있습니다.




댓글 0
첫 댓글을 남겨보세요.