Cron + Python으로 서버 상태 리포트 자동 생성 및 슬랙 알림

Cron + Python으로 서버 상태 리포트 자동 생성 및 슬랙 알림 구축 완전 가이드

목차

서버 모니터링 자동화가 필요한 이유와 전체 아키텍처 이해

현대의 서버 운영 환경에서 서버 모니터링 슬랙 알림 시스템을 갖추는 것은 선택이 아닌 필수가 되었습니다. 2026년 3월 현재, 클라우드 인프라와 마이크로서비스 아키텍처가 보편화되면서 관리해야 할 서버의 수가 급격히 늘어났고, 운영팀이 모든 서버를 수동으로 확인하는 것은 사실상 불가능한 수준에 이르렀습니다. CPU 사용률이 급등하거나 디스크 공간이 임계치에 도달했을 때 즉각적인 알림을 받지 못하면 서비스 장애로 이어질 수 있으며, 이는 곧 비즈니스 손실과 직결됩니다.

Python 서버 상태 자동화를 활용하면 이러한 문제를 체계적으로 해결할 수 있습니다. Python은 풍부한 표준 라이브러리와 서드파티 패키지를 제공하며, 특히 psutil 라이브러리를 통해 CPU, 메모리, 디스크, 네트워크 등 다양한 시스템 지표를 손쉽게 수집할 수 있습니다. 여기에 requests 라이브러리를 더하면 Slack과의 통합도 매우 간단하게 구현됩니다. 이 두 가지 도구를 결합하면 강력한 서버 모니터링 솔루션을 빠르게 구축할 수 있습니다.

이 가이드에서 구축할 시스템의 전체 아키텍처는 다음과 같이 동작합니다. cron 자동화 스크립트가 정해진 시간 간격으로 Python 스크립트를 실행하고, Python 스크립트는 서버의 각종 상태 지표를 수집하여 구조화된 리포트를 생성합니다. 이후 임계값을 초과한 항목이 있는 경우 즉시 경고 메시지를 구성하고, Slack webhook 서버 알림을 통해 지정된 슬랙 채널로 리포트를 전송합니다. 이 모든 과정이 자동화되어 있으므로 운영자는 슬랙만 확인하면 서버 상태를 실시간으로 파악할 수 있습니다.

전체 시스템을 구성하는 핵심 컴포넌트를 정리하면 아래 표와 같습니다. 각 컴포넌트의 역할과 기술 스택을 명확히 이해한 후 구축을 시작하는 것이 중요합니다. 특히 Ubuntu 서버 모니터링 환경에서는 cron 데몬이 기본으로 설치되어 있어 별도의 스케줄러 설치 없이도 강력한 자동화 파이프라인을 구성할 수 있다는 점이 큰 장점입니다. 이제 각 단계를 하나씩 상세히 살펴보겠습니다.

컴포넌트 역할 기술 스택 비고 스케줄러 정해진 시간에 스크립트 자동 실행 Linux cron Ubuntu 기본 내장 데이터 수집기 서버 상태 지표 수집 Python + psutil CPU, 메모리, 디스크, 네트워크 리포트 생성기 수집된 데이터를 구조화된 메시지로 변환 Python JSON 포맷 활용 알림 전송기 슬랙 채널로 리포트 전송 Python + requests + Slack Webhook Incoming Webhooks 사용 로그 관리 실행 이력 및 오류 기록 Python logging 모듈 로테이션 설정 권장 

[[IMAGEPROMPT: A professional diagram showing a server monitoring automation architecture with Ubuntu server, Python script, cron scheduler, and Slack notification flow, displayed on a dark background with clean technical illustration style]]

Ubuntu 서버 환경 준비 및 Python 의존성 설치

Ubuntu 서버 모니터링 시스템을 구축하기 위해 가장 먼저 해야 할 일은 서버 환경을 올바르게 준비하는 것입니다. 2026년 3월 기준으로 Ubuntu 22.04 LTS 또는 Ubuntu 24.04 LTS 환경을 기준으로 설명합니다. 두 버전 모두 Python 3.10 이상이 기본으로 설치되어 있으므로 별도의 Python 설치 없이 바로 작업을 시작할 수 있습니다. 먼저 시스템 패키지를 최신 상태로 업데이트하는 것부터 시작합니다.

터미널

sudo apt update sudo apt upgrade -y python3 –version which python3

Python 버전을 확인한 후에는 가상 환경(virtual environment)을 생성하는 것을 강력히 권장합니다. 가상 환경을 사용하면 시스템 Python 환경을 오염시키지 않고 프로젝트별로 독립된 패키지 관리가 가능합니다. 특히 여러 Python 프로젝트가 동일한 서버에서 운영될 때 패키지 버전 충돌을 방지하는 데 매우 효과적입니다. 모니터링 프로젝트 전용 디렉토리를 생성하고 그 안에 가상 환경을 설정합니다.

터미널

sudo mkdir -p /opt/server-monitor sudo chown $USER:$USER /opt/server-monitor cd /opt/server-monitor python3 -m venv venv source venv/bin/activate

