Monitor 백엔드 매뉴얼
각 호스트의 에이전트가 보내는 메트릭을 수집·저장하고, 실시간 대시보드와 텔레그램 알림을 제공하는 중앙 관제 서버(FastAPI)의 설치·설정·운영 방법을 설명합니다.
1.개요
Monitor 백엔드는 이 시스템의 중앙 수집·관제 서버입니다. 각 호스트의 통합 호스트 에이전트가 push 방식으로 보내는 메트릭을 받아 SQLite에 저장하고, 실시간 대시보드·조회 API·텔레그램 알림을 제공합니다.
| 항목 | 내용 |
|---|---|
| 기술 스택 | Python 3.12 · FastAPI · SQLite(aiosqlite) · Jinja2 + HTMX (대시보드) |
| 포트 | 8009 — 대시보드·수집·API 모두 단일 포트 |
| 인증 | 수집·조회 API는 요청 헤더 X-API-Key (환경변수 MONITORING_API_KEY) |
| 데이터 저장 | SQLite 파일 1개 (data/monitors.db) — 외부 DB 불필요 |
| 호스트 등록 | 사전 등록 없음 — 에이전트 첫 전송 시 hostname 기준 자동 등록 |
| 알림 | 상태 전이 시에만 텔레그램 전송(스팸 방지) + 하루 2회 일일 리포트 |
1.1내부 모듈 구조
| 모듈 | 역할 |
|---|---|
app/main.py | FastAPI 진입점. .env 로드, 백그라운드 태스크 2종(오프라인 스윕·일일 리포트 스케줄러) 관리, 매뉴얼 정적 서빙(/manual) |
app/database.py | SQLite 연결 관리. 기동 시 테이블·인덱스 자동 생성 + 컬럼 마이그레이션 |
app/routes/host.py | 수집 엔드포인트 POST /api/v1/host/metrics — 호스트/서비스 upsert + 메트릭 적재 + 알림 평가 |
app/routes/dashboard.py | 대시보드 화면(/), HTMX 부분 갱신, 조회 API, 텔레그램 전송 API |
manuals/ | 사용 매뉴얼 정적 HTML — /manual(랜딩) · /manual/monitor/(이 문서) · /manual/host_agent/ |
app/levels.py | 상태 레벨(ok/warn/crit) 산정 규칙 — 9.1절 표의 구현체 |
app/categories.py | 서비스 type → 카테고리(웹서버/캐시/DB/기타) 분류표 |
app/alerts.py | 알림 엔진 — 전이 감지, 진단 사유 문구 생성, 문제 지속 시간 추적, 인시던트 누적(최근 알림 타임라인·/api/v1/incidents), 호스트 상세·일일 리포트 메시지 작성 |
app/telegram.py | 텔레그램 Bot API 전송(표준 라이브러리만 사용). 토큰 미설정 시 조용히 비활성화 |
app/schemas.py | 수집 페이로드 Pydantic 스키마 |
setup_db.py | 스키마 수동 생성 스크립트(선택 — 앱 기동 시 자동 생성되므로 보통 불필요) |
2.빠른 시작
Docker가 있는 서버라면 3분 안에 기동할 수 있습니다.
# 1) monitor 폴더로 이동
cd monitor
# 2) 환경 설정 — 최소한 API 키는 교체 (4장 참고)
cat > .env <<'EOF'
MONITORING_API_KEY=강력한_임의_문자열
TELEGRAM_BOT_TOKEN=
TELEGRAM_CHAT_ID=
EOF
# 3) 빌드 + 백그라운드 기동
docker compose up -d --build
# 4) 확인
docker logs -f monitor-backend # "Uvicorn running on ..." 확인
curl -s http://localhost:8009/api/v1/hosts -H "X-API-Key: 강력한_임의_문자열"
# → [] (아직 등록된 호스트가 없으면 빈 배열 — 정상)
브라우저에서 열어보세요:
| 주소 | 화면 |
|---|---|
http://서버주소:8009/ | 실시간 대시보드 |
http://서버주소:8009/manual | 내장 사용 매뉴얼 |
http://서버주소:8009/docs | Swagger API 문서 (자동 생성) |
이후 각 감시 대상 서버에 호스트 에이전트를 배포하면 대시보드에 호스트 카드가 자동으로 나타납니다.
방화벽에서 8009 포트 인바운드를 에이전트가 있는 서버 대역과 대시보드 사용자에게만 열어 주세요.
3.설치와 실행
3.1Docker Compose (권장)
monitor/docker-compose.yml에 운영 구성이 준비되어 있습니다.
cd monitor
docker compose up -d --build # 기동
docker compose down # 중지 (데이터는 ./data에 보존)
docker logs -f monitor-backend # 로그
compose가 구성하는 것들:
| 항목 | 내용 |
|---|---|
| 포트 | 8009:8009 |
| 데이터 볼륨 | ./data:/app/data — SQLite 파일이 호스트에 남아 컨테이너를 재생성해도 데이터 유지 |
.env 마운트 | ./.env:/app/.env:ro — 앱이 직접 읽음(이중 안전장치). compose의 environment: 값이 우선 |
| 재시작 정책 | restart: unless-stopped — 서버 재부팅 시 자동 기동 |
3.2로컬 가상환경 실행 (개발용)
cd monitor
# 1) 가상환경 + 의존성
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
# 2) 기동 — 스키마는 첫 기동 때 자동 생성됩니다
uvicorn app.main:app --host 0.0.0.0 --port 8009
# (개발 중 자동 리로드가 필요하면)
uvicorn app.main:app --host 0.0.0.0 --port 8009 --reload
데이터 파일은 monitor/data/monitors.db에 생성됩니다.
python setup_db.py로 스키마를 미리 만들 수도 있지만, 앱이 기동 시
자동 생성·마이그레이션하므로 보통 필요 없습니다.
3.3업데이트와 재기동
cd monitor
git pull # 코드 갱신
docker compose up -d --build # 이미지 재빌드 + 재기동 (데이터 유지)
스키마가 바뀌는 업데이트도 기동 시 자동 반영됩니다(CREATE TABLE IF NOT EXISTS +
컬럼 추가 마이그레이션). 별도 DB 작업이 필요 없습니다.
백엔드가 재시작되면 알림 엔진의 인메모리 상태가 초기화되어, 현재 이상 상태인 항목의 알림이 1회 다시 올 수 있습니다(데이터 유실은 없음). 에이전트들은 백엔드 재기동 중의 페이로드를 버퍼에 보관했다가 재전송하므로 그래프 공백도 생기지 않습니다.
4.환경변수 (.env)
monitor/.env 파일에 작성합니다. 앱이 기동 시 직접 읽으며,
이미 설정된 OS/컨테이너 환경변수가 우선합니다.
| 변수 | 기본값 | 설명 |
|---|---|---|
MONITORING_API_KEY | secret_monitoring_key |
수집·조회 API 인증 키. 모든 에이전트의 MONITOR_API_KEY와 동일해야 함. 운영에서는 반드시 교체 |
DATABASE_URL | sqlite+aiosqlite:///./data/monitors.db |
SQLite 파일 경로 (compose에서는 /app/data/monitors.db로 지정됨) |
DASHBOARD_POLL_INTERVAL | 5 | 대시보드 자동 갱신 주기(초) |
OFFLINE_THRESHOLD_SEC | 15 |
마지막 수신 후 이 시간(초)이 지나면 호스트를 오프라인으로 판정. 에이전트 수집 주기보다 커야 함 |
OFFLINE_SWEEP_INTERVAL | 5 | 오프라인 검사 주기(초) |
OFFLINE_ALERT_CONFIRM_SEC | 60 |
텔레그램 오프라인 알림 확정 시간(초). 이 시간 이상 연속으로 수신이 없어야
알림을 보냅니다 — 수집 지연으로 생기는 순간 공백 오탐 방지.
화면의 오프라인 표시는 OFFLINE_THRESHOLD_SEC 기준으로 더 빠르게 반영됩니다 |
TELEGRAM_BOT_TOKEN | (없음) | 텔레그램 봇 토큰 — 6.1절. 미설정 시 알림 기능 전체가 조용히 비활성화 |
TELEGRAM_CHAT_ID | (없음) | 알림을 받을 대화방 ID (개인/그룹/채널) |
ALERT_RECOVERY | true | false면 "복구" 알림을 보내지 않음 (악화 알림만) |
DAILY_REPORT_TIMES | 09:00,18:00 | 일일 리포트 전송 시각(KST, 쉼표 구분). 비우면 미전송 |
작성 예시
# monitor/.env — 운영 예시
MONITORING_API_KEY=1c9a7e...강력한_임의_문자열
# 텔레그램 알림 (6장 참고 — 비우면 알림 비활성)
TELEGRAM_BOT_TOKEN=123456789:AAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TELEGRAM_CHAT_ID=-1001234567890
# 하루 2회 리포트 (KST)
DAILY_REPORT_TIMES=09:00,18:00
# 복구 알림도 받기
ALERT_RECOVERY=true
.env에는 실제 토큰·키가 들어가므로 git에 커밋하지 마세요.
API 키를 바꾸면 모든 에이전트의 MONITOR_API_KEY도 함께 바꿔야 합니다.
5.대시보드 사용법
http://서버주소:8009/ — 별도 로그인 없이 접근하며,
5초마다 자동 갱신됩니다(HTMX 부분 갱신 — 새로고침 불필요).
5.1화면 구성
화면은 위에서부터 네 구역으로 나뉩니다.
- 상단바 — 현재 시각(KST)과 Live 뱃지
(다음 자동 갱신까지 남은 시간을 초 단위로 카운트다운 — 5→4→…→0으로 줄다가 갱신 순간 리셋),
현황 전송(전체 요약을 즉시 텔레그램으로), 테마 전환(달/해 아이콘),
API(
/docsSwagger), Manual(/manual내장 매뉴얼) 버튼이 있습니다. - 통계 스트립 — 한 줄 요약 바. 전체 호스트·서버 수 → 호스트 상태 칩(정상/주의/위험/오프라인) → 서비스 인스턴스 단위 서버 상태(온라인/주의/오프라인) → 분류(웹서버/캐시/DB) 순서입니다. 호스트 상태 칩은 클릭하면 아래 목록이 해당 상태의 호스트만 필터링되고, 같은 칩을 다시 클릭하면 해제됩니다. 서버 수십 대 중 문제 호스트만 빠르게 추릴 때 사용하세요.
- 호스트 그리드 — 호스트 1대 = 카드 1장. 우측 상단 토글로 컴팩트(카드) / 테이블 뷰를 전환할 수 있습니다(5.4절).
- 최근 알림 타임라인 — 그리드 아래에 상태가 바뀐 순간의 기록이 최신순으로 표시됩니다(시각 · 아이콘 · 대상 · 사유). 텔레그램으로 발송된 전이 알림과 같은 소스라서, 알림을 놓쳤어도 화면에서 "방금 무슨 일이 있었나"를 확인할 수 있습니다. 일일 리포트 발송 시 초기화되며(6.3절), 백엔드 재시작 시에도 비워집니다.
지표 설명 툴팁
대시보드의 모든 데이터 요소에 설명 툴팁이 붙어 있습니다. 수치·상태 뱃지·차트 위에 마우스를 올리면 해당 지표의 의미와 함께 주의/위험 판정 기준이 말풍선으로 표시됩니다 — 통계 스트립, 호스트 카드, 테이블, 상세 모달의 서비스 지표까지 모두 동일하게 동작합니다. 화면의 노랑/빨강 강조가 왜 그런지 궁금할 때 그 자리에서 바로 확인하세요.
모바일 화면
화면 폭 640px 이하(스마트폰)에서는 별도 주소 없이 같은 URL이 자동으로 모바일 레이아웃으로 전환됩니다. 컴팩트 뷰로 고정되어(뷰 토글 숨김) 카드가 1열로 정렬되고, 상세 모달은 화면 아래에서 올라오는 시트 형태로 열립니다. 모바일에서는 조회 전용으로 동작해 전송·삭제 버튼이 표시되지 않습니다(조작은 PC 화면에서).
5.2호스트 카드 읽는 법
- 헤더 — 호스트명, 상태 뱃지(Online / 주의 / 위험 / Offline), 종이비행기 버튼(이 호스트 현황을 텔레그램으로 전송).
- CPU / MEM / DISK — 시스템 사용률. 75% 이상이면 노랑, 90% 이상이면 빨강으로 숫자 색이 바뀝니다(9.1절 임계값).
- 서버 카운트 — 이 호스트에 등록된 서비스 수와 상태별 집계: ● 온라인 · ▲ 주의 · ■ 오프라인(down).
- 문제 사유 배지 — 주의/위험 서비스가 있으면 카드 하단에 어느 서비스가 · 왜 · 얼마나 오래 문제인지 배지로 바로 표시됩니다. 예: 🔴 payment-server · HTTP 500 · 5분째. 모달을 열지 않아도 대응 판단이 가능합니다. 문제가 3개 이상이면 상위 2개 + "+N건 더"로 접히고, 카드를 클릭하면 전체를 볼 수 있습니다. 오프라인 호스트는 ⚫ 오프라인 — 수신 끊김 · 지속 시간이 표시됩니다. (지속 시간은 인메모리 추적이라 백엔드 재시작 시 다시 계산됩니다)
| 상태 색 | 의미 |
|---|---|
| 초록 (ok) | 정상 |
| 노랑 (warn) | 주의 — 임계값 초과 (9.1절) |
| 빨강 (crit) | 위험 — 서비스 down 또는 위험 임계 초과 |
| 회색 (offline) | 오프라인 — 에이전트 수신 끊김 (9.2절) |
호스트 카드의 전체 상태는 시스템 상태와 소속 서비스들 중 가장 나쁜 상태로 롤업됩니다. 카드의 IP·OS·서비스별 상세 지표는 카드를 클릭하면 열리는 상세 모달에서 확인합니다.
5.3호스트 상세 모달
카드(또는 테이블 행)를 클릭하면 상세 모달이 열립니다. 모달도 5초 갱신 주기에 맞춰 자동으로 최신화되며, ESC 또는 바깥 영역 클릭으로 닫습니다.
모달은 위에서부터:
- 헤더·메타 — 호스트명, 상태 뱃지, 현황 전송·닫기 버튼 (오프라인 상태면 삭제 버튼 추가), IP · OS · 마지막 수신 시각 · 서비스 수.
- 진단 패널 — 왜 이 상태인지를 임계값 기준으로 항목별로 풀어 주고, 각 항목에 조치 가이드가 붙습니다. 모든 지표가 정상이면 초록 패널 1줄로 표시됩니다.
- 리눅스 정보 — CPU/Memory/Disk 게이지 바, 네트워크 TX/RX 속도, Load Average(1m/5m/15m), 그리고 최근 폴링에서 적립한 CPU 추이 그래프.
- 서비스 패널 — 서비스가 웹서버/캐시/DB/기타 카테고리로 묶여 표시됩니다. 각 패널에는 인스턴스명 · type · runtime 배지(🐳 Docker + 컨테이너명 / ⚙️ Native) · status 뱃지(UP 등)와 type별 핵심 지표 셀이 나옵니다. 주의/위험 상태인 서비스는 status 뱃지 옆에 ⏱ 지속 시간(문제가 시작된 후 경과)이 함께 표시됩니다.
| 서비스 type | 모달에 표시되는 지표 |
|---|---|
postgres | Connections(활성/최대), DB Size, Cache Hit, Index Hit, Deadlocks, Dead Tuples(비율), Locks |
mysql | Connections, Running, Slow Queries, Questions, Uptime |
redis | Memory, Hit Rate, Clients, Ops/sec, Evicted Keys, Uptime |
nginx | Active Conn, Requests, Accepts, Reading/Writing/Waiting, Status |
| HTTP 앱 (fastapi/nodejs/nextjs …) | Status Code, Latency, Reachable |
| + Docker 컨테이너 | 컨테이너 상태, 컨테이너 CPU/MEM(%), 재시작 횟수, Health |
임계값을 초과한 지표는 셀 값이 노랑/빨강으로 강조됩니다. 아래는 위험 상태 호스트의 예 — 진단 패널이 어떤 지표가 어떤 기준을 넘었는지, 무엇을 하면 되는지 알려줍니다.
5.4화면 동작과 버튼
| 동작 | 설명 |
|---|---|
| 상태 필터 | 통계 스트립의 호스트 상태 칩(정상/주의/위험/오프라인) 클릭 — 해당 상태의 호스트만 표시, 재클릭 시 해제. 컴팩트/테이블 뷰 모두 적용되며 5초 자동 갱신에도 유지됩니다 |
| 컴팩트 / 테이블 뷰 | 우측 상단 토글 — 카드 그리드와 한눈에 보는 표 형태를 전환. 선택은 브라우저에 저장됩니다 |
| 지표 툴팁 | 모든 수치·뱃지·차트에 마우스를 올리면 지표의 의미와 주의/위험 임계값이 말풍선으로 표시됩니다 (5.1절) |
| 자동 갱신 카운트다운 | 상단 Live 뱃지의 숫자가 다음 갱신까지 남은 초를 표시 — 데이터가 다시 로드되는 순간 리셋됩니다 |
| 현황 전송 | 상단 버튼 — 전체 요약 리포트를 텔레그램으로 전송 (POST /notify/summary).
실수 방지를 위해 확인 모달에서 "전송"을 눌러야 실제 전송되며, 결과는 화면 하단 토스트로 안내 |
| 호스트별 전송 | 카드·테이블·모달의 종이비행기 버튼 — 해당 호스트 상세 현황을 텔레그램으로 전송 (POST /notify/host/{id}). 역시 확인 모달을 거칩니다 |
| API / 매뉴얼 | 상단바 링크 — /docs(Swagger)와 /manual(내장 사용 매뉴얼) |
| 테마 | 라이트/다크 전환 (브라우저에 저장, 최초에는 OS 설정을 따름) |
5.5호스트 삭제
퇴역한 서버는 삭제 버튼(휴지통)으로 제거합니다. 실수 방지를 위해 오프라인 상태의 호스트만 삭제할 수 있고, 삭제 전에 호스트명을 똑같이 입력해 확인해야 합니다.
- 대상 서버에서 에이전트 중지 (
./stop.sh또는systemctl stop host-agent) - 15초 뒤 카드가 오프라인(회색)으로 바뀌면, 상세 모달 헤더 또는 테이블 뷰 행 끝의 휴지통 버튼 클릭
- 확인 창에 호스트명을 정확히 입력하면 삭제됩니다
삭제하면 해당 호스트의 모든 메트릭 이력이 함께 삭제됩니다(CASCADE). 온라인 상태에서 삭제를 시도하면 409로 거절됩니다.
6.텔레그램 알림
6.1봇 생성과 연결
- 봇 만들기 — 텔레그램에서
@BotFather를 검색해 대화 시작 →/newbot입력 → 봇 이름과 사용자명(…bot으로 끝나야 함) 지정 → 발급된 토큰(123456789:AAxx...형태)을 복사 - 대화방 준비 — 알림을 받을 곳을 정합니다.
- 개인으로 받기: 방금 만든 봇에게 아무 메시지나 1번 보내기
- 그룹으로 받기: 그룹에 봇을 초대하고 그룹에서 아무 메시지나 1번 보내기
- chat_id 알아내기 — 아래 명령의 응답에서
"chat":{"id": ...}값을 찾습니다. 그룹은 음수(-100...)입니다.curl -s "https://api.telegram.org/bot<토큰>/getUpdates" | python3 -m json.tool | grep -A2 '"chat"' - .env에 설정 후 재기동
TELEGRAM_BOT_TOKEN=123456789:AAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx TELEGRAM_CHAT_ID=-1001234567890docker compose up -d # 재기동 — 로그에 "[telegram] 알림 활성화됨" 확인 - 전송 테스트
curl -s -X POST http://localhost:8009/api/v1/notify/message \ -H "X-API-Key: 설정한_키" -H "Content-Type: application/json" \ -d '{"text": "<b>테스트</b> 모니터링 알림 연결 확인"}' # → {"sent": true}
6.2알림 동작 원리 — 전이 기반
임계값을 초과했다고 매 수신(5초)마다 알리는 것이 아니라, 상태가 바뀌는 순간에만 1회 알립니다. 같은 상태가 지속되면 추가 알림이 없습니다(알림 스팸 방지).
오프라인 알림은 OFFLINE_ALERT_CONFIRM_SEC(기본 60초) 이상
연속으로 수신이 없어야 발송됩니다. 화면 표시는 15초부터 오프라인으로
바뀌지만, 수집 지연으로 생기는 몇 초의 공백에는 알림을 보내지 않아 오탐을 방지합니다.
알림 메시지에는 마지막 수신 후 경과 시간이 함께 표시됩니다.
알림 메시지에는 구체적 사유가 함께 옵니다 — 예:
🔴 [위험] prod-01 · payment-db (postgres)
• 캐시 히트율 88.0%
• 커넥션 48/50 (96%)
2026-07-08 14:03:22 KST
전이 판정 단위(각각 독립적으로 추적됩니다):
- 호스트 시스템 (CPU/MEM/DISK)
- 서비스 인스턴스 각각 (type·name 단위)
- 호스트 온라인/오프라인
발생한 전이 알림은 텔레그램 발송과 동시에 대시보드 하단 "최근 알림" 타임라인
(5.1절)과 GET /api/v1/incidents(7장)에도
같은 내용으로 기록됩니다. 또 전이가 시작된 시각을 기억해 두었다가, 문제가 지속되는 동안
호스트 카드 배지와 상세 모달에 지속 시간("5분째")으로 표시합니다.
6.3일일 리포트
DAILY_REPORT_TIMES(기본 09:00, 18:00 KST)에 전체 현황 요약이 자동 전송됩니다.
- 전체 집계 — 호스트 온라인/오프라인 수, 서비스 정상/주의/위험/오프라인 수
- 기간 내 발생 알림 건수 (리포트 전송 시 카운터 초기화)
- 호스트별 요약 — 상태가 나쁜 호스트가 위로 정렬, 문제 서비스와 사유 표시
같은 내용을 수동으로 받으려면 대시보드의 "현황 전송" 버튼 또는
POST /notify/summary를 호출하세요.
7.REST API 레퍼런스
전체 스키마는 http://서버주소:8009/docs(Swagger)에서 인터랙티브하게 확인할 수 있습니다.
KEY 표시는 X-API-Key 헤더가 필요한 API입니다.
/docs) — 각 엔드포인트를 펼쳐
브라우저에서 바로 호출해 볼 수 있습니다| 엔드포인트 | 인증 | 설명 |
|---|---|---|
POST /api/v1/host/metrics | KEY | 에이전트 수집 (직접 호출할 일 없음) |
GET /api/v1/hosts | KEY | 호스트 목록 + 요약 상태 |
GET /api/v1/hosts/{id} | KEY | 호스트 상세 (서비스별 전체 지표) |
GET /api/v1/incidents | KEY | 최근 발생 알림 목록 (최신순, ?limit=20) |
POST /api/v1/notify/message | KEY | 임의 텍스트 텔레그램 전송 |
POST /notify/summary | 없음 | 전체 현황 요약 텔레그램 전송 (대시보드 버튼용) |
POST /notify/host/{id} | 없음 | 호스트 상세 현황 텔레그램 전송 (대시보드 버튼용) |
DELETE /hosts/{id} | 없음 | 오프라인 호스트 삭제 (온라인이면 409) |
GET / · /manual · /docs | 없음 | 대시보드 · 내장 매뉴얼 · Swagger |
호스트 목록 조회
curl -s http://localhost:8009/api/v1/hosts -H "X-API-Key: 설정한_키"
[
{
"id": "b1f2...", "hostname": "prod-01", "ip_address": "10.0.0.5",
"os_info": "ubuntu 22.04", "status": "warn", "is_active": true,
"cpu_usage": 40.0, "memory_usage": 63.0, "disk_usage": 83.0, "load_1m": 0.8,
"service_count": 3,
"servers": {"online": 3, "attention": 0, "offline": 0},
"last_seen": "2026-07-08 14:00:00"
}
]
호스트 상세 조회
curl -s http://localhost:8009/api/v1/hosts/b1f2... -H "X-API-Key: 설정한_키"
{
"id": "b1f2...", "hostname": "prod-01", "status": "warn", "is_active": true,
"cpu_usage": 40, "memory_usage": 63, "disk_usage": 83, "load_1m": 0.8,
"groups": [
{"key": "db", "label": "DB", "status": "crit", "services": [
{"type": "postgres", "name": "payment-db", "runtime": "docker",
"status": "up", "level": "crit",
"reasons": ["캐시 히트율 88.0%", "커넥션 48/50 (96%)"], "issue_duration": "5분째",
"metrics": {"cache_hit_ratio": 88.0, "active_connections": 48, "total_connections": 50}}
]}
]
}
최근 발생 알림 조회
대시보드 하단 "최근 알림" 타임라인과 같은 데이터입니다. 인메모리 누적분이라 일일 리포트 발송 시 초기화되고, 백엔드 재시작 시 사라집니다.
curl -s "http://localhost:8009/api/v1/incidents?limit=20" -H "X-API-Key: 설정한_키"
[
{"ts": "2026-07-13 10:30:01", "level": "ok",
"title": "payment · payment-server (fastapi)",
"reasons": ["정상으로 복구되었습니다"], "recovered": true},
{"ts": "2026-07-13 10:22:33", "level": "crit",
"title": "payment · payment-server (fastapi)",
"reasons": ["HTTP 500"], "recovered": false}
]
임의 메시지 텔레그램 전송
배포 스크립트·점검 공지 등 외부 시스템에서 알림 채널을 재사용할 수 있습니다.
텔레그램 HTML 서식(<b>, <i>, <code>)을 지원합니다.
curl -s -X POST http://localhost:8009/api/v1/notify/message \
-H "X-API-Key: 설정한_키" -H "Content-Type: application/json" \
-d '{"text": "<b>점검 안내</b>\n22:00~22:30 서비스가 잠시 중단됩니다."}'
# → {"sent": true} (텔레그램 미설정 시 {"sent": false, "detail": "..."})
8.데이터베이스
SQLite 파일 1개(data/monitors.db)에 모든 데이터가 저장됩니다.
스키마는 앱 기동 시 자동 생성·마이그레이션됩니다.
8.1테이블 구조
| 테이블 | 주요 컬럼 | 비고 |
|---|---|---|
hosts | id(UUID), hostname, ip_address, os_info, is_active | 호스트 삭제 시 하위 데이터 CASCADE 삭제 |
host_metrics | cpu/memory/disk_usage, load_1m/5m/15m, bytes_sent/recv, disk_read/write_bps, timestamp | timestamp는 에이전트 수집 시각(버퍼 재전송 시에도 정확) |
services | id(UUID), category, type, name, runtime, container, image | category는 type에서 자동 산정 |
service_metrics | status, metrics_json, timestamp | type별 지표를 JSON 그대로 보존 |
직접 조회 예시:
sqlite3 monitor/data/monitors.db \
"SELECT timestamp, cpu_usage, memory_usage FROM host_metrics
ORDER BY id DESC LIMIT 10;"
8.2백업과 정리
# 온라인 백업 (서비스 중단 없이 안전)
sqlite3 monitor/data/monitors.db ".backup monitor/data/backup-$(date +%Y%m%d).db"
# 오래된 메트릭 정리 (예: 30일 이전) — 필요 시 cron 등록
sqlite3 monitor/data/monitors.db "
DELETE FROM host_metrics WHERE timestamp < datetime('now', '-30 days');
DELETE FROM service_metrics WHERE timestamp < datetime('now', '-30 days');
VACUUM;"
메트릭은 5초 주기로 계속 쌓입니다(호스트당 하루 약 1.7만 행 + 서비스당 1.7만 행). 수개월 이상 운영한다면 위 정리 쿼리를 주기적으로 실행하는 것을 권장합니다.
9.운영
9.1상태 레벨과 임계값
수집된 지표로 ok · warn · crit을 판정합니다. 여러 조건이 겹치면 가장 나쁜 레벨이 적용되고, 호스트 카드는 시스템+서비스 전체의 최악 레벨로 롤업됩니다.
| 대상 | 주의(warn) | 위험(crit) |
|---|---|---|
| 호스트 CPU/MEM/DISK | ≥ 75% | ≥ 90% |
| 서비스 status | degraded, restarting, unhealthy | down, exited, dead |
| 컨테이너 | 재시작 ≥ 5회, 메모리 ≥ 75% | 정지 상태, 메모리 ≥ 90%, 헬스체크 unhealthy |
| postgres 캐시 히트율 | < 95% | < 90% |
| postgres 데드락 | > 0 | ≥ 5 |
| postgres 죽은 튜플 | 1,000개 이상이면서 ≥ 10% | 1,000개 이상이면서 ≥ 25% |
| postgres/mysql 연결 사용률 | ≥ 80% | ≥ 95% |
| redis 히트율 | < 95% | < 90% |
| HTTP 앱 상태 코드 | 4xx | 5xx |
| HTTP 앱 응답 지연 | ≥ 1,000ms | ≥ 3,000ms |
죽은 튜플은 절대량 하한(1,000개)을 함께 봅니다 — 행이 수백 개뿐인
소형 DB에서는 죽은 튜플 몇 개만으로 비율이 10%를 넘어 의미 없는 경고가 반복되기 때문입니다.
하한은 app/levels.py의 DEAD_TUPLES_MIN으로 조정합니다
(대시보드 쪽 DEAD_MIN도 동일 값으로).
임계값을 바꾸려면 app/levels.py(레벨 판정)와
app/alerts.py(사유 문구)를 수정한 뒤 재기동하세요.
9.2오프라인 판정
백그라운드 스윕이 OFFLINE_SWEEP_INTERVAL(5초)마다 각 호스트의 마지막 수신 시각을
확인합니다. 판정은 두 단계입니다:
- 화면 표시 —
OFFLINE_THRESHOLD_SEC(15초)를 넘기면 대시보드에서 오프라인(회색)으로 표시됩니다. 재수신되면 자동 복귀합니다. - 텔레그램 알림 —
OFFLINE_ALERT_CONFIRM_SEC(60초) 이상 연속으로 수신이 없어야 ⚫ 알림을 보냅니다. 에이전트는 수집(도커 stats·DB 조회 등)에 걸리는 시간만큼 전송 간격이 벌어질 수 있어, 표시 임계값(15초)만으로 알림을 보내면 살아 있는 호스트에도 오탐 알림이 반복될 수 있기 때문입니다.
에이전트의 COLLECT_INTERVAL_SEC보다 두 임계값 모두 충분히 커야 합니다.
실제 전송 간격은 수집 주기 + 수집 소요 시간이므로, 수집 주기를 늘렸다면
OFFLINE_THRESHOLD_SEC은 실제 전송 간격의 3배,
OFFLINE_ALERT_CONFIRM_SEC은 6배 정도를 권장합니다.
9.3보안 권장 사항
- 기본
MONITORING_API_KEY(secret_monitoring_key)를 강력한 값으로 교체하고 모든 에이전트에 동일 적용 - 8009 포트는 방화벽에서 에이전트 서버 대역과 관리자에게만 허용
- 대시보드는 인증이 없으므로 외부 공개 시 리버스 프록시(nginx 등)에서 Basic Auth·IP 제한 권장
.env(토큰·키 포함)는 git에 커밋 금지
9.4트러블슈팅
| 증상 | 확인 사항 |
|---|---|
| 대시보드가 안 열림 | docker ps로 컨테이너 확인 → docker logs monitor-backend에서 기동 오류 확인 → 8009 방화벽 |
| 호스트가 안 나타남 | 에이전트 쪽 문제일 가능성이 높음 — 에이전트 로그에 전송 성공이 찍히는지부터 확인
(에이전트 매뉴얼 6.4) |
| API가 401을 반환 | X-API-Key 헤더 누락 또는 MONITORING_API_KEY 불일치 |
| 텔레그램 알림이 안 옴 | 기동 로그에 [telegram] 알림 활성화됨이 있는지 → 없으면 토큰/chat_id 미설정.
POST /api/v1/notify/message로 전송 테스트 → {"sent": false}면 로그의
[telegram] 전송 실패 사유 확인 (잘못된 chat_id는 400, 봇 차단은 403) |
| 알림이 너무 자주 옴 | 임계값 경계에서 상태가 진동하는 경우 — 해당 지표의 임계값을 조정하거나(app/levels.py),
복구 알림을 끄기(ALERT_RECOVERY=false) |
| 호스트가 온라인인데 삭제하고 싶음 | 정상 동작 — 먼저 해당 서버의 에이전트를 중지해 오프라인으로 만든 뒤 삭제 (5.5절) |
| 시간이 이상하게 표시됨 | 저장은 UTC, 표시·알림은 KST 기준 — 서버 시계(NTP) 동기화 확인 |
| 데이터 파일이 계속 커짐 | 오래된 메트릭 정리 쿼리 실행 (8.2절) |