우리는 PC가 평상시보다 느릴 때 ‘작업 관리자’를 켜곤 합니다. 작업 관리자의 내용을 잘 살펴보면 하드웨어 자원을 유달리 많이 사용하는 프로세스가 있습니다. 이런 프로세스를 강제 종료하면 대체로 정상으로 돌아갑니다. 서버 모니터링의 역할은 PC의 작업 관리자와 비슷합니다. PC 버벅거림 현상과 마찬가지로, 대부분의 IT 서비스 장애는 하드웨어 자원 사용량의 이상 패턴을 동반합니다. 따라서 서버 모니터링은 장애의 예측과 대응을 위한 가장 중요한 도구라고 할 수 있습니다. 오늘은 서버 모니터링의 근간이 되는 원리에 대해 설명하고자 합니다.
리눅스 서버에는 proc이라는 특수한 디렉토리가 있습니다. 리눅스 매뉴얼에서는 커널의 자료구조에 접근할 수 있는 인터페이스를 제공하는 유사 파일 시스템(psuedo-filesystem)이라고 설명합니다. 겉으로 보기엔 다른 파일 시스템과 유사하지만 실제로는 파일로 이루어지지 않았다는 뜻입니다. proc 디렉토리는 일반적인 파일 시스템과는 달리 모든 내용이 메모리에 저장되어 있다는 차이가 있습니다. 때문에 일반적인 파일보다 훨씬 빠르게 내용을 조회할 수 있습니다.
마운트 경로를 변경하지 않았다면
ls /proc
명령어로 proc 파일 시스템의 내용을 조회할 수 있습니다. 다음 캡처는 명령어를 실행한 결과입니다.
의미를 짐작하기 어려운 파일이나 디렉토리도 있지만, cpuinfo, meminfo처럼 쉬운 이름도 있습니다. 다음은
/proc/meminfo
파일의 내용을 출력한 예입니다.
root@localhost:~# cat /proc/meminfo
MemTotal: 7943776 kB
MemFree: 6581156 kB
MemAvailable: 7389244 kB
Buffers: 272652 kB
Cached: 718740 kB
SwapCached: 0 kB
Active: 640872 kB
Inactive: 514776 kB
Active(anon): 1032 kB
Inactive(anon): 153368 kB
Active(file): 639840 kB
Inactive(file): 361408 kB
Unevictable: 29252 kB
Mlocked: 27716 kB
SwapTotal: 0 kB
SwapFree: 0 kB
Dirty: 8 kB
Writeback: 0 kB
AnonPages: 193512 kB
Mapped: 144492 kB
Shmem: 2628 kB
KReclaimable: 55484 kB
Slab: 97024 kB
SReclaimable: 55484 kB
SUnreclaim: 41540 kB
KernelStack: 3168 kB
PageTables: 3444 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 3971888 kB
Committed_AS: 1231864 kB
VmallocTotal: 34359738367 kB
VmallocUsed: 15340 kB
VmallocChunk: 0 kB
Percpu: 1440 kB
HardwareCorrupted: 0 kB
AnonHugePages: 0 kB
ShmemHugePages: 0 kB
ShmemPmdMapped: 0 kB
FileHugePages: 0 kB
FilePmdMapped: 0 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
Hugetlb: 0 kB
DirectMap4k: 187904 kB
DirectMap2M: 4005888 kB
DirectMap1G: 6291456 kB
meminfo는 이름 그대로 메모리에 대한 정보를 담고 있는 파일입니다. 그런데 메모리의 용량을 total, free, available 등으로 구분하는 방식이 어쩐지
free
명령어와 비슷합니다. 실제로 free 명령어를 실행하면 meminfo를 조회했을 때와 거의 동일한 내용이 출력되는 것을 확인할 수 있습니다.
root@localhost:~# free
total used free shared buff/cache available
Mem: 7943776 315968 6580932 2628 1046876 7389024
Swap: 0 0 0
man free
명령어를 통해 도움말 페이지를 읽어보면 free에서 출력하는 정보는 meminfo 파일의 내용을 파싱 해서 수집한다고 설명하고 있습니다. (The information is gathered by parsing /proc/meminfo.) free의 결과가 meminfo와 유사한 것이 단순한 우연이 아님을 알 수 있습니다.
proc에서 알 수 있는 것은 메모리 사용량뿐만이 아닙니다. CPU 사용량, 디스크 I/O 사용량 등 대부분의 서버 모니터링 지표의 기원이 proc 파일 시스템입니다.
proc 파일 시스템의 내용을 이용해 CPU 사용량을 계산하는 실습을 해보겠습니다.
/proc/stat
파일의 내용을 출력하면 다음과 같습니다.
출력 결과의 각 행은 ‘cpu’라는 문자열로 시작하는데요. 두 번째 행부터는 0부터 시작하는 숫자가 차례대로 붙어 있습니다. 이 숫자는 CPU의 각 코어를 구분하기 위해 붙인 것입니다. 첫 행의 수치는 전체 코어의 합계입니다.
이후로는 10개의 수치가 공백으로 구분되어 있습니다. 이 수치들은 부팅 이후 CPU의 사용 시간을 의미합니다. 단위는 100분의 1초를 의미하는 USER_HZ입니다. (엄밀히 말하면 단순히 100분의 1초를 의미하는 단위는 아니지만, 이에 대한 자세한 설명은 글의 주제를 벗어나므로 생략하도록 하겠습니다.) 각 수치의 의미를 왼쪽부터 순서대로 설명하면 다음과 같습니다.
10개의 수치 중 idle, iowait은 CPU를 사용하지 않은 시간임을 알 수 있습니다. 따라서 모든 열의 수치를 합한 후, idle과 iowait을 제외한 시간의 비율을 계산하면 전체 사용량을 구할 수 있습니다.
주의할 점은 특정 시점 t의 CPU 사용량을 구하기 위해선 두 번의 측정이 필요하다는 점입니다. CPU 사용 시간은 부팅 이후 계속 누적 증가하는 수치이므로, t에서 파싱 한 값을 가지고 계산하면 부팅 이후 전체 사용량을 구하게 됩니다. 따라서 t의 CPU 사용 시간이 t-1 시점과 비교해 얼마나 증가하였는지를 계산하여야 합니다.
t 시점의 전체 CPU 사용 시간을 , idle과 iowait을 제외한 실제 사용 시간을 라 할 때 t 시점의 CPU 사용량을 % 단위로 구하는 과정을 수식으로 나타내면 다음과 같습니다.
이론을 알았으니 구현도 가능합니다. 다음은 파이썬을 이용해 5초 동안의 CPU 사용량을 구하는 예시입니다.
import time
def parse_cpu_info():
with open('/proc/stat', 'r') as f:
stat_line = f.readline()
cpu_info = stat_line.split()
cpu_total = sum(map(int, cpu_info[1:]))
cpu_usage = cpu_total - int(cpu_info[4]) - int(cpu_info[5])
return cpu_total, cpu_usage
def calculate_cpu_usage(interval):
cpu_total1, cpu_usage1 = parse_cpu_info()
time.sleep(interval)
cpu_total2, cpu_usage2 = parse_cpu_info()
total_diff = cpu_total2 - cpu_total1
usage_diff = cpu_usage2 - cpu_usage1
cpu_usage = usage_diff / total_diff * 100
return cpu_usage
if __name__ == "__main__":
cpu_usage = calculate_cpu_usage(5)
print(f"CPU 사용량: {cpu_usage:.2f}%")
이번 포스팅에서는 서버 모니터링의 정보 원천인 proc 파일 시스템을 소개했습니다. 와탭의 서버 모니터링도 기본 원리는 free나 top과 같은 고전적인 도구들과 크게 다르지 않습니다. 실습 단계까지 따라오셨다면 서버 모니터링의 원리에 대해 더 잘 이해하게 되셨을 것입니다.
Tex 수식과 python 소스 코드의 일부는 ChatGPT가 작성하였습니다.