리눅스 iostat 명령어 사용법 - await와 %util로 디스크 병목 판별하기
이 명령어를 언제 쓰는가
서버 느림 진단의 세 번째 단계다. uptime으로 로드가 높은 것을 보고, vmstat에서 wa가 높고 b가 0이 아닌 것을 확인했다면 이제 어느 장치가 문제인지 좁혀야 한다. iostat은 장치별로 초당 요청 수, 처리량, 그리고 요청 하나가 완료되기까지 걸린 평균 시간을 보여준다.
DB 서버에서 쿼리는 그대로인데 응답이 느려졌을 때, 백업이 도는 시간에만 서비스가 밀릴 때, 클라우드에서 디스크 IOPS 한도(버스트 크레딧 소진)에 걸렸는지 확인할 때 쓴다.
설치
iostat은 기본 명령이 아니라 sysstat 패키지에 들어 있다. 최소 설치 서버에는 대개 없다.
sudo apt install sysstat # Ubuntu / Debian
sudo dnf install sysstat # RHEL / Rocky / CentOS Stream
같은 패키지에 들어 있는 sar는 과거 시점의 디스크 통계를 되돌려 볼 수 있어서, 장애가 끝난 뒤 원인을 찾는 상황이라면 오히려 sar가 답이다. Ubuntu는 /etc/default/sysstat에서 ENABLED="true"로 바꿔야 수집이 시작된다.
기본 형식
iostat [옵션] [간격초 [횟수]]
실무에서 거의 항상 쓰는 형태는 하나다.
iostat -xz 1
-x는 확장 통계(await, %util 등)를 켜고, -z는 이번 구간에 아무 활동이 없던 장치를 숨겨 화면을 깨끗하게 만든다. 뒤의 1은 1초 간격이다.
자주 쓰는 옵션
| 옵션 | 의미 | 예시 |
|---|---|---|
| -x | 확장 통계 표시. 이게 없으면 진단이 안 된다 | iostat -x 1 |
| -z | 활동 없는 장치 숨김 | iostat -xz 1 |
| -d | 디스크 통계만(CPU 블록 생략) | iostat -dxz 1 |
| -c | CPU 통계만 | iostat -c 1 |
| -m | KB 대신 MB 단위 | iostat -dxmz 1 |
| -t | 각 리포트에 시각 표시 | iostat -xzt 5 |
| -y | 부팅 이후 평균인 첫 리포트를 생략 | iostat -xzy 1 5 |
| -p 장치 | 파티션 단위까지 표시 | iostat -xp sda 1 |
| -h | 단위를 사람이 읽기 좋게 + 장치명을 오른쪽으로(--human --pretty와 같음) | iostat -xhz 1 |
출력 읽는 법
Device r/s rkB/s r_await w/s wkB/s w_await aqu-sz %util
nvme0n1 12.00 480.00 0.24 340.00 18720.00 1.86 0.65 9.20
실제 -x 출력은 이보다 훨씬 넓다. 위는 판단에 쓰는 열만 추린 것이고, 사이사이에 rrqm/s, %rrqm, rareq-sz, d/s(discard), f/s(flush) 같은 열이 더 끼어 있다. 그래서 필드 번호로 파싱하지 말고 헤더 이름으로 찾아야 한다.
| 열 | 의미 | 판단 |
|---|---|---|
| r/s, w/s | 초당 읽기/쓰기 요청 수 = IOPS | 클라우드 볼륨의 IOPS 한도와 비교한다 |
| rkB/s, wkB/s | 초당 전송량 | 처리량(대역폭) 한도와 비교한다 |
| r_await, w_await | 요청 하나의 평균 완료 시간(ms) | 가장 중요한 지표. 큐 대기 + 서비스 시간 |
| aqu-sz | 평균 큐 길이 | 1을 크게 넘으면 요청이 줄 서 있다 |
| %util | 장치에 요청이 걸려 있던 시간 비율 | SSD에서는 포화도가 아니다(아래 함정 참고) |
기준값을 감으로 잡아 두면 편하다. NVMe SSD는 await가 1ms 미만이 정상이고, SATA SSD는 수 ms, 회전 디스크(HDD)는 10ms 안팎이 정상 범위다. NVMe에서 await가 20ms씩 나오면 그 자체로 이상이다.
참고로 열 이름은 sysstat 버전에 따라 다르다. sysstat 11 이하에서는 avgqu-sz, await, svctm이라는 이름이 쓰였고 12부터 aqu-sz로 바뀌었으며 svctm은 신뢰할 수 없다는 이유로 제거됐다. 오래된 블로그를 볼 때 이 차이를 감안해야 한다.
실전 예제
1) 지금 어느 디스크가 밀리는지 실시간으로 본다.
iostat -xz 1
첫 리포트는 부팅 이후 평균이므로 버리고 두 번째부터 읽는다. 아예 안 보고 싶으면 -y를 붙인다.
2) 5초 간격 표본 5개를 시각과 함께 뜬다(장애 보고서 첨부용).
iostat -dxzty 5 5
3) 백업이 도는 시간대에 디스크가 어떻게 되는지 로그로 남긴다.
nohup iostat -dxzmt 10 > /var/log/iostat-$(date +%F).log 2>&1 &
4) 어떤 프로세스가 디스크를 쓰는지 찾는다(iostat만으로는 알 수 없다).
pidstat -d 1
sudo iotop -oPa
pidstat은 sysstat에 같이 들어 있고, iotop은 별도 패키지지만 프로세스별 실제 읽기/쓰기를 보여준다. iostat이 "디스크가 밀린다"까지 알려주면 그다음 범인 지목은 이 둘의 몫이다.
5) LVM이나 암호화 볼륨의 dm 이름을 실제 이름으로 매핑한다.
lsblk
ls -l /dev/mapper/
6) 파티션 단위로 쪼개 어느 영역이 바쁜지 본다.
iostat -xzp nvme0n1 1
함정과 주의점
첫 리포트는 부팅 이후 평균이다. vmstat과 같은 함정이다. 오래 떠 있는 서버라면 첫 리포트는 언제나 평온해 보인다. iostat -xz 1로 관찰하며 두 번째 리포트부터 읽거나, -y로 첫 리포트를 생략한다.
%util 100%가 SSD에서는 포화를 뜻하지 않는다. %util은 "장치에 최소 한 개의 요청이 걸려 있던 시간 비율"이다. 한 번에 하나씩만 처리하던 회전 디스크 시절에는 이 값이 곧 포화도였지만, NVMe SSD는 수십~수백 개의 요청을 병렬로 처리한다. 그래서 %util이 100%인데 await가 0.3ms라면 전혀 문제가 없는 상태다. SSD에서는 %util 대신 await와 aqu-sz를 기준으로 판단한다. 반대로 %util이 60%인데 await가 50ms라면 그것이 진짜 문제다.
장치 이름을 잘못 보면 엉뚱한 결론이 난다. LVM을 쓰면 dm-0, dm-1 같은 이름이 나오는데 이것이 어느 볼륨인지는 lsblk로 확인해야 한다. 또 LVM이나 소프트웨어 RAID 위의 논리 장치와 그 아래 물리 디스크가 같은 I/O를 이중으로 계상하므로, 합계를 내면 실제보다 부풀려진다. 물리 디스크 줄만 보고 판단하는 것이 안전하다.
클라우드에서는 디스크가 아니라 한도가 원인일 때가 많다. AWS gp2/gp3, GCP PD 등은 볼륨 크기에 비례한 IOPS·처리량 한도가 걸려 있고, 버스트 크레딧을 다 쓰면 갑자기 성능이 떨어진다. 이때 iostat에는 await 급증과 aqu-sz 상승으로 나타나는데 디스크가 고장 난 것이 아니다. r/s + w/s 합계를 볼륨의 IOPS 한도와 비교해 봐야 한다.
await가 높다고 항상 디스크 탓은 아니다. 애플리케이션이 fsync를 남발하거나 큐를 한꺼번에 밀어 넣으면 await가 올라간다. 이 경우 디스크를 교체해도 개선되지 않는다. r/s와 w/s가 장치 사양 대비 낮은데 await만 높다면 애플리케이션의 I/O 패턴을 먼저 의심한다.
macOS의 iostat은 완전히 다른 프로그램이다. BSD 계열 iostat이라 -x가 없고 출력도 KB/t tps MB/s 형태의 세 열뿐이라 await 같은 지연 지표가 아예 없다. macOS에서 디스크 지연을 보려면 fs_usage나 활성 상태 보기를 쓴다.
함께 보면 좋은 명령어
- vmstat - iostat으로 넘어오기 전에 wa와 b 열로 디스크 병목 여부를 먼저 가른다.
- lsof - 특정 프로세스가 어떤 파일을 붙잡고 I/O를 내는지 확인한다.
- df - 디스크가 느린 것이 아니라 가득 찬 것은 아닌지 함께 확인한다.