G-1: 메신저 Webhook Relay + _send_to_room 실제 httpx 호출 구현 G-2: POST /api/tasks/bulk SR 대량작업 엔드포인트 (최대 100건) G-3: 라이선스 만료 알림 스케줄러 (매일 09:00 KST) G-4: 체험판 upgrade_banner 필드 + license.py 배너 로직 G-5: core/auto_rca.py + incidents/problem auto-rca 엔드포인트 G-6: core/deploy_impact.py + vibe impact-analysis 엔드포인트 G-7: core/ticket_classifier.py + SR 생성 시 AI 분류 + ai-suggestion API G-8: VulnPatchRecord 모델 + vuln_scan 패치추적 4개 엔드포인트 G-9: core/jira_sync.py + gateway Jira/Confluence 연동 엔드포인트 G-10: core/push_notify.py + routers/push.py + PushSubscription 모델 G-11: approvals 다중승인 (위임/서명/기한초과/마감연장) G-12: alembic.ini + migrations/ + cicd/migrate_to_postgres.sh 하네스: guardia-orchestrator 확장기능 Phase 반영 봇명령어: /sr /status /license /bulk 슬래시 명령어 추가 설치스크립트: setup/ (Ubuntu, CentOS, RHEL, Windows) --test 옵션 포함 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
140 lines
7.1 KiB
Bash
140 lines
7.1 KiB
Bash
#!/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
|