가상 환경이 활성화되면 이제 필요한 Python 패키지를 설치할 차례입니다. 이 프로젝트에서 사용할 핵심 패키지는 psutilrequests입니다. psutil은 크로스 플랫폼 시스템 및 프로세스 유틸리티 라이브러리로, CPU 사용률, 메모리 사용량, 디스크 I/O, 네트워크 통계 등 다양한 시스템 정보를 Python 코드로 쉽게 접근할 수 있게 해줍니다. requests는 HTTP 요청을 간편하게 처리할 수 있는 라이브러리로, Slack Webhook API를 호출하는 데 사용됩니다.

터미널

pip install psutil requests pip install python-dotenv pip freeze > requirements.txt

패키지 설치가 완료되면 프로젝트의 디렉토리 구조를 정리합니다. 체계적인 디렉토리 구조는 나중에 스크립트를 유지보수하거나 기능을 확장할 때 큰 도움이 됩니다. 아래와 같은 구조로 디렉토리와 파일을 생성합니다. 특히 민감한 정보인 Slack Webhook URL은 반드시 별도의 환경 변수 파일에 저장하고 버전 관리 시스템에 포함되지 않도록 주의해야 합니다.

터미널

mkdir -p /opt/server-monitor/{scripts,logs,config} touch /opt/server-monitor/config/.env touch /opt/server-monitor/scripts/monitor.py touch /opt/server-monitor/scripts/slacknotifier.py touch /opt/server-monitor/scripts/reportgenerator.py chmod 600 /opt/server-monitor/config/.env

환경 변수 파일을 설정하는 것도 중요한 단계입니다. .env 파일에는 Slack Webhook URL, 알림 임계값, 서버 식별 정보 등 환경에 따라 달라지는 설정값을 저장합니다. 이렇게 하면 코드를 수정하지 않고도 설정값만 변경하여 다양한 서버에 동일한 스크립트를 배포할 수 있습니다. python-dotenv 라이브러리를 사용하면 이 파일을 Python에서 손쉽게 로드할 수 있습니다.

  • SLACKWEBHOOKURL: Slack Incoming Webhook 주소 (필수)
  • SERVERNAME: 알림 메시지에 표시될 서버 이름 (예: web-server-01)
  • CPUTHRESHOLD: CPU 사용률 경고 임계값 (기본값: 80)
  • MEMORYTHRESHOLD: 메모리 사용률 경고 임계값 (기본값: 85)
  • DISKTHRESHOLD: 디스크 사용률 경고 임계값 (기본값: 90)
  • REPORTCHANNEL: 정기 리포트를 전송할 슬랙 채널명
  • ALERTCHANNEL: 긴급 경고를 전송할 슬랙 채널명
  • LOGLEVEL: 로그 출력 수준 (DEBUG, INFO, WARNING, ERROR)

[[IMAGEPROMPT: A terminal window on Ubuntu server showing Python virtual environment setup, package installation with pip, and directory structure creation commands, professional dark theme terminal screenshot]]

Python으로 서버 상태 데이터 수집 스크립트 작성

Python으로 서버 상태 체크를 구현하는 핵심은 psutil 라이브러리를 활용하여 정확하고 신뢰할 수 있는 데이터를 수집하는 것입니다. 단순히 현재 순간의 CPU 사용률을 스냅샷으로 찍는 것보다는 일정 시간 동안의 평균값을 측정하는 것이 더 의미 있는 데이터를 제공합니다. 예를 들어 CPU 사용률은 psutil.cpupercent(interval=1)처럼 interval 파라미터를 지정하면 해당 시간 동안의 평균 사용률을 반환합니다. 이 방식이 순간적인 스파이크에 의한 오탐(false positive)을 줄이는 데 효과적입니다.

먼저 서버 상태 데이터를 수집하는 메인 모듈인 monitor.py를 작성합니다. 이 스크립트는 CPU, 메모리, 디스크, 네트워크, 프로세스 등 주요 시스템 지표를 수집하고 구조화된 딕셔너리 형태로 반환하는 역할을 합니다. 각 지표는 현재값뿐만 아니라 임계값 초과 여부도 함께 판단하여 후속 처리 로직을 단순화합니다.

{ } 코드

