리눅스 vmstat 명령어 사용법 - 스왑·CPU wait로 서버 느림의 원인 좁히기
이 명령어를 언제 쓰는가
서버 느림 진단의 두 번째 단계다. uptime으로 로드가 높은 것은 확인했는데, 그 로드가 CPU가 모자라서인지, 메모리가 모자라 스왑이 돌아서인지, 디스크가 밀려서인지는 아직 모른다. vmstat 한 화면이면 이 셋을 동시에 볼 수 있다.
top이 "어느 프로세스가 범인인가"를 보여준다면 vmstat은 "시스템 전체가 어느 자원에서 막혔는가"를 보여준다. 그래서 순서가 중요하다. vmstat으로 병목 자원을 먼저 특정하고, 그다음에 top으로 프로세스를 지목하거나 iostat으로 디스크를 파고든다. 반대로 하면 엉뚱한 프로세스를 범인으로 지목하기 쉽다.
기본 형식
vmstat [옵션] [지연초 [횟수]]
vmstat 1이면 1초마다 계속, vmstat 5 12면 5초 간격으로 12번(총 1분) 출력한다. 인자를 주지 않고 그냥 vmstat만 치면 부팅 이후 전체 평균 한 줄이 나오는데, 이 값은 진단에 거의 쓸모가 없다. 이유는 아래에서 설명한다.
vmstat은 procps(procps-ng) 패키지에 들어 있어 대부분의 리눅스 배포판에 기본 설치돼 있다. macOS에는 vmstat이 없고 형식이 전혀 다른 vm_stat(밑줄 있음)이 있으므로 이 문서의 내용은 리눅스 기준이다.
자주 쓰는 옵션
| 옵션 | 의미 | 예시 |
|---|---|---|
| -S M | 메모리 열을 MB 단위로(기본은 KB라 자릿수가 길다) | vmstat -S M 1 |
| -w | 열 너비를 넓혀 값이 붙지 않게 | vmstat -w 1 |
| -t | 각 줄에 타임스탬프 추가. 로그로 남길 때 필수 | vmstat -t 5 |
| -a | buff/cache 대신 active/inactive 표시 | vmstat -a 1 |
| -d | 디스크별 누적 I/O 통계 | vmstat -d |
| -s | 메모리·이벤트 누적값을 세로 목록으로 | vmstat -s -S M |
출력 읽는 법
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
2 0 0 234156 81424 1502340 0 0 12 31 88 164 3 1 96 0 0
1 0 0 233904 81424 1502340 0 0 0 0 412 903 5 2 93 0 0
| 열 | 의미 | 주목할 때 |
|---|---|---|
| r | CPU를 기다리며 실행 대기 중인 프로세스 수 | 코어 수보다 계속 크면 CPU 병목 |
| b | I/O 등을 기다리며 블록된(D 상태) 프로세스 수 | 0이 아닌 값이 지속되면 디스크·NFS 의심 |
| swpd | 스왑에 나가 있는 메모리 총량(KB) | 커도 si/so가 0이면 과거 흔적일 뿐 |
| free | 어디에도 쓰이지 않는 메모리 | 작은 것 자체는 정상 |
| buff / cache | 블록 버퍼 / 페이지 캐시 | 필요하면 회수되는 여유분 |
| si / so | 초당 스왑 인 / 스왑 아웃(KB) | 0이 아니면 실제 메모리 부족 |
| bi / bo | 초당 블록 장치에서 읽음 / 씀 | wa와 함께 보면 디스크 부하량 |
| in / cs | 초당 인터럽트 / 컨텍스트 스위치 | cs 폭증은 스레드 과다·락 경합 |
| us / sy | 사용자 / 커널 CPU 비율 | sy가 높으면 시스템 콜·인터럽트 과다 |
| id / wa | 유휴 / I/O 대기 비율 | wa가 높으면 iostat 단계로 |
| st | 하이퍼바이저에 빼앗긴 CPU 시간 | 클라우드에서 0이 아니면 이웃 VM 영향 |
판단 기준 - 어느 열을 보고 무엇을 결론 내는가
CPU 병목이다. r이 코어 수(nproc)보다 지속적으로 크고 us가 높으며 wa는 0에 가깝다. 다음 단계는 top으로 CPU를 먹는 프로세스를 찾는 것이다.
메모리 부족이다. si와 so가 매 초 0이 아니다. 이것은 지금 이 순간 디스크와 메모리 사이로 페이지가 오가고 있다는 뜻이고, 체감 성능을 가장 크게 떨어뜨리는 상태다. 반대로 swpd가 2GB라도 si/so가 계속 0이면 문제가 아니다. 예전에 한 번 밀려 나간 페이지가 그냥 스왑에 남아 있을 뿐이고 아무도 그걸 다시 찾지 않는다는 뜻이다. 이 구분을 못 해서 멀쩡한 서버의 메모리를 증설하는 일이 흔하다.
디스크 병목이다. wa가 두 자릿수로 지속되고 b가 0보다 크며 bi/bo가 크다. 여기서 vmstat의 역할은 끝난다. 어느 장치가 얼마나 밀리는지는 iostat -xz 1로 넘어간다.
가상화 문제다. st가 5% 이상 꾸준히 잡히면 내 서버가 아니라 호스트가 붐비는 것이다. 애플리케이션을 아무리 튜닝해도 해결되지 않으므로 인스턴스 타입을 바꾸거나 재배치를 요청해야 한다.
실전 예제
1) 부하 상황을 실시간으로 관찰한다(가장 기본).
vmstat -S M 1
첫 줄은 무시하고 두 번째 줄부터 읽는다.
2) 1분간 5초 간격으로 표본을 뜬다(보고서용).
vmstat -S M -w 5 12
3) 야간에만 느려지는 현상을 잡기 위해 타임스탬프와 함께 로그로 남긴다.
nohup vmstat -S M -t 10 > /var/log/vmstat-$(date +%F).log 2>&1 &
이렇게 남겨 두면 다음 날 아침 "느렸다"는 시각의 줄만 찾아보면 된다. -t가 없으면 몇 시 기록인지 알 수 없어 무용지물이 되므로 반드시 붙인다.
4) 스왑이 실제로 오가는지만 빠르게 확인한다.
vmstat 1 10 | awk 'NR > 3 {print $7, $8}'
7번째와 8번째 필드가 si와 so다. 계속 0 0이면 스왑은 범인이 아니다.
5) 부팅 이후 누적 통계를 요약해서 본다.
vmstat -s -S M | head -20
6) 디스크별 누적 읽기/쓰기를 확인해 어느 장치가 바쁜지 감을 잡는다.
vmstat -d | head
함정과 주의점
첫 줄은 반드시 버려야 한다. vmstat의 첫 출력 줄은 지금 상태가 아니라 부팅 이후 전체 평균이다. 한 달째 떠 있는 서버라면 방금 시작된 부하가 한 달 평균에 희석되어 완전히 정상으로 보인다. "vmstat을 쳐 봤는데 멀쩡하던데요"라는 보고의 대부분이 이 첫 줄만 본 경우다. 항상 간격을 주고 두 번째 줄부터 읽는다.
버전에 따라 열이 하나 더 있다. procps-ng 4.x부터 cpu 영역에 게스트 시간을 뜻하는 gu 열이 추가됐다. awk로 필드 번호를 박아 파싱하는 스크립트를 여러 배포판에 돌린다면 열 위치가 밀릴 수 있으니 vmstat -V로 버전을 확인하거나 헤더 이름으로 인덱스를 찾는 편이 안전하다.
컨테이너 안의 vmstat은 호스트 전체 수치다. vmstat은 /proc/stat, /proc/vmstat, /proc/meminfo를 읽는데 이 파일들은 cgroup으로 격리되지 않는다. 도커 컨테이너 안에서 본 free나 wa는 컨테이너가 아니라 호스트의 값이다. 컨테이너별 자원은 /sys/fs/cgroup 아래 통계나 컨테이너 모니터링 도구로 봐야 한다.
wa가 낮다고 디스크가 한가한 것은 아니다. wa는 "CPU가 놀면서 I/O를 기다린 시간 비율"이다. CPU가 바쁘면 같은 디스크 지연이라도 wa로 잡히지 않는다. 그래서 wa가 0이어도 b 열이 계속 0보다 크면 디스크를 의심해야 한다.
순간값에 놀라지 않는다. 로그 로테이트나 백업이 도는 몇 초 동안은 bi/bo와 wa가 치솟는 것이 정상이다. 최소 30초 이상 관찰해 지속되는지를 기준으로 판단한다.
함께 보면 좋은 명령어
- uptime - vmstat을 보기 전에 부하가 있는지부터 확인하는 앞 단계다.
- iostat - wa가 높을 때 어느 디스크가 얼마나 밀리는지 확인하는 다음 단계다.
- top - vmstat이 지목한 병목 자원을 실제로 먹는 프로세스를 찾는다.