vmstat
고친 사람 github-actions[bot]
vmstat 은 서버의 자원 상태를 몇 초마다 한 줄씩 찍어 주는 명령입니다. 계산이 밀리는지, 메모리가 모자란지, 디스크를 기다리는지를 한 화면에 함께 보여 줍니다. 서버가 느려졌을 때 어느 자원이 막혔는지 가장 먼저 가늠해 보는 진단 명령입니다.
쉽고 빠른 이해
vmstat 은 기계 전체가 얼마나 바쁜지를 시간 순으로 찍는 명령입니다. CPU(Central Processing Unit, 중앙 처리 장치)는 계산을 맡는 장치입니다. vmstat 1 을 치면 1초마다 「돌고 싶은 프로세스가 6개, CPU 가 쉰 시간은 2%」 같은 줄이 하나씩 붙습니다.
서버가 느릴 때 원인은 CPU, 메모리, 디스크 어디든 될 수 있습니다. 이 명령이 없으면 도구를 하나씩 바꿔 가며 짐작으로 좁혀야 합니다.
- 커널이 세어 둔 숫자를 정한 간격마다 읽습니다
- 그 간격 동안 무슨 일이 얼마나 있었는지 셉니다
- 프로세스, 메모리, 스왑(메모리가 모자랄 때 내용을 옮겨 두는 디스크 공간), 디스크, 시스템, CPU 여섯 묶음을 한 줄에 찍습니다
대가가 있습니다. 기계 전체를 더한 값이라 어느 프로세스가 원인인지는 안 보입니다. 첫 줄은 지금이 아니라 부팅 뒤 평균이라 잘못 읽기 쉽습니다.
상세
vmstat 은 virtual memory statistics(가상 메모리 통계)를 줄인 이름입니다. 이름은 메모리를 말하지만 CPU 와 디스크 상태까지 함께 찍습니다. 리눅스와 FreeBSD 같은 유닉스 계열 운영체제에 들어 있는 명령입니다.
백엔드 개발자는 대개 서버가 느려졌을 때 이 명령을 꺼냅니다. 아직 이유를 모를 때입니다. 응답이 늦는 원인은 CPU 가 모자라서일 수 있습니다. 메모리가 모자라 디스크를 오가서일 수도 있습니다. 디스크 읽기를 기다리느라 멈춰 있을 수도 있습니다. vmstat 은 이 셋을 한 줄에 나란히 찍어 어느 쪽부터 파 볼지 정하게 해 줍니다.
이 절은 먼저 명령을 치는 법과 출력 모양을 봅니다. 그다음 여섯 칸 묶음을 하나씩 풉니다. 이어서 응답이 늦을 때 읽는 순서와 이 명령이 못 보는 것을 봅니다. 옵션과 칸 이름은 리눅스 기준입니다.
간격과 횟수
vmstat 은 숫자 두 개를 받습니다. 앞 숫자는 몇 초마다 찍을지, 뒤 숫자는 몇 번 찍을지입니다.
vmstat # 한 줄만 찍고 끝
vmstat 1 # 1초마다, 끊을 때까지
vmstat 2 5 # 2초마다 5줄
간격만 주면 Ctrl+C 로 끊을 때까지 계속 찍습니다. 진단할 때는 흔히 vmstat 1 을 띄워 두고 수십 초 동안 숫자가 어떻게 움직이는지 봅니다.
출력 모양
아래는 CPU 가 넷인 서버에서 vmstat 1 3 을 친 모습입니다. 숫자는 예로 지어낸 것입니다.
$ vmstat 1 3
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 812340 65432 2345678 0 0 5 12 80 150 3 1 96 0 0
6 0 0 810112 65432 2345690 0 0 0 48 2100 5400 88 10 2 0 0
5 0 0 809980 65432 2345702 0 0 0 16 2050 5300 90 9 1 0 0
맨 위 줄은 칸을 여섯 묶음으로 가르는 머리말입니다. 둘째 줄이 칸 이름입니다. 그 아래 세 줄이 값입니다.
칸마다 세는 대상과 단위가 달라서 숫자를 칸끼리 견주지는 않습니다. 같은 칸이 줄마다 어떻게 움직이는지를 봅니다. 아래 소절에서 묶음을 하나씩 풉니다. 이 출력은 「응답이 늦을 때 읽는 순서」에서 다시 읽습니다.
첫 줄은 부팅 뒤 평균
첫 값 줄은 지금 상태가 아닙니다. 컴퓨터가 켜진 뒤로 지금까지의 평균입니다. 둘째 값 줄부터가 방금 지난 간격 동안의 값입니다.
위 출력의 첫 값 줄은 CPU 가 96% 쉬고 있습니다. 둘째 줄부터는 쉰 몫(id)이 2% 로 떨어졌습니다. 방금 1초 동안 CPU 가 거의 쉬지 못했다는 뜻입니다.
이 차이를 모르면 첫 줄만 보고 「한가하다」고 잘못 판단합니다. 며칠 동안 한가하던 서버는 방금 바빠져도 첫 줄의 평균이 거의 안 움직입니다.
그래서 간격 없이 vmstat 을 한 번 치는 것으로는 지금 무슨 일이 나는지 알 수 없습니다.
메모리 양을 적는 네 칸(swpd · free · buff · cache)만은 첫 줄에서도 그 순간의 값입니다. 네 칸의 뜻은 아래 memory 소절에서 풉니다.
procs — 실행을 기다리는 프로세스
CPU 하나는 한 순간에 프로세스 하나만 돌립니다. 돌고 싶은 프로세스가 CPU 보다 많으면 나머지는 차례를 기다립니다. 이렇게 기다리는 줄을 실행 대기열이라 부릅니다.
r 은 지금 도는 프로세스와 실행 대기열에서 기다리는 프로세스를 합친 수입니다. 그래서 r 이 CPU 개수보다 계속 크면 그 차이만큼 CPU 앞에 줄이 서 있는 것입니다.
위 예에서 CPU 넷에 r 이 6 이면 둘이 기다리는 중입니다.
b 는 CPU 가 아니라 입출력을 기다리며 멈춘 프로세스 수입니다. 디스크 읽기가 끝나야 다음 줄을 실행할 수 있는 프로세스가 여기 들어갑니다.
이 값이 계속 0 보다 크면 디스크나 네트워크로 붙인 저장소의 응답이 늦다는 신호입니다.
memory — 메모리를 어디에 썼나
메모리가 모자라면 커널은 한동안 안 쓰인 메모리 내용을 디스크로 옮겨 메모리를 비웁니다. 이때 쓰는 디스크 공간을 스왑이라 부릅니다. swpd 는 스왑으로 내려가 있는 양입니다.
free 는 아무 데도 안 쓰이고 비어 있는 메모리입니다. buff 와 cache 는 커널이 디스크 내용을 메모리에 담아 둔 양입니다.
디스크에서 읽는 데는 메모리보다 훨씬 오래 걸립니다. 그래서 커널은 한 번 읽은 파일 내용을 남는 메모리에 담아 두고 다음 읽기에 다시 씁니다.
cache 칸은 이렇게 담아 둔 파일 내용, 곧 페이지 캐시의 양입니다.
디스크는 정해진 크기의 덩어리 단위로 읽고 씁니다. 이 덩어리를 블록이라 합니다. 커널은 파일을 거치지 않고 디스크의 블록을 그대로 메모리에 담아 두기도 합니다.
이렇게 블록째 담아 둔 메모리가 버퍼입니다. buff 칸이 그 양입니다.
리눅스는 남는 메모리를 캐시로 채워 두므로 오래 켜진 서버일수록 free 가 작습니다. 프로그램이 메모리를 더 달라고 하면 커널은 캐시를 비워서 내줍니다.
그래서 free 가 작다는 것만으로는 메모리가 모자란 것이 아닙니다. 모자란지는 다음 소절의 si 와 so 로 가립니다.
메모리 칸의 기본 단위는 KiB(kibibyte, 1024 바이트)입니다. -S M 을 붙이면 MiB(mebibyte, 1024×1024 바이트) 단위로 찍습니다.
swap — 디스크를 오가는 메모리
si 는 스왑에서 메모리로 다시 읽어 들인 양입니다. so 는 메모리에서 스왑으로 내보낸 양입니다. 둘 다 초당 값입니다.
이 두 칸이 메모리 부족의 가장 분명한 신호입니다. 프로그램들이 쓰려는 메모리가 실제 메모리보다 커지면 커널은 내보냈다 다시 들이기를 쉬지 않고 되풀이합니다. 그러면 CPU 가 남아도 모든 프로그램이 디스크를 기다리느라 느려집니다. 이 상태를 스래싱이라 부릅니다.
swpd 가 0 보다 크다는 것만으로는 문제가 아닙니다. 예전에 한 번 내보낸 뒤로 다시 안 쓰인 메모리일 수 있습니다. 볼 것은 si 와 so 가 줄마다 0 이 아닌 값으로 이어지는지입니다.
io — 디스크와 오간 양
디스크처럼 블록 단위로 읽고 쓰는 장치를 블록 장치라 합니다. bi 는 블록 장치에서 읽어 들인 양입니다. bo 는 블록 장치로 써 보낸 양입니다. 둘 다 초당 값입니다.
이 두 칸은 디스크가 얼마나 바쁜지 대략 보여 줍니다. 로그를 많이 쓰는 서버는 bo 가 커집니다. 큰 파일을 읽는 배치 작업은 bi 가 커집니다.
bi · bo 를 어떤 단위로 세는지는 확인 못 했습니다(미확인). 숫자의 크기보다 0 인지, 평소보다 큰지로 읽습니다.
디스크 장치마다 나눠 보려면 iostat 을 씁니다.
system — 인터럽트와 문맥 교환
인터럽트는 장치가 CPU 에게 「일이 생겼다」고 알리는 신호입니다. 네트워크 카드에 패킷이 도착하거나 디스크 읽기가 끝나면 인터럽트가 옵니다.
in 은 초당 인터럽트 수입니다. 시계가 정해진 간격마다 보내는 인터럽트도 이 수에 들어갑니다.
CPU 가 돌리던 프로세스나 스레드를 멈추고 다른 것으로 바꿔 돌리는 일을 문맥 교환이라 합니다. 바꿀 때마다 지금 상태를 저장하고 다음 것의 상태를 불러와야 해서 쓸모 있는 일 없이 시간이 듭니다.
cs 는 초당 문맥 교환 수입니다.
cs 가 평소보다 크게 뛰면 흔히 스레드가 너무 많거나 락을 두고 다투는 일이 잦을 때입니다. 락은 여러 스레드가 한 자원을 한 번에 하나씩만 쓰게 막는 잠금입니다. 락을 못 잡은 스레드는 잠들었다 깨기를 되풀이합니다. 그때마다 문맥 교환이 일어납니다.
얼마가 많은 것인지는 서버마다 달라서 평소 값과 견주어 봅니다.
cpu — CPU 시간을 어디에 썼나
CPU 는 프로그램 자신의 코드를 돌린 시간과 커널의 코드를 돌린 시간을 나눠 셉니다. 프로그램 자신의 코드를 도는 상태를 사용자 모드라 부릅니다. 이 시간이 us 칸입니다.
커널의 코드를 도는 상태는 커널 모드라 부릅니다. 이 시간이 sy 칸입니다.
프로그램이 파일을 읽거나 네트워크로 보내려면 시스템 콜로 커널에 부탁해야 합니다. 그동안은 커널 모드로 돕니다. 시스템 콜이 잦은 프로그램일수록 sy 가 커집니다.
wa 는 헷갈리기 쉬운 칸입니다. CPU 가 할 일이 없어 쉬는 시간 가운데 입출력을 기다리는 프로세스가 있던 시간을 wa 로 셉니다.
그러니 wa 는 CPU 가 바쁜 시간이 아니라 쉬는 시간의 한 갈래입니다. CPU 가 다른 일로 바쁘면 디스크를 기다리는 프로세스가 있어도 wa 가 안 오릅니다.
클라우드 서버는 대개 물리 서버 한 대를 나눠 쓰는 가상 머신입니다. 물리 CPU 는 그 위의 가상 머신들이 번갈아 씁니다.
st 는 내 가상 머신에 돌 일이 있는 동안 물리 CPU 가 다른 가상 머신을 돌리느라 못 돈 시간입니다. 이름은 steal(빼앗김)에서 왔습니다.
다섯 칸은 CPU 시간을 나눈 백분율이라 더하면 100 이 됩니다. 칸마다 크면 의심할 것은 아래와 같습니다.
| 칸 | 뜻 | 크면 의심할 것 |
|---|---|---|
us |
사용자 모드로 프로그램 코드를 돈 시간 | 애플리케이션의 계산이 많다 |
sy |
커널 모드로 돈 시간 | 시스템 콜이나 문맥 교환이 너무 잦다 |
id |
쉰 시간 | 작을수록 CPU 가 바쁘다 |
wa |
쉬는 동안 입출력을 기다린 프로세스가 있던 시간 | 디스크가 요청을 다 못 받아 밀려 있다 |
st |
다른 가상 머신에게 빼앗긴 시간 | 같은 물리 서버의 이웃이 CPU 를 많이 쓴다 |
판에 따라 vmstat 은 맨 끝에 gu 칸을 하나 더 찍습니다. 이 서버가 띄운 가상 머신의 코드를 돌린 시간입니다. 가상 머신을 띄우지 않는 서버에서는 늘 0 입니다.
이 칸이 붙을 때 여섯 칸을 더해 100 이 되는지는 확인 못 했습니다(미확인).
응답이 늦을 때 읽는 순서
칸이 많아서 읽는 순서를 정해 두면 덜 헷갈립니다. 아래 그림은 흔히 쓰는 순서입니다.
flowchart TD
A{"vmstat 1 에서 si · so 가 계속 0 이 아닌가"}
A -->|"그렇다"| B["메모리 부족 · 스왑을 오가고 있다"]
A -->|"아니다"| C{"r 이 CPU 개수보다 계속 큰가"}
C -->|"그렇다"| D["CPU 부족 · us 가 크면 애플리케이션, sy 가 크면 커널 쪽"]
C -->|"아니다"| E{"wa 가 크고 b 가 0 보다 큰가"}
E -->|"그렇다"| F["디스크 대기 · iostat 으로 장치를 본다"]
E -->|"아니다"| G["자원은 여유 · 락이나 외부 호출을 본다"]
메모리를 먼저 보는 까닭이 있습니다. 스왑을 오가기 시작하면 wa 와 b 가 함께 오르고 CPU 칸도 흔들립니다. 디스크부터 보면 메모리 문제를 디스크 문제로 잘못 읽게 됩니다.
자원이 모두 여유로운 채로 느리면 문제는 이 서버 밖에 있기 쉽습니다. 프로세스는 락이나 데이터베이스, 다른 서비스의 응답을 기다리는 동안 CPU 도 디스크도 거의 안 씁니다.
클라우드 서버라면 st 도 함께 봅니다. st 가 계속 크면 내 프로그램과 상관없이 CPU 를 덜 받고 있는 것입니다.
「출력 모양」의 예를 이 순서로 읽어 봅니다. si 와 so 는 0 이라 메모리는 넉넉합니다. r 이 6 과 5 로 CPU 넷보다 큽니다. us 는 88 과 90 입니다.
애플리케이션 코드가 CPU 를 다 쓰고도 모자라 줄을 서는 상태입니다.
자주 붙이는 옵션
간격과 횟수 말고 진단에 흔히 쓰는 옵션 넷은 아래와 같습니다.
| 옵션 | 하는 일 |
|---|---|
-S M |
메모리 칸을 MiB 단위로 찍는다. si · so · bi · bo 는 안 바뀐다 |
-w |
칸을 넓혀 찍는다. 메모리가 큰 서버에서 숫자가 옆 칸으로 밀리지 않는다 |
-t |
줄마다 찍은 시각을 붙인다 |
-s |
한 줄씩 찍지 않고 누적 통계를 표로 한 번 찍는다 |
-t 는 출력을 파일로 남겨 두고 나중에 장애 시각과 맞춰 볼 때 씁니다.
vmstat 이 못 보는 것
vmstat 은 기계 전체를 더한 값만 찍습니다. CPU 가 바쁘다는 것은 알려 줘도 어느 프로세스가 바쁘게 했는지는 안 알려 줍니다.
CPU 칸도 모든 CPU 를 평균 낸 값입니다. CPU 가 열여섯인 서버에서 스레드 하나가 CPU 하나를 다 써도 us 는 6 남짓입니다.
그래서 「CPU 가 한가하다」는 결론은 CPU 마다 나눠 본 뒤에 내립니다.
vmstat 으로 어느 자원인지 가늠한 다음은 나눠 보는 명령이 맡습니다.
| 명령 | 알려 주는 것 |
|---|---|
| vmstat | 기계 전체의 CPU · 메모리 · 스왑 · 디스크 · 문맥 교환 흐름 |
| top | 프로세스마다 쓰는 CPU 와 메모리 |
| mpstat | CPU 하나하나의 바쁜 정도 |
| pidstat | 프로세스마다의 CPU · 입출력 · 문맥 교환 |
| iostat | 디스크 장치마다의 읽기 · 쓰기와 바쁜 정도 |
| sar | 같은 종류의 값을 기록해 두었다가 지난 시각의 것을 다시 보기 |
관련 항목
vmstat 이 칸으로 보여 주는 커널 개념
실행 대기열 · 스왑 · 페이지 캐시 · 버퍼 · 블록 장치 · 인터럽트 · 문맥 교환 · 사용자 모드 · 커널 모드 · 시스템 콜 · 가상 메모리 · 프로세스 · 스레드
vmstat 으로 짚어 내는 자원 문제
포화 · 스래싱 · 스와핑 · 입출력 대기 · 스틸 시간 · OOM 킬러 · 락 경합
vmstat 과 진단을 나눠 맡는 명령과 그 도구 묶음
top · htop · iostat · mpstat · pidstat · sar · free · netstat · perf · sysstat · procps
vmstat 숫자를 읽는 성능 분석 틀
성능 · USE 방법 · 사용률 · 로드 애버리지 · PSI
vmstat 이 상태를 읽어 오는 운영체제와 가상화 계층
다른 이름: virtual memory statistics · vmstat 명령