#!/usr/bin/env python3 — coding: utf-8 — “”” 서버 상태 모니터링 데이터 수집 모듈 파일명: /opt/server-monitor/scripts/monitor.py “”” import psutil import os import socket import datetime import logging from dotenv import loaddotenv 환경 변수 로드 loaddotenv(‘/opt/server-monitor/config/.env’) 로깅 설정 logging.basicConfig( filename=’/opt/server-monitor/logs/monitor.log’, level=getattr(logging, os.getenv(‘LOGLEVEL’, ‘INFO’)), format=’%(asctime)s – %(levelname)s – %(message)s’, datefmt=’%Y-%m-%d %H:%M:%S’ ) logger = logging.getLogger(name) 임계값 설정 CPUTHRESHOLD = int(os.getenv(‘CPUTHRESHOLD’, 80)) MEMORYTHRESHOLD = int(os.getenv(‘MEMORYTHRESHOLD’, 85)) DISKTHRESHOLD = int(os.getenv(‘DISKTHRESHOLD’, 90)) SERVERNAME = os.getenv(‘SERVERNAME’, socket.gethostname()) def getcpuinfo(): “””CPU 사용률 및 관련 정보 수집””” cpupercent = psutil.cpupercent(interval=2) cpucount = psutil.cpucount(logical=True) cpufreq = psutil.cpufreq() loadavg = os.getloadavg() return { ‘usagepercent’: cpupercent, ‘corecount’: cpucount, ‘frequencymhz’: round(cpufreq.current, 2) if cpufreq else None, ‘loadavg1min’: round(loadavg[0], 2), ‘loadavg5min’: round(loadavg[1], 2), ‘loadavg15min’: round(loadavg[2], 2), ‘iscritical’: cpupercent >= CPUTHRESHOLD } def getmemoryinfo(): “””메모리 사용량 정보 수집””” mem = psutil.virtualmemory() swap = psutil.swapmemory() return { ‘totalgb’: round(mem.total / (10243), 2), ‘usedgb’: round(mem.used / (10243), 2), ‘availablegb’: round(mem.available / (10243), 2), ‘usagepercent’: mem.percent, ‘swaptotalgb’: round(swap.total / (10243), 2), ‘swapusedgb’: round(swap.used / (10243), 2), ‘swappercent’: swap.percent, ‘iscritical’: mem.percent >= MEMORYTHRESHOLD } def getdiskinfo(): “””디스크 사용량 정보 수집 (마운트된 모든 파티션)””” disklist = [] partitions = psutil.diskpartitions() for partition in partitions: if ‘loop’ in partition.device: continue try: usage = psutil.diskusage(partition.mountpoint) disklist.append({ ‘device’: partition.device, ‘mountpoint’: partition.mountpoint, ‘totalgb’: round(usage.total / (10243), 2), ‘usedgb’: round(usage.used / (10243), 2), ‘freegb’: round(usage.free / (10243), 2), ‘usagepercent’: usage.percent, ‘iscritical’: usage.percent >= DISKTHRESHOLD }) except PermissionError: continue return disklist def getnetworkinfo(): “””네트워크 인터페이스 및 연결 정보 수집””” netio = psutil.netiocounters() connections = psutil.netconnections() established = sum(1 for c in connections if c.status == ‘ESTABLISHED’) return { ‘bytessentmb’: round(netio.bytessent / (10242), 2), ‘bytesrecvmb’: round(netio.bytesrecv / (10242), 2), ‘packetssent’: netio.packetssent, ‘packetsrecv’: netio.packetsrecv, ‘establishedconnections’: established } def gettopprocesses(limit=5): “””CPU 사용률 상위 프로세스 목록 반환””” processes = [] for proc in psutil.processiter([‘pid’, ‘name’, ‘cpupercent’, ‘memorypercent’]): try: processes.append(proc.info) except (psutil.NoSuchProcess, psutil.AccessDenied): pass sortedprocs = sorted(processes, key=lambda x: x[‘cpupercent’], reverse=True) return sortedprocs[:limit] def collectserverstatus(): “””전체 서버 상태 데이터 수집 및 반환””” logger.info(f”서버 상태 수집 시작: {SERVERNAME}”) status = { ‘servername’: SERVERNAME, ‘hostname’: socket.gethostname(), ‘ipaddress’: socket.gethostbyname(socket.gethostname()), ‘collectedat’: datetime.datetime.now().strftime(‘%Y-%m-%d %H:%M:%S’), ‘cpu’: getcpuinfo(), ‘memory’: getmemoryinfo(), ‘disks’: getdiskinfo(), ‘network’: getnetworkinfo(), ‘topprocesses’: gettopprocesses(), ‘uptimehours’: round((datetime.datetime.now().timestamp() – psutil.boottime()) / 3600, 1) } # 전체 위험 상태 여부 판단 status[‘hascritical’] = ( status[‘cpu’][‘iscritical’] or status[‘memory’][‘iscritical’] or any(d[‘iscritical’] for d in status[‘disks’]) ) logger.info(f”서버 상태 수집 완료. 위험 상태: {status[‘hascritical’]}”) return status if name == ‘main’: import json result = collectserverstatus() print(json.dumps(result, ensureascii=False, indent=2))

위 스크립트를 직접 실행하면 JSON 형태로 서버 상태 데이터가 출력됩니다. 이 데이터를 기반으로 리포트를 생성하고 슬랙 알림을 전송하는 구조입니다. 스크립트에서 특히 주목할 부분은 임계값 초과 여부를 데이터 수집 단계에서 함께 판단한다는 점입니다. 이렇게 하면 이후 알림 로직에서 단순히 iscritical 플래그만 확인하면 되므로 코드가 훨씬 간결해집니다.

스크립트를 테스트하여 정상적으로 데이터가 수집되는지 확인합니다. 가상 환경이 활성화된 상태에서 아래 명령을 실행하면 현재 서버의 상태가 JSON 형태로 출력됩니다.

터미널

cd /opt/server-monitor source venv/bin/activate python3 scripts/monitor.py

