#!/bin/bash # ============================================================ # GUARDiA SM | db_postgresql_sm.sh # 대상: PostgreSQL # 파라미터: PG_HOST=localhost PG_PORT=5432 PG_DB=postgres # PG_USER=postgres PGPASSWORD= # SLOW_QUERY_MS=1000 MAX_CONN_WARN=80 # ============================================================ set -euo pipefail PG_HOST=${PG_HOST:-localhost} PG_PORT=${PG_PORT:-5432} PG_DB=${PG_DB:-postgres} PG_USER=${PG_USER:-postgres} export PGPASSWORD=${PGPASSWORD:-""} SLOW_QUERY_MS=${SLOW_QUERY_MS:-1000} MAX_CONN_WARN=${MAX_CONN_WARN:-80} OK="[OK]"; WARN="[WARN]"; CRIT="[CRIT]" SEP="─────────────────────────────────────────" RESULT=0 PSQ="psql -h ${PG_HOST} -p ${PG_PORT} -U ${PG_USER} -d ${PG_DB} -t -A -c" echo "======================================================" echo " GUARDiA SM 점검 | PostgreSQL | $(hostname -s)" echo " 점검 시각: $(date '+%Y-%m-%d %H:%M:%S %Z')" echo "======================================================" # ── 1. 프로세스 ─────────────────────────────────────────── echo; echo "[$SEP] 1. PostgreSQL 프로세스" PG_PROC=$(pgrep -c "postgres" 2>/dev/null || echo 0) PG_MASTER=$(pgrep -f "postgres: checkpointer\|postmaster" 2>/dev/null | head -1 || echo "") if [ "$PG_PROC" -gt 0 ]; then echo " ${OK} PostgreSQL 실행 중 (${PG_PROC}개 프로세스)" else echo " ${CRIT} PostgreSQL 프로세스 없음" RESULT=2 fi # ── 2. 포트 리스닝 ──────────────────────────────────────── echo; echo "[$SEP] 2. 포트 리스닝" if ss -tlnp 2>/dev/null | grep -q ":${PG_PORT} "; then echo " ${OK} 포트 ${PG_PORT} LISTEN" else echo " ${CRIT} 포트 ${PG_PORT} LISTEN 없음" RESULT=2 fi # psql 접속 테스트 if command -v psql &>/dev/null; then VERSION=$(${PSQ} "SELECT version();" 2>/dev/null | head -1 || echo "접속 실패") if echo "$VERSION" | grep -qi "postgresql"; then echo " ${OK} DB 접속 성공: $(echo "$VERSION" | head -c 60)..." else echo " ${CRIT} DB 접속 실패: ${VERSION}" RESULT=2 fi else echo " ${WARN} psql 클라이언트 없음 — 접속 점검 건너뜀" [ $RESULT -lt 1 ] && RESULT=1 fi # ── 3. 연결 수 ──────────────────────────────────────────── echo; echo "[$SEP] 3. 연결 현황" if command -v psql &>/dev/null && [ "$RESULT" -lt 2 ]; then CONN_INFO=$(${PSQ} " SELECT setting AS max_connections FROM pg_settings WHERE name='max_connections';" 2>/dev/null || echo "") MAX_CONN=$CONN_INFO CURR_CONN=$(${PSQ} "SELECT count(*) FROM pg_stat_activity;" 2>/dev/null || echo 0) ACTIVE_CONN=$(${PSQ} "SELECT count(*) FROM pg_stat_activity WHERE state='active';" 2>/dev/null || echo 0) IDLE_CONN=$(${PSQ} "SELECT count(*) FROM pg_stat_activity WHERE state='idle';" 2>/dev/null || echo 0) echo " 최대 연결 수: ${MAX_CONN}" echo " 현재 연결 수: ${CURR_CONN} (active:${ACTIVE_CONN} idle:${IDLE_CONN})" if [ -n "$MAX_CONN" ] && [ "$MAX_CONN" -gt 0 ]; then CONN_PCT=$(( CURR_CONN * 100 / MAX_CONN )) if [ "$CONN_PCT" -ge "$MAX_CONN_WARN" ]; then echo " ${WARN} 연결 사용률 ${CONN_PCT}%" [ $RESULT -lt 1 ] && RESULT=1 else echo " ${OK} 연결 사용률 ${CONN_PCT}%" fi fi fi # ── 4. 슬로우 쿼리 ──────────────────────────────────────── echo; echo "[$SEP] 4. 슬로우 쿼리 (>${SLOW_QUERY_MS}ms)" if command -v psql &>/dev/null && [ "$RESULT" -lt 2 ]; then SLOW=$(${PSQ} " SELECT pid, now() - pg_stat_activity.query_start AS duration, left(query, 80) AS query FROM pg_stat_activity WHERE (now() - pg_stat_activity.query_start) > interval '${SLOW_QUERY_MS} ms' AND state = 'active' ORDER BY duration DESC LIMIT 5;" 2>/dev/null || echo "") if [ -n "$SLOW" ]; then echo " ${WARN} 슬로우 쿼리 감지:" echo "$SLOW" | sed 's/^/ /' [ $RESULT -lt 1 ] && RESULT=1 else echo " ${OK} 슬로우 쿼리 없음" fi fi # ── 5. 잠금(Lock) 대기 ──────────────────────────────────── echo; echo "[$SEP] 5. 잠금 대기" if command -v psql &>/dev/null && [ "$RESULT" -lt 2 ]; then LOCK=$(${PSQ} " SELECT count(*) FROM pg_stat_activity WHERE wait_event_type = 'Lock';" 2>/dev/null || echo 0) if [ "$LOCK" -gt 5 ]; then echo " ${CRIT} 잠금 대기 ${LOCK}건" RESULT=2 elif [ "$LOCK" -gt 0 ]; then echo " ${WARN} 잠금 대기 ${LOCK}건" [ $RESULT -lt 1 ] && RESULT=1 else echo " ${OK} 잠금 대기 없음" fi fi # ── 6. 복제(Replication) 상태 ──────────────────────────── echo; echo "[$SEP] 6. 복제 상태" if command -v psql &>/dev/null && [ "$RESULT" -lt 2 ]; then IS_STANDBY=$(${PSQ} "SELECT pg_is_in_recovery();" 2>/dev/null || echo "f") if echo "$IS_STANDBY" | grep -q "t"; then LAG=$(${PSQ} "SELECT EXTRACT(EPOCH FROM (now() - pg_last_xact_replay_timestamp()))::integer;" \ 2>/dev/null || echo "N/A") echo " ${OK} 스탠바이 모드 — 복제 지연: ${LAG}초" [ "$LAG" -gt 60 ] 2>/dev/null && echo " ${WARN} 복제 지연 과다 (${LAG}s)" && [ $RESULT -lt 1 ] && RESULT=1 else REP_COUNT=$(${PSQ} "SELECT count(*) FROM pg_stat_replication;" 2>/dev/null || echo 0) echo " 마스터 모드 — 복제 슬레이브 수: ${REP_COUNT}" fi fi # ── 7. DB 크기 상위 5개 ────────────────────────────────── echo; echo "[$SEP] 7. DB 크기" if command -v psql &>/dev/null && [ "$RESULT" -lt 2 ]; then ${PSQ} " SELECT datname, pg_size_pretty(pg_database_size(datname)) AS size FROM pg_database ORDER BY pg_database_size(datname) DESC LIMIT 5;" 2>/dev/null | sed 's/^/ /' || true fi # ── 요약 ───────────────────────────────────────────────── echo echo "======================================================" case $RESULT in 0) echo " 최종 결과: ${OK} PostgreSQL 정상" ;; 1) echo " 최종 결과: ${WARN} 주의 항목 있음" ;; 2) echo " 최종 결과: ${CRIT} 즉시 조치 필요" ;; esac echo " 점검 완료: $(date '+%Y-%m-%d %H:%M:%S')" echo "======================================================" exit $RESULT