#!/bin/bash # ============================================================ # GUARDiA SM | system_health.sh # 대상: 모든 Linux 서버 # 내용: CPU / 메모리 / 디스크 / 네트워크 I/O / 로드 평균 # 파라미터: DISK_WARN=80 DISK_CRIT=90 MEM_WARN=85 CPU_WARN=90 # ============================================================ set -euo pipefail DISK_WARN=${DISK_WARN:-80}; DISK_CRIT=${DISK_CRIT:-90} MEM_WARN=${MEM_WARN:-85}; CPU_WARN=${CPU_WARN:-90} SEP="─────────────────────────────────────────" OK="[OK]"; WARN="[WARN]"; CRIT="[CRIT]" RESULT=0 # 0=OK 1=WARN 2=CRIT echo "======================================================" echo " GUARDiA SM 점검 | 시스템 리소스 | $(hostname -s)" echo " 점검 시각: $(date '+%Y-%m-%d %H:%M:%S %Z')" echo "======================================================" # ── 1. CPU 로드 ─────────────────────────────────────────── echo; echo "[$SEP] 1. CPU / 로드 평균" CORES=$(nproc 2>/dev/null || grep -c ^processor /proc/cpuinfo) LOAD1=$(awk '{print $1}' /proc/loadavg) LOAD5=$(awk '{print $2}' /proc/loadavg) LOAD15=$(awk '{print $3}' /proc/loadavg) # CPU 사용률 (1초 샘플) CPU_IDLE=$(top -bn2 -d0.5 | grep "Cpu(s)" | tail -1 | awk '{for(i=1;i<=NF;i++) if($i~/%id/) print $(i-1)}' | tr -d ',') CPU_USE=$(echo "100 - ${CPU_IDLE:-0}" | bc 2>/dev/null || echo "N/A") echo " CPU 코어 수 : ${CORES}" echo " 로드 평균 : ${LOAD1} / ${LOAD5} / ${LOAD15} (1/5/15분)" echo " CPU 사용률 : ${CPU_USE}%" LOAD_THRESH=$(echo "$CORES * 2" | bc) if awk "BEGIN{exit !($LOAD5 > $LOAD_THRESH)}" 2>/dev/null; then echo " 로드 상태 : ${CRIT} 로드 평균이 코어 수 2배 초과 (${LOAD5} > ${LOAD_THRESH})" RESULT=2 elif awk "BEGIN{exit !($LOAD5 > $CORES)}" 2>/dev/null; then echo " 로드 상태 : ${WARN} 로드 평균이 코어 수 초과 (${LOAD5} > ${CORES})" [ $RESULT -lt 1 ] && RESULT=1 else echo " 로드 상태 : ${OK}" fi # ── 2. 메모리 ──────────────────────────────────────────── echo; echo "[$SEP] 2. 메모리" MEM_TOTAL=$(awk '/MemTotal/{print $2}' /proc/meminfo) MEM_AVAIL=$(awk '/MemAvailable/{print $2}' /proc/meminfo) MEM_FREE=$(awk '/MemFree/{print $2}' /proc/meminfo) MEM_BUFCACHE=$(( $(awk '/Buffers/{print $2}' /proc/meminfo) + $(awk '/^Cached:/{print $2}' /proc/meminfo) )) MEM_USED=$(( MEM_TOTAL - MEM_AVAIL )) MEM_PCT=$(( MEM_USED * 100 / MEM_TOTAL )) SWAP_TOTAL=$(awk '/SwapTotal/{print $2}' /proc/meminfo) SWAP_FREE=$(awk '/SwapFree/{print $2}' /proc/meminfo) SWAP_USED=$(( SWAP_TOTAL - SWAP_FREE )) echo " 전체 메모리 : $(( MEM_TOTAL / 1024 )) MB" echo " 사용 메모리 : $(( MEM_USED / 1024 )) MB (${MEM_PCT}%)" echo " 가용 메모리 : $(( MEM_AVAIL / 1024 )) MB" echo " 버퍼/캐시 : $(( MEM_BUFCACHE / 1024 )) MB" [ $SWAP_TOTAL -gt 0 ] && echo " 스왑 사용 : $(( SWAP_USED / 1024 )) MB / $(( SWAP_TOTAL / 1024 )) MB" if [ $MEM_PCT -ge $((MEM_WARN + 10)) ]; then echo " 메모리 상태 : ${CRIT} ${MEM_PCT}% 사용 중" RESULT=2 elif [ $MEM_PCT -ge $MEM_WARN ]; then echo " 메모리 상태 : ${WARN} ${MEM_PCT}% 사용 중" [ $RESULT -lt 1 ] && RESULT=1 else echo " 메모리 상태 : ${OK} ${MEM_PCT}%" fi # ── 3. 디스크 ──────────────────────────────────────────── echo; echo "[$SEP] 3. 디스크 사용량" DF_OUT=$(df -h --output=source,size,used,avail,pcent,target 2>/dev/null | grep -v tmpfs | grep -v udev | grep -v "Filesystem") echo "$DF_OUT" | head -1 | awk '{printf " %-20s %6s %6s %6s %5s %s\n","마운트포인트","전체","사용","가용","사용%","장치"}' echo "$DF_OUT" | while read line; do PCENT=$(echo "$line" | awk '{print $5}' | tr -d '%') MP=$(echo "$line" | awk '{print $6}') if [ -n "$PCENT" ] && [ "$PCENT" -ge $DISK_CRIT ] 2>/dev/null; then echo " ${CRIT} $line" echo "DISK_CRIT_FOUND" >/tmp/.guardia_disk_crit_$$ elif [ -n "$PCENT" ] && [ "$PCENT" -ge $DISK_WARN ] 2>/dev/null; then echo " ${WARN} $line" else echo " ${OK} $line" fi done [ -f /tmp/.guardia_disk_crit_$$ ] && { RESULT=2; rm -f /tmp/.guardia_disk_crit_$$; } # ── 4. 주요 마운트 inode ───────────────────────────────── echo; echo "[$SEP] 4. inode 사용률" df -i 2>/dev/null | grep -v tmpfs | grep -v udev | tail -n +2 | while read line; do IPCT=$(echo "$line" | awk '{print $5}' | tr -d '%') if [ -n "$IPCT" ] && [ "$IPCT" -ge 90 ] 2>/dev/null; then echo " ${WARN} inode 부족: $line" fi done echo " inode 점검 완료" # ── 5. 네트워크 I/O ────────────────────────────────────── echo; echo "[$SEP] 5. 네트워크 인터페이스" ip -s link show 2>/dev/null | awk ' /^[0-9]+:/{name=$2; gsub(/:$/,"",name)} /RX:/{rx_bytes=0; getline; rx_bytes=$1} /TX:/{tx_bytes=0; getline; tx_bytes=$1; if (name != "lo") printf " %-12s RX: %s bytes TX: %s bytes\n", name, rx_bytes, tx_bytes}' \ | head -5 # ── 6. 상위 프로세스 (CPU) ────────────────────────────── echo; echo "[$SEP] 6. CPU 상위 5개 프로세스" ps aux --sort=-%cpu 2>/dev/null | awk 'NR==1{print " ",$0} NR>1 && NR<=6{printf " %-8s %5s%% %5s%% %s\n",$1,$3,$4,substr($0,index($0,$11))}' 2>/dev/null || \ ps -eo user,pcpu,pmem,comm --sort=-pcpu 2>/dev/null | head -6 | awk '{printf " %-10s %5s%% %5s%% %s\n",$1,$2,$3,$4}' # ── 7. 열린 파일 수 ────────────────────────────────────── echo; echo "[$SEP] 7. 파일 디스크립터" OPEN_FILES=$(ls /proc/*/fd 2>/dev/null | wc -l) MAX_FILES=$(cat /proc/sys/fs/file-max 2>/dev/null || echo "N/A") echo " 열린 파일 수 : ${OPEN_FILES}" echo " 시스템 최대 : ${MAX_FILES}" # ── 8. 최근 커널 OOM ───────────────────────────────────── echo; echo "[$SEP] 8. OOM / 커널 오류 (최근 1시간)" OOM=$(dmesg --since="1 hour ago" 2>/dev/null | grep -i "oom\|out of memory\|killed process" | tail -5 || true) if [ -n "$OOM" ]; then echo " ${WARN} OOM 이벤트 감지:" echo "$OOM" | sed 's/^/ /' [ $RESULT -lt 1 ] && RESULT=1 else echo " ${OK} OOM 없음" fi # ── 요약 ───────────────────────────────────────────────── echo echo "======================================================" case $RESULT in 0) echo " 최종 결과: ${OK} 시스템 정상" ;; 1) echo " 최종 결과: ${WARN} 주의 항목 있음 — 모니터링 강화 권고" ;; 2) echo " 최종 결과: ${CRIT} 즉시 조치 필요" ;; esac echo " 점검 완료: $(date '+%Y-%m-%d %H:%M:%S')" echo "======================================================" exit $RESULT