Q: psutil.cpupercent()를 interval 없이 호출하면 어떤 문제가 생기나요? 
A: interval 파라미터 없이 호출하면 마지막 호출 이후 경과된 시간 동안의 CPU 사용률을 반환합니다. 최초 호출 시에는 항상 0.0을 반환하는 문제가 있습니다. 따라서 interval=1 또는 interval=2처럼 측정 시간을 지정하는 것이 정확한 값을 얻는 데 필수적입니다. 모니터링 스크립트에서는 interval=2 정도가 정확성과 실행 시간의 균형을 맞추기에 적합합니다.

[[IMAGEPROMPT: A Python code editor showing server monitoring script with psutil library, displaying CPU memory disk usage data collection code on a professional dark IDE, with terminal output showing JSON formatted server status data]]

Slack Webhook 설정 및 Python 알림 전송 구현

Slack webhook 서버 알림을 구현하기 위해서는 먼저 Slack 워크스페이스에서 Incoming Webhook을 설정해야 합니다. Slack App을 생성하고 Incoming Webhooks 기능을 활성화하는 과정은 Slack API 포털(api.slack.com)에서 진행합니다. 2026년 3월 기준으로 Slack의 Incoming Webhooks는 여전히 가장 간단하고 안정적인 서버 알림 통합 방법으로 널리 사용되고 있습니다. Webhook URL은 https://hooks.slack.com/services/로 시작하는 고유한 주소이며, 이 URL로 HTTP POST 요청을 보내면 지정된 채널에 메시지가 전송됩니다.

Slack App 설정 절차를 단계별로 설명합니다. 첫째, api.slack.com/apps에 접속하여 “Create New App”을 클릭하고 “From scratch”를 선택합니다. 둘째, 앱 이름(예: Server Monitor Bot)과 설치할 워크스페이스를 선택합니다. 셋째, 좌측 메뉴에서 “Incoming Webhooks”를 선택하고 “Activate Incoming Webhooks”를 켭니다. 넷째, 하단의 “Add New Webhook to Workspace” 버튼을 클릭하고 메시지를 전송할 채널을 선택합니다. 다섯째, 생성된 Webhook URL을 복사하여 .env 파일의 SLACKWEBHOOKURL 항목에 저장합니다.

이제 슬랙 웹훅으로 서버 알림 보내기 기능을 구현하는 Python 모듈을 작성합니다. Slack의 Block Kit을 활용하면 단순한 텍스트 메시지보다 훨씬 풍부하고 읽기 쉬운 메시지를 구성할 수 있습니다. 헤더, 섹션, 구분선, 컨텍스트 블록 등을 조합하여 한눈에 서버 상태를 파악할 수 있는 리포트를 만들어 봅니다.

{ } 코드

