Monitoring (Grafana LGTM)
multi-saas-kit의 공식 통합 모니터링 플랫폼입니다. 모든 프로젝트(Laravel)와 플랫폼 서비스(_services/)의 로그·메트릭·알림을 단일 Grafana 인터페이스에서 관리합니다.
개요
| 항목 | 내용 |
|---|---|
| 위치 | workspace/_devtools/monitoring |
| 스택 | Grafana + Loki + Prometheus + Promtail |
| 권장 도메인 | monitor.devtools.{platform-domain} |
| 설 계 문서 | ADR-017 (Monitoring Stack Grafana LGTM) |
왜 중요한가
운영 중인 플랫폼에서 **"지금 무슨 일이 일어나고 있는지"**를 즉시 답할 수 있어야 장애를 빨리 감지하고, 고객 문의에 근거로 답하며, 비용/사용량을 추적할 수 있습니다. 모니터링은 인프라의 필수 구성요소이지 선택 사항이 아닙니다.
multi-saas-kit은 멀티테넌트 SaaS 플랫폼이라 다음이 특히 중요합니다:
- 여러 고객 프로젝트의 로그가 한 화면에서 조회되지만 격리되어 있어야 함
- 에러 스파이크가 자동으로 감지되어 운영 알림 채널로 전달
- 서비스 버전/배포 이력이 대시보드에서 추적 가능
- AI 점검 시스템이 프로그래밍 방식으로 로그/메트릭 쿼리 가능
Grafana LGTM 스택은 이 모든 것을 OSS 기반으로 충족합니다.
스택 구성
컴포넌트
| 컴포넌트 | 역할 |
|---|---|
| Grafana | 대시보드, Explore UI, 알림 관리, 사용자 인증 |
| Loki | 로그 저장소. Prometheus 스타일 레이블 기반 |
| Prometheus | 메트릭 시계열 DB. /metrics 엔드포인트 pull |
| Promtail | 로그 수집 agent. journald + 파일 + Docker 지원 |
| (선택) Tempo | 분산 트레이싱 저장소 |
| (선택) Alertmanager | 고급 알림 라우팅 |
데이터 흐름
[관측 대상] ──► [수집] ──► [저장] ──► [조회/알림]
Laravel storage/logs/*.log ─┐
systemd journal (Go 서비스) ─┼─► Promtail ─► Loki ──┐
Docker containers stdout ─┘ │
├─► Grafana
/metrics endpoints ──────────► Prometheus ──────────┘ │
▼
(Slack / Email /
Report files)
시작하기
로컬 개발
make up NAME=_devtools/monitoring
접속은 배포 환경의 monitoring 도메인을 사용합니다. 관리자 자격증명은 .env의
GRAFANA_ADMIN_PASSWORD로 관리하며 문서에 값을 기록하 지 않습니다.
운영 (기존 도메인 구조 기반)
루트 .env에 PLATFORM_DEVTOOLS_URL이 설정되어 있다면 자동으로 monitor.{platform-domain} 또는 monitor.devtools.{platform-domain}으로 접근 가능합니다.
Nginx Proxy Manager에 다음 Proxy Host 등록:
- Domain:
monitor.devtools.codebase.how - Forward:
<monitoring-host> - SSL: Let's Encrypt
- WebSocket Support: ON
설정 파일 구조
workspace/_devtools/monitoring/
├── docker-compose.yml # 4개 서비스
├── .env.example # 환경변수 템플릿
├── Makefile
├── AGENTS.md
└── configs/
├── loki/
│ └── loki-config.yaml # 30일 retention, filesystem storage
├── promtail/
│ └── promtail.yaml # journald + Laravel + Docker scrape
├── prometheus/
│ └── prometheus.yml # scrape targets
└── grafana/
├── provisioning/
│ ├── datasources/
│ │ └── datasources.yaml # Loki + Prometheus 자동 등록
│ ├── dashboards/
│ │ └── dashboards.yaml # JSON 자동 로드
│ └── alerting/ # alerting canonical 3파일
└── dashboards/
├── overview.json
├── services-health.json
├── laravel-projects.json
└── edge-relay.json
수집 대상 설정
Laravel 프로젝트
특별한 설정 변경 없이 동작합니다. Laravel 기본 로그(storage/logs/laravel-*.log)를 Promtail이 tail합니다.
권장: JSON 채널 사용 — config/logging.php:
'stack' => [
'driver' => 'stack',
'channels' => ['json'],
'ignore_exceptions' => false,
],
'json' => [
'driver' => 'daily',
'path' => storage_path('logs/laravel.log'),
'formatter' => Monolog\Formatter\JsonFormatter::class,
'days' => 14,
],
Loki 레이블 자동 부여:
project— 프로젝트 이름 (예:apis.how)app—laravelenv—APP_ENV값
_services/ Go 서비스
slog JSON 출력이 systemd journal로 흐르므로 추가 작업 없음. Promtail이 _SYSTEMD_UNIT=edge-relay-service.service 같은 필터로 scrape.
Loki 레이블:
service— 서비스 이름 (예:edge-relay-service)runtime—goenv—production
Docker 컨테이너 (LiteLLM 등)
Docker daemon 소켓 접근으로 자동 수집:
# promtail.yaml
scrape_configs:
- job_name: docker
docker_sd_configs:
- host: unix:///var/run/docker.sock
메트릭 (/metrics 엔드포인트)
각 서비스의 /metrics를 Prometheus가 주기적으로 pull:
# prometheus.yml
global:
# 저사양 호스트(P330 등)에서 Prometheus 자체 부하를 줄이기 위해 30s 권장.
# 더 짧은 간격이 필요한 잡은 잡별 scrape_interval로 개별 단축 가능.
scrape_interval: 30s
evaluation_interval: 30s
scrape_configs:
- job_name: edge-relay-service
static_configs:
- targets: ['<edge-relay-metrics-target>']
metrics_path: /metrics
scheme: https
- job_name: speech-service
static_configs:
- targets: ['<speech-metrics-target>']
metrics_path: /metrics
scheme: https
| 호스트 환경 | 권장 scrape_interval |
|---|---|
| 저사양 자가 호스팅 (4~8코어, 컨테이너 50개+) | 30s ~ 60s |
| 일반 운영 서버 | 30s (기본값) |
| 알람 정밀도 우선 (특정 잡만) | 잡별 scrape_interval: 10s 오버라이드 |
scrape_interval을 절반으로 줄이면 Prometheus가 처리하는 시계열 수가 그만큼 늘어나 CPU/메모리 사용량이 비례 증가합니다. 대시보드 새로고침 주기보다 짧을 필요는 없습니다.
기본 제공 대시보드
1. Overview
- 전체 서비스 상태 (up/down)
- 최근 1h 총 요청 수, 에러 수, 평균 지연
- 활성 프로젝트 수
2. Services Health
_services/각 서비스/healthz결과- 버전, 업타임, 최근 재시작
- 버전과
services-registry.json일치 여부
3. Laravel Projects
- 프로젝트별 ERROR/WARN 로그 카운트
- 최근 에러 메시지 테이블
- Laravel 응답 지연 분포
4. Edge Relay
- 모듈별(speech, chorus) 요청 수/지연
- 세션 검증 성공/실패율
- 업스트림 지연
5. Logs Explorer
- 사용자 정의 LogQL 쿼리
- 레이블 조합으로 빠른 필터링
LogQL 쿼리 예시
Grafana Explore에서 바로 사용 가능:
# 1시간 내 전체 edge-relay 에러
{service="edge-relay-service",level="ERROR"} | json
# 세션 서명 검증 실패만
{service="edge-relay-service"} | json | error_code="signature_mismatch"
# apis.how Laravel 에러 중 chorus 언급
{project="apis.how",app="laravel"} |= "chorus" |= "error"
# 특정 request_id 추적 (여러 서비스 동시)
{service=~"edge-relay-service|chorus-api"} | json | request_id="abc123"
# 5분 window 에러율
sum by (service) (rate({level="ERROR"}[5m]))
알림 설정
알림 엔진은 Grafana Alerting으로 일원화되어 있습니다 (ADR-021). 별도 watcher 데몬 없이 Grafana가 평가·디스패치·UI를 모두 담당합니다.
소스 오브 트루스
규칙·채널·라우팅은 Grafana 네이티브 Provisioning 포맷으로 현재 Compose 경로에서 Git 관리:
구
_services/monitoring/폴더와 중간 변환 계층은 [ADR-021]에서 제거되었습니다.grafana_lgtmAnsible role은 아직 미구현 scaffold이며 현재 alerting 원본이 아닙니다.
동작 흐름
alert-rules.yaml + contact-points.yaml + notification-policies.yaml
↓ _devtools/monitoring Compose read-only mount
Grafana provisioning/alerting
↓ Grafana Alerting
Discord contact point
운영 UI
monitor.devtools.codebase.how → Alerting 메뉴에서 현재 상태, 무음, 히스토리를 확인합니다.
Provisioning으로 관리되는 규칙은 UI 수정 불가(allowUiUpdates: false) — 규칙 변경은 반드시 alert-rules.yaml PR로.
심각도와 채널
| severity | 채널 | 예시 |
|---|---|---|
critical | Discord | 서비스 다운, panic |
error | Discord | 5xx 스파이크 |
warning | Discord | 지연 증가 |
info | Discord | 통계성 이벤트 |
로그 보존 정책
| 레이어 | 보존 기간 | 비고 |
|---|---|---|
Laravel storage/logs | 7일 (logrotate) | 프로젝트별 |
| systemd journal | 14일 (또는 2GB) | journald.conf |
| Docker logs | 7일 (100MB per container) | daemon.json |
| Loki (중앙) | 30일 (기본) / 90일 (중요) | loki-config.yaml |
| Prometheus | 15일 | --storage.tsdb.retention.time |
| 장기 S3/R2 | 1년+ | Phase 3 |
멀티테넌시 (프로젝트 격리)
Loki의 X-Scope-OrgID 헤더로 프로젝트별 격리 가능합니다. Grafana 데이터소스를 프로젝트별로 분리 설정하면 각 고객은 자기 프로젝트 로그만 조회.
현재: 단일 조직, 운영자만 접근. 고객 직접 접근은 Phase 3 (Laravel SSO 연동).
보안
| 항목 | 정 책 |
|---|---|
| Grafana 외부 접근 | NPM + SSL 필수 |
| Loki/Prometheus | Docker 내부 네트워크만 |
| 관리자 비밀번호 | GRAFANA_ADMIN_PASSWORD env 필수 |
| 로그 민감 정보 | API 토큰, 비번, 프롬프트 본문 로그 금지 |
| 방화벽 | Tailscale ACL 권장 |
AI 모니터링 연동
.ai-core/config/services-registry.json에 등록된 서비스는 AI 정기 점검 대상:
- AI는 Grafana HTTP API (
/api/ds/query)로 로그/메트릭 쿼리 services-registry.json의error_patterns를 LogQL로 변환- 결과를
.ai-core/data/reports/services/_ai-summary/{date}/에 저장 - 이상 발견 시 AskUserQuestion으로 사용자에게 대응 제안
자세한 내용은 AI 모니터링 계약 참조.
운영 가이드
일별 체크리스트
- Overview 대시보드 확인 — 이상 징후 없음
- Slack critical 채널 미해결 항목 확인
-
.ai-core/data/reports/services/_ai-summary/{오늘}리뷰
주별 체크리스트
- 저장 공간 사용률 (
data/platform/monitoring/디스크 사용) - 주요 서비스 버전 업데이트 검토
- AI 주간 보고서 검토
월별 체크리스트
- 보존 정책 준수 확인
- 알림 규칙 튜닝 (false positive)
- 미사용 대시보드 정리
- 비용/리소스 트렌드 리뷰
트러블슈팅
Grafana에 로그가 안 보여요
- Promtail 상태 확인:
docker logs -f promtail - Promtail → Loki 연결 상태는 서비스 healthcheck 또는 Grafana datasource 상태에서 확인
- Loki 수집 확인: Grafana Explore에서
{job=~".+"}쿼리 - 레이블 확인: 로그가 뜨는데 필터가 안 맞는 것일 수 있음
"Monitoring" 카드가 unreachable
_devtools/monitoring컨테이너 기동 확인:make status NAME=_devtools/monitoring- NPM Proxy Host 등록 여부
- SSL 인증서 발급 여부
- Grafana
serve_from_sub_path설정 (경로 기반 라우팅 시)
에러 알림이 너무 많아요
- 알림 규칙의
threshold/for값 상향 cooldown_sec증가severity를error→warning으로 하향 (즉시 알림 아닌 일별 digest)
디스크 공간이 부족해요
- Loki retention 단축:
loki-config.yaml의retention_period감소 - Prometheus retention 단축:
--storage.tsdb.retention.time=7d - 장기 보관을 S3로 이관 (Phase 3)
단계별 도입 로드맵
| Phase | 범위 | 상태 |
|---|---|---|
| 1 | 기본 스택 + 로그 중앙화 + 대시보드 5개 | 🚧 진행 중 |
| 2 | 메트릭 확산 + contact point 운영 강화 | ⏳ 예정 |
| 3 | Grafana SSO + Loki 장기 보관 | ⏳ |
| 4 | AI 자가 점검 스킬 통합 | ⏳ |
| 5 | Tempo (트레이싱) | 🔶 선택 |