
OSWatch 스크립트 요약 정보
이 스크립트는 Linux OS 모니터링 강화를 위해 Oracle OSWatcher Black Box를 모티브로 작성한 파이썬 스크립트입니다. 이름도 여기서 따왔습니다.
이 스크립트는 vmstat, ps, top, iostat, mpstat, netstat 등의 명령어를 주기적으로 수행하여 로그파일에 저장합니다. 장애나 비정상적인 리소스 사용량 증가 현상이 발생했을 때 Linux OS 모니터링 스크립트를 활용하여 원인 분석 용도로 활용할 수 있습니다.
스크립트 작성 개요
DB든 미들웨어든, 혹은 직접 작성한 어떤 애플리케이션이든 결국 OS 위에서 동작하기 마련입니다. 아무래도 OS에 문제가 발생한다면 그 위에 동작하고 있는 것이 무엇이 되었든 결국에는 장애로 이어질 것이기에 각종 모니터링 솔루션 등을 활용해, 예쁘게 그래프도 그리고, 매일같이 확인도하고, 문제 있으면 알람도 받고 할 것입니다.
대개 이러한 모니터링 솔루션들은 아예 장애 예방을 위해 선제적으로 활용되고 있다고 생각됩니다.
그런데 최근 제가 겪은 현상으로, 전조 증상 없이 갑자기 CPU 사용량이 급증하는 문제가 있었습니다. 각종 모니터링 솔루션들 덕분에, 장애가 발생한 시점의 정확한 시간도 알 수 있었고, 그 때 문제는 CPU 사용량이 증가했다는것. 그리고 IO 작업량이 동시에 급증한다는 것 등을 확인할 수 있었습니다. 이를 통해 Linux OS 모니터링 스크립트의 필요성을 더욱 느꼈습니다.
하지만 해당 서버가 쿠버네티스 워커 노드로 사용되고 있는 서버였기에 너무 다양한 프로세스들이 올라와있어서 정확히 어떤 프로세스가 문제를 유발하는지 알기가 어려웠습니다. 해당 시점에 서버에 접속해 명령어로 확인하고 싶었지만, 커널이 사용할 리소스조차 부족해 서버에 연결할 수 없었고, 강제로 재부팅할 수밖에 없었습니다.
Linux OS 모니터링 스크립트를 재부팅 후 남아있는 로그들로 원인을 추적해보려 했으나 로그가 부족했습니다. 장애 시점에 top 명령이나 ps 명령 같은것 한번만 딱 찍어봤어도 좋았을텐데..
라는 생각이 미치자 과거 Oracle을 주력으로 사용하던 시절에 종종 적용했었던 Oracle OSWatcher Black Box라는 툴이 생각났습니다. 이 툴이 하는일은 사실 뭐 대단한 일은 아닙니다. 주기적으로 top, iostat, netstat, mpstat, ps 등의 명령을 수행하여 로그 파일에 저장해두고, 보관 주기가 지나면 삭제합니다. 컴파일된 프로그램도 아니고 그냥 쉘스크립트입니다.
해당 솔루션이 제공하는 모든 기능이 필요한 것도 아니거니와, 기능이 복잡한 것도 아닌데 굳이 Oracle 제품을 설치할 필요는 없을 것 같아 비슷하게 한번 작성해봤습니다.
Linux OS 모니터링 스크립트 내용
- Linux OS 모니터링 스크립트 코드
import os
import subprocess
import time
from datetime import datetime, timedelta
import threading
import daemon # 데몬화 모듈
import signal # 종료 신호 처리
# === 설정 부분 ===
LOG_DIR = "/root/scripts/logs"
COLLECTION_INTERVAL = 30 # 데이터 수집 주기 (초)
LOG_RETENTION_DAYS = 14 # 로그 보관 기간 (일)
# ==================
if not os.path.exists(LOG_DIR):
os.makedirs(LOG_DIR)
# 오래된 로그 삭제 함수
def clean_old_logs():
cutoff_date = datetime.now() - timedelta(days=LOG_RETENTION_DAYS)
for filename in os.listdir(LOG_DIR):
filepath = os.path.join(LOG_DIR, filename)
if os.path.isfile(filepath):
# 파일명에서 날짜 추출
try:
file_date_str = filename.split('_')[-1].split('.')[0]
file_date = datetime.strptime(file_date_str, "%Y%m%d")
if file_date < cutoff_date:
os.remove(filepath)
print(f"오래된 로그 삭제: {filename}")
except ValueError:
print(f"파일에서 날짜를 추출할 수 없습니다: {filename}")
# 명령 실행 및 로그 저장
def run_command_and_log(command, log_file):
try:
with open(log_file, "a") as f:
f.write(f"[{datetime.now()}] {command}\n")
result = subprocess.run(command, shell=True, capture_output=True, text=True)
f.write(result.stdout)
f.write("\n" + "-" * 80 + "\n")
except Exception as e:
print(f"명령 실행 오류 {command}: {e}")
# 멀티스레딩 기반 시스템 메트릭 수집
def collect_system_metrics():
commands = {
"vmstat": "vmstat 1 5",
"iostat": "iostat -x 1 5",
"mpstat": "mpstat -P ALL 1 5",
"netstat": "netstat -an",
"top": "top -b -n 1 | head -20",
"ps": "ps -ef",
}
while True:
today = datetime.now().strftime("%Y%m%d")
# 각 명령어를 독립적인 스레드에서 실행
threads = []
for name, command in commands.items():
log_file = os.path.join(LOG_DIR, f"{name}_{today}.log")
thread = threading.Thread(target=run_command_and_log, args=(command, log_file))
threads.append(thread)
thread.start()
# 모든 스레드가 종료될 때까지 대기
for thread in threads:
thread.join()
clean_old_logs() # 오래된 로그 정리
time.sleep(COLLECTION_INTERVAL)
# 종료 신호 처리
def stop_daemon(signum, frame):
print("\n데몬 프로세스를 종료합니다.\n")
exit(0)
if __name__ == "__main__":
print("시스템 성능 데이터 수집 시작...")
print("\n프로세스를 종료하려면 다음 명령을 사용하세요:\nkill -9 <PID>\n")
with daemon.DaemonContext(
signal_map={
signal.SIGTERM: stop_daemon,
signal.SIGINT: stop_daemon
}
):
collect_system_metrics()
- Python3으로 작성되었으며 거의 기본 라이브러리들로 구성되어있습니다.
daemon
모듈은 기본 라이브러리가 아니지만, Python3 설치 과정 중에 함께 설치되었을 가능성이 높습니다. 혹시 없다면yum install python3-daemon
내지는pip3 install python-daemon
명령을 통해 설치바랍니다.- 데몬 형태로 동작하기에, 스크립트 실행하면 바로 백그라운드로 들어갑니다.
[root@Minhang04 scripts]# python3 oswatch.py
시스템 성능 데이터 수집 시작...
프로세스를 종료하려면 다음 명령을 사용하세요:
kill -9 <PID>
[root@Minhang04 scripts]# ps -ef | grep oswatch
root 3660040 1 0 14:59 ? 00:00:00 python3 oswatch.py
root 3660118 3628292 0 14:59 pts/0 00:00:00 grep --color=auto oswatch
- 로그 파일은 명령어별_날짜별로 저장됩니다. Linux OS 모니터링 스크립트는 로그 파일 관리를 용이하게 합니다.
[root@Minhang04 logs]# ls -l
total 6760
-rw-rw-rw- 1 root root 374952 Jan 8 15:30 iostat_20250108.log
-rw-rw-rw- 1 root root 342765 Jan 8 15:30 mpstat_20250108.log
-rw-rw-rw- 1 root root 2984075 Jan 8 15:30 netstat_20250108.log
-rw-rw-rw- 1 root root 1063840 Jan 8 15:30 ps_20250108.log
-rw-rw-rw- 1 root root 222058 Jan 8 15:31 top_20250108.log
-rw-rw-rw- 1 root root 93708 Jan 8 15:30 vmstat_20250108.log
- 각 파일 내용을 확인해보면, 명령어 실행 시점의 날짜, 시간과 실행 결과가 로그 파일에 저장됩니다.
[root@Minhang03 logs]# tail -f top_20250108.log
6 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 netns
8 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 kworker+
10 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 mm_perc+
11 root 20 0 0 0 0 I 0.0 0.0 0:00.00 rcu_tas+
12 root 20 0 0 0 0 I 0.0 0.0 0:00.00 rcu_tas+
13 root 20 0 0 0 0 I 0.0 0.0 0:00.00 rcu_tas+
14 root 20 0 0 0 0 S 0.0 0.0 1:25.88 ksoftir+
15 root 20 0 0 0 0 I 0.0 0.0 1:35.35 rcu_pre+
--------------------------------------------------------------------------------
[2025-01-08 12:15:23.100213] top -b -n 1 | head -20
top - 12:15:23 up 6 days, 1:15, 2 users, load average: 0.00, 0.02, 0.04
Tasks: 127 total, 1 running, 126 sleeping, 0 stopped, 0 zombie
%Cpu(s): 3.1 us, 12.5 sy, 0.0 ni, 81.2 id, 0.0 wa, 0.0 hi, 0.0 si, 3.1 st
MiB Mem : 1907.5 total, 475.2 free, 507.5 used, 924.8 buff/cache
MiB Swap: 0.0 total, 0.0 free, 0.0 used. 1227.0 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
14719 root 20 0 1269004 36932 26932 S 6.2 1.9 32:38.14 flanneld
1284550 root 20 0 223780 3220 2748 R 6.2 0.2 0:00.01 top
1 root 20 0 168156 13684 9144 S 0.0 0.7 2:18.93 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:00.20 kthreadd
3 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 rcu_gp
4 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 rcu_par+
5 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 slub_fl+
6 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 netns
8 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 kworker+
10 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 mm_perc+
11 root 20 0 0 0 0 I 0.0 0.0 0:00.00 rcu_tas+
12 root 20 0 0 0 0 I 0.0 0.0 0:00.00 rcu_tas+
13 root 20 0 0 0 0 I 0.0 0.0 0:00.00 rcu_tas+
--------------------------------------------------------------------------------
- 프로그램을 종료할 때에는
kill -9
명령어로 종료할 수 있습니다.
[root@Minhang04 scripts]# ps -ef | grep oswatch
root 3628479 1 0 14:11 ? 00:00:01 python3 oswatch.py
root 3659457 3628292 0 14:58 pts/0 00:00:00 grep --color=auto oswatch
[root@Minhang04 scripts]# kill -9 3628479
[root@Minhang04 scripts]# ps -ef | grep oswatch
root 3659663 3628292 0 14:58 pts/0 00:00:00 grep --color=auto oswatch
- 스크립트 상단의 설정 부분을 수정하여 로그파일 저장경로, 스크립트 실행주기, 로그파일 보관주기를 설정 가능합니다.
# === 설정 부분 ===
LOG_DIR = "/root/scripts/logs"
COLLECTION_INTERVAL = 30 # 데이터 수집 주기 (초)
LOG_RETENTION_DAYS = 14 # 로그 보관 기간 (일)
# ==================
- 혹시 실행하려는 OS 명령어의 종류를 늘리거나, 줄이려면 중간에 command 부분을 수정하시면 됩니다.
commands = {
"vmstat": "vmstat 1 5",
"iostat": "iostat -x 1 5",
"mpstat": "mpstat -P ALL 1 5",
"netstat": "netstat -an",
"top": "top -b -n 1 | head -20",
"ps": "ps -ef",
}
- 앞부분의 “vmstat”은 파일명으로 사용되고, 뒷 부분은 실제로 수행할 명령어와 옵션입니다.