#!/usr/bin/env python3 — coding: utf-8 — “”” Slack 알림 전송 모듈 파일명: /opt/server-monitor/scripts/slacknotifier.py “”” import requests import json import os import logging from dotenv import loaddotenv loaddotenv(‘/opt/server-monitor/config/.env’) logger = logging.getLogger(name) SLACKWEBHOOKURL = os.getenv(‘SLACKWEBHOOKURL’) def sendslackmessage(payload: dict) -> bool: “””슬랙으로 메시지 페이로드 전송””” if not SLACKWEBHOOKURL: logger.error(“SLACKWEBHOOKURL이 설정되지 않았습니다.”) return False try: response = requests.post( SLACKWEBHOOKURL, data=json.dumps(payload), headers={‘Content-Type’: ‘application/json’}, timeout=10 ) if response.statuscode == 200 and response.text == ‘ok’: logger.info(“슬랙 메시지 전송 성공”) return True else: logger.error(f”슬랙 전송 실패: {response.statuscode} – {response.text}”) return False except requests.exceptions.RequestException as e: logger.error(f”슬랙 요청 오류: {e}”) return False def buildstatusreport(status: dict) -> dict: “””서버 상태 데이터를 슬랙 Block Kit 메시지로 변환””” servername = status[‘servername’] collectedat = status[‘collectedat’] cpu = status[‘cpu’] memory = status[‘memory’] disks = status[‘disks’] network = status[‘network’] uptime = status[‘uptimehours’] # 상태에 따른 색상 및 이콘 설정 overallstatus = “위험” if status[‘hascritical’] else “정상” color = “#FF0000” if status[‘hascritical’] else “#36A64F” # 디스크 정보 문자열 생성 disktext = “” for disk in disks: criticalmark = ” [경고]” if disk[‘iscritical’] else “” disktext += f”• {disk[‘mountpoint’]}: {disk[‘usagepercent’]}% 사용 ({disk[‘usedgb’]}GB / {disk[‘totalgb’]}GB){criticalmark}\n” # 상위 프로세스 정보 proctext = “” for proc in status.get(‘topprocesses’, [])[:3]: proctext += f”• {proc[‘name’]} (PID:{proc[‘pid’]}) – CPU: {proc[‘cpupercent’]}%\n” blocks = [ { “type”: “header”, “text”: { “type”: “plaintext”, “text”: f”[{overallstatus}] {servername} 서버 상태 리포트” } }, { “type”: “section”, “fields”: [ {“type”: “mrkdwn”, “text”: f”서버명\n{servername}”}, {“type”: “mrkdwn”, “text”: f”수집 시각\n{collectedat}”}, {“type”: “mrkdwn”, “text”: f”IP 주소\n{status[‘ipaddress’]}”}, {“type”: “mrkdwn”, “text”: f”가동 시간\n{uptime}시간”} ] }, {“type”: “divider”}, { “type”: “section”, “text”: { “type”: “mrkdwn”, “text”: f”CPU 상태 {‘[경고]’ if cpu[‘iscritical’] else ‘[정상]’}\n” f”사용률: {cpu[‘usagepercent’]}% (임계값: {os.getenv(‘CPUTHRESHOLD’, 80)}%)\n” f”코어 수: {cpu[‘corecount’]} | ” f”Load Avg: {cpu[‘loadavg1min’]} / {cpu[‘loadavg5min’]} / {cpu[‘loadavg15min’]}” } }, { “type”: “section”, “text”: { “type”: “mrkdwn”, “text”: f”메모리 상태 {‘[경고]’ if memory[‘iscritical’] else ‘[정상]’}\n” f”사용률: {memory[‘usagepercent’]}% | ” f”사용: {memory[‘usedgb’]}GB / 전체: {memory[‘totalgb’]}GB\n” f”SWAP: {memory[‘swappercent’]}% ({memory[‘swapusedgb’]}GB / {memory[‘swaptotalgb’]}GB)” } }, { “type”: “section”, “text”: { “type”: “mrkdwn”, “text”: f”디스크 상태\n{disktext}” } }, { “type”: “section”, “text”: { “type”: “mrkdwn”, “text”: f”네트워크 상태\n” f”송신: {network[‘bytessentmb’]}MB | 수신: {network[‘bytesrecvmb’]}MB\n” f”활성 연결: {network[‘establishedconnections’]}개” } }, { “type”: “section”, “text”: { “type”: “mrkdwn”, “text”: f”CPU 상위 프로세스\n{proctext}” } }, { “type”: “context”, “elements”: [ { “type”: “mrkdwn”, “text”: f”자동 생성된 서버 모니터링 리포트 | {servername} | {collectedat}” } ] } ] return {“attachments”: [{“color”: color, “blocks”: blocks}]} def sendcriticalalert(status: dict) -> bool: “””위험 상태 발생 시 긴급 알림 전송””” criticalitems = [] if status[‘cpu’][‘iscritical’]: criticalitems.append(f”CPU 사용률 {status[‘cpu’][‘usagepercent’]}%”) if status[‘memory’][‘iscritical’]: criticalitems.append(f”메모리 사용률 {status[‘memory’][‘usagepercent’]}%”) for disk in status[‘disks’]: if disk[‘iscritical’]: criticalitems.append(f”디스크 {disk[‘mountpoint’]} {disk[‘usagepercent’]}%”) alerttext = “, “.join(criticalitems) payload = { “text”: f”[긴급 경고] {status[‘servername’]} 서버 임계값 초과: {alerttext}” } return sendslackmessage(payload)

Webhook URL을 .env 파일에 저장한 후, 간단한 테스트 메시지를 전송하여 설정이 올바른지 확인합니다. 아래 명령을 실행하면 슬랙 채널에 테스트 메시지가 전송됩니다.

터미널

cd /opt/server-monitor source venv/bin/activate python3 -c ” from scripts.slacknotifier import sendslackmessage result = sendslackmessage({‘text’: ‘서버 모니터링 시스템 테스트 메시지입니다.’}) print(‘전송 성공’ if result else ‘전송 실패’) “

메시지 유형 전송 조건 색상 코드 우선순위 정기 리포트 매 시간 또는 매일 정해진 시간 #36A64F (초록) 낮음 경고 알림 임계값의 80% 도달 시 #FFA500 (주황) 중간 긴급 경보 임계값 초과 시 #FF0000 (빨강) 높음 복구 알림 위험 상태에서 정상으로 복귀 시 #0000FF (파랑) 중간 

[[IMAGEPROMPT: A Slack channel interface showing a formatted server monitoring report message with colored attachment, displaying CPU memory disk usage statistics in a professional block kit layout, dark mode Slack desktop view]]

Cron으로 자동화 스케줄링 설정 및 운영 관리

cron 자동화 스크립트를 설정하기 전에, 전체 모니터링 파이프라인을 하나로 연결하는 메인 실행 스크립트를 완성해야 합니다. 이 스크립트는 monitor.py에서 데이터를 수집하고, slacknotifier.py를 통해 슬랙으로 전송하는 역할을 합니다. 또한 이전 실행에서 위험 상태였는지 여부를 파일로 저장하여, 동일한 위험 상태가 지속될 때 반복 알림이 발송되지 않도록 제어하는 로직도 포함합니다. 이 기능은 실제 운영 환경에서 알림 피로도를 줄이는 데 매우 중요합니다.

{ } 코드

#!/usr/bin/env python3 — coding: utf-8 — “”” 서버 모니터링 메인 실행 스크립트 파일명: /opt/server-monitor/runmonitor.py “”” import sys import os import json import logging from pathlib import Path 경로 설정 BASEDIR = Path(‘/opt/server-monitor’) sys.path.insert(0, str(BASEDIR)) from scripts.monitor import collectserverstatus from scripts.slacknotifier import ( sendslackmessage, buildstatusreport, sendcriticalalert ) 로깅 설정 logging.basicConfig( filename=str(BASEDIR / ‘logs’ / ‘runmonitor.log’), level=logging.INFO, format=’%(asctime)s – %(levelname)s – %(message)s’ ) logger = logging.getLogger(name) STATEFILE = BASEDIR / ‘logs’ / ‘.laststate.json’ def loadlaststate(): “””이전 실행 상태 로드””” if STATEFILE.exists(): with open(STATEFILE, ‘r’) as f: return json.load(f) return {‘wascritical’: False} def savecurrentstate(status: dict): “””현재 실행 상태 저장””” state = {‘wascritical’: status[‘hascritical’]} with open(STATEFILE, ‘w’) as f: json.dump(state, f) def main(reportmode: str = ‘auto’): “”” 메인 실행 함수 reportmode: ‘auto’ (임계값 초과 시만), ‘full’ (항상 전체 리포트), ‘alert’ (경고만) “”” logger.info(f”모니터링 실행 시작 (모드: {reportmode})”) # 서버 상태 수집 status = collectserverstatus() laststate = loadlaststate() if reportmode == ‘full’: # 전체 리포트 전송 (정기 리포트용) payload = buildstatusreport(status) sendslackmessage(payload) logger.info(“전체 리포트 전송 완료”) elif reportmode == ‘auto’ or reportmode == ‘alert’: # 위험 상태 진입 시 긴급 알림 if status[‘hascritical’] and not laststate[‘wascritical’]: sendcriticalalert(status) payload = buildstatusreport(status) sendslackmessage(payload) logger.warning(“위험 상태 감지 – 긴급 알림 전송”) # 위험 상태에서 정상으로 복구 시 복구 알림 elif not status[‘hascritical’] and laststate[‘wascritical’]: recoverypayload = { “attachments”: [{ “color”: “#0000FF”, “text”: f”[복구] {status[‘servername’]} 서버가 정상 상태로 복구되었습니다. ({status[‘collectedat’]})” }] } sendslackmessage(recoverypayload) logger.info(“복구 알림 전송 완료”) # 상태 저장 savecurrentstate(status) logger.info(“모니터링 실행 완료”) if name == ‘main’: mode = sys.argv[1] if len(sys.argv) > 1 else ‘auto’ main(mode)

이제 cron job 서버 상태 알림 슬랙 연동의 핵심인 crontab을 설정합니다. crontab은 “분 시 일 월 요일 명령어” 형식으로 스케줄을 정의합니다. 모니터링 시스템에서는 일반적으로 두 가지 유형의 cron 작업이 필요합니다. 첫째는 5분 또는 10분마다 실행되는 임계값 감시 작업이고, 둘째는 매일 아침 업무 시작 전에 전체 서버 상태 리포트를 전송하는 정기 리포트 작업입니다.

터미널

crontab -e

crontab 편집기가 열리면 아래 내용을 추가합니다. 각 줄의 의미를 주석으로 설명했으니 환경에 맞게 수정하여 사용하세요.

{ } 코드

# 서버 모니터링 cron 설정 가상 환경의 Python 경로를 직접 지정하여 환경 문제 방지 PYTHON=/opt/server-monitor/venv/bin/python3 MONITORSCRIPT=/opt/server-monitor/runmonitor.py 5분마다 임계값 감시 (위험 상태 감지 시 즉시 슬랙 알림) /5 $PYTHON $MONITORSCRIPT auto >> /opt/server-monitor/logs/cron.log 2>&1 매일 오전 9시에 전체 서버 상태 리포트 전송 (일일 리포트) 0 9 $PYTHON $MONITORSCRIPT full >> /opt/server-monitor/logs/cron.log 2>&1 매주 월요일 오전 8시 50분에 주간 요약 리포트 전송 50 8 1 $PYTHON $MONITORSCRIPT full >> /opt/server-monitor/logs/cron.log 2>&1

cron에서 Python 가상 환경을 사용할 때 가장 흔한 실수는 시스템 Python을 사용하거나 환경 변수가 로드되지 않는 것입니다. 이를 방지하기 위해 위 예시처럼 가상 환경의 Python 전체 경로를 직접 지정하고, 스크립트 내에서 .env 파일의 절대 경로를 사용하여 환경 변수를 로드합니다. crontab에서는 사용자의 쉘 환경이 적용되지 않으므로 이 점을 반드시 고려해야 합니다.

터미널

# crontab 설정 확인 crontab -l cron 서비스 상태 확인 systemctl status cron cron 로그 실시간 모니터링 tail -f /opt/server-monitor/logs/cron.log 스크립트 수동 실행 테스트 /opt/server-monitor/venv/bin/python3 /opt/server-monitor/runmonitor.py full

cron 표현식 실행 주기 사용 용도 /5 5분마다 임계값 감시 (alert 모드) /10 10분마다 임계값 감시 (부하가 낮은 서버) 0 9 매일 오전 9시 일일 전체 리포트 (full 모드) 0 9,18 매일 오전 9시, 오후 6시 하루 2회 정기 리포트 50 8 1 매주 월요일 오전 8시 50분 주간 요약 리포트 0 0 1 매월 1일 자정 월간 리포트 

[[IMAGEPROMPT: A Linux terminal showing crontab configuration file with scheduled tasks for server monitoring, alongside a Slack notification appearing on a smartphone screen, professional dark theme environment]]

실전 운영을 위한 고급 설정과 트러블슈팅

자동화된 서버 리포트 생성 시스템을 실제 운영 환경에 배포할 때는 단순한 기능 구현 이상의 고려 사항이 있습니다. 로그 파일이 무한정 증가하지 않도록 로그 로테이션을 설정하고, 스크립트 실행 실패 시 알림을 받을 수 있도록 오류 처리를 강화하며, 여러 서버를 동시에 모니터링하는 구조로 확장하는 방법도 알아야 합니다. 이 섹션에서는 실제 운영 환경에서 발생할 수 있는 다양한 상황에 대한 해결책을 제시합니다.

첫 번째로 다룰 고급 기능은 logrotate를 이용한 로그 관리입니다. 모니터링 스크립트가 5분마다 실행되면 하루에만 288번 실행되므로 로그 파일이 빠르게 커질 수 있습니다. logrotate 설정 파일을 생성하여 로그 파일이 일정 크기 이상이 되면 자동으로 압축 보관하고 오래된 로그는 삭제하도록 설정합니다.

터미널

sudo nano /etc/logrotate.d/server-monitor

{ } 코드

/opt/server-monitor/logs/.log { daily rotate 30 compress delaycompress missingok notifempty create 0644 ubuntu ubuntu dateext dateformat -%Y%m%d }

두 번째 고급 기능은 여러 서버를 중앙에서 모니터링하는 다중 서버 모니터링 구조입니다. 각 서버에 동일한 스크립트를 배포하되, SERVERNAME 환경 변수만 다르게 설정하면 됩니다. 모든 서버의 알림이 동일한 슬랙 채널로 전송되므로 운영팀은 하나의 채널만 모니터링하면 전체 인프라 상태를 파악할 수 있습니다. 더 나아가 Ansible 같은 구성 관리 도구를 활용하면 수십 대의 서버에 동시에 배포하고 설정을 관리할 수 있습니다.

세 번째로 고려해야 할 것은 알림 중복 방지 및 에스컬레이션 로직입니다. 앞서 구현한 상태 파일 기반의 중복 방지 외에도, 위험 상태가 일정 시간 이상 지속될 경우 더 높은 우선순위의 채널(예: 온콜 채널)에 알림을 보내는 에스컬레이션 기능을 추가할 수 있습니다. 예를 들어 CPU 사용률이 30분 이상 임계값을 초과하면 일반 알림 채널과 함께 긴급 대응 채널에도 메시지를 전송하는 방식입니다.

네 번째는 HTTP 엔드포인트 모니터링 기능 추가입니다. 서버 자원 모니터링과 함께 실제 서비스가 정상적으로 응답하는지 확인하는 것도 중요합니다. requests 라이브러리를 사용하여 지정된 URL로 HTTP GET 요청을 보내고 응답 코드와 응답 시간을 측정하는 기능을 monitor.py에 추가합니다.

{ } 코드

def checkhttpendpoints(endpoints: list) -> list: “””HTTP 엔드포인트 가용성 및 응답 시간 체크””” import requests import time results = [] for url in endpoints: starttime = time.time() try: response = requests.get(url, timeout=10, allowredirects=True) elapsedms = round((time.time() – starttime) 1000, 2) results.append({ ‘url’: url, ‘statuscode’: response.statuscode, ‘responsetimems’: elapsedms, ‘ishealthy’: response.statuscode < 400, ‘isslow’: elapsedms > 2000 }) except requests.exceptions.RequestException as e: results.append({ ‘url’: url, ‘statuscode’: None, ‘responsetimems’: None, ‘ishealthy’: False, ‘error’: str(e) }) return results

Q. cron 작업이 실행되지 않을 때 가장 먼저 확인해야 할 사항은 무엇인가요?

가장 먼저 cron 서비스가 실행 중인지 확인합니다(systemctl status cron). 그 다음 /var/log/syslog 파일에서 cron 관련 오류 메시지를 확인합니다(grep CRON /var/log/syslog). Python 경로가 절대 경로로 지정되어 있는지, 스크립트 파일에 실행 권한이 있는지(chmod +x), 가상 환경이 올바르게 활성화되는지도 점검합니다. 또한 crontab에서 환경 변수가 제대로 설정되어 있는지 확인하고, 스크립트를 직접 실행했을 때 오류가 없는지 수동으로 테스트해 보는 것이 좋습니다.

Q. 슬랙 Webhook 요청이 실패할 때 어떻게 처리해야 하나요?

네트워크 일시 장애나 Slack 서버 점검으로 인해 Webhook 요청이 실패할 수 있습니다. 이를 대비하여 지수 백오프(exponential backoff) 방식의 재시도 로직을 구현하는 것이 좋습니다. 최초 실패 후 1초, 2초, 4초 간격으로 최대 3회 재시도하고, 모든 시도가 실패하면 로컬 파일에 미전송 메시지를 저장해 두었다가 다음 실행 시 재전송을 시도하는 방식을 권장합니다. 또한 Webhook URL의 유효성을 주기적으로 검증하는 헬스체크 작업도 별도로 구성하는 것이 안전합니다.

Q. psutil이 특정 시스템 정보를 읽지 못하는 권한 오류가 발생하면 어떻게 하나요?

일부 시스템 정보(예: 특정 프로세스의 메모리 정보, 일부 네트워크 소켓 정보)는 루트 권한이 필요합니다. 일반 사용자 권한으로 실행할 때 발생하는 psutil.AccessDenied 예외는 try-except 블록으로 처리하여 스크립트가 중단되지 않도록 합니다. 꼭 필요한 정보가 루트 권한을 요구한다면, sudo visudo로 해당 스크립트에 대한 NOPASSWD sudo 권한을 부여하는 방법을 고려할 수 있습니다. 단 이 경우 보안 취약점이 생기지 않도록 최소 권한 원칙을 철저히 적용해야 합니다.

[[IMAGEPROMPT: A professional server operations center with multiple monitors showing server metrics dashboards, log files, and Slack notifications on screens, with a system administrator reviewing alerts, modern NOC environment]]

전체 시스템 통합 및 유지보수 전략

지금까지 구축한 서버 모니터링 슬랙 알림 시스템의 모든 구성 요소를 하나로 통합하고, 장기적으로 안정적으로 운영하기 위한 전략을 살펴봅니다. 실시간 서버 모니터링 Python 시스템은 한 번 구축으로 끝나는 것이 아니라 서버 환경의 변화와 비즈니스 요구 사항에 맞게 지속적으로 개선해야 합니다. 특히 새로운 서버가 추가되거나 서비스 구조가 변경될 때 모니터링 시스템도 함께 업데이트되어야 합니다.

전체 시스템을 처음부터 배포하는 과정을 자동화하기 위한 설치 스크립트를 작성하면 새로운 서버에 동일한 환경을 빠르게 구성할 수 있습니다. 아래는 전체 설치 과정을 자동화하는 setup.sh 스크립트입니다. 이 스크립트 하나로 Python 환경 구성부터 crontab 설정까지 모든 과정이 자동으로 완료됩니다.

{ } 코드

#!/bin/bash 서버 모니터링 시스템 자동 설치 스크립트 setup.sh set -e echo “=== 서버 모니터링 시스템 설치 시작 ===” 디렉토리 생성 sudo mkdir -p /opt/server-monitor/{scripts,logs,config} sudo chown -R $USER:$USER /opt/server-monitor Python

가상 환경 생성 및 패키지 설치 cd /opt/server-monitor python3 -m venv venv source venv/bin/activate pip install psutil requests python-dotenv 스크립트 파일 권한 설정 chmod +x /opt/server-monitor/runmonitor.py 환경 변수 파일 생성 (값은 수동으로 입력 필요) if [ ! -f /opt/server-monitor/config/.env ]; then cat > /opt/server-monitor/config/.env << ‘EOF’ SLACKWEBHOOKURL=https://hooks.slack.com/services/YOUR/WEBHOOK/URL SERVERNAME=my-server-01 CPUTHRESHOLD=80 MEMORYTHRESHOLD=85 DISKTHRESHOLD=90 LOGLEVEL=INFO EOF echo “config/.env 파일이 생성되었습니다. SLACKWEBHOOKURL을 반드시 수정하세요.” fi logrotate 설정 sudo tee /etc/logrotate.d/server-monitor > /dev/null << ‘EOF’ /opt/server-monitor/logs/.log { daily rotate 30 compress delaycompress missingok notifempty } EOF crontab 설정 PYTHONPATH=/opt/server-monitor/venv/bin/python3 SCRIPTPATH=/opt/server-monitor/runmonitor.py (crontab -l 2>/dev/null; echo “/5 $PYTHONPATH $SCRIPTPATH auto >> /opt/server-monitor/logs/cron.log 2>&1”) | crontab – (crontab -l 2>/dev/null; echo “0 9 * $PYTHONPATH $SCRIPTPATH full >> /opt/server-monitor/logs/cron.log 2>&1”) | crontab – echo “=== 설치 완료 ===” echo “config/.env 파일에서 SLACKWEBHOOKURL을 설정하세요.” echo “설치 확인: crontab -l”

 

시스템이 안정적으로 운영되기 시작하면 모니터링 항목을 점진적으로 확장하는 것을 고려합니다. 기본적인 CPU, 메모리, 디스크 모니터링 외에도 Python cron 스케줄링 예제를 활용하여 MySQL, PostgreSQL 같은 데이터베이스 연결 상태 확인, Redis 메모리 사용량 모니터링, Nginx 또는 Apache 프로세스 상태 확인, SSL 인증서 만료일 경고 등 다양한 기능을 추가할 수 있습니다. 각 기능은 독립적인 모듈로 작성하여 monitor.py의 collectserverstatus 함수에서 선택적으로 호출하는 구조로 설계하면 유지보수가 용이합니다.

장기 운영 관점에서 서버 상태 보고서 슬랙 통지 시스템의 신뢰성을 높이기 위한 몇 가지