Monitoring (Grafana LGTM)
multi-saas-kit의 공식 통합 모니터링 플랫폼입니다. 모든 프로젝트(Laravel)와 플랫폼 서비스(_services/)의 로그·메트릭·알림을 단일 Grafana 인터페이스에서 관리합니다.
개요
| 항목 | 내용 |
|---|---|
| 위치 | workspace/_devtools/monitoring |
| 스택 | Grafana + Loki + Prometheus + Promtail |
| 포트 | 7060 (로컬) |
| 권장 도메인 | monitor.devtools.{platform-domain} |
| 설계 문서 | ADR-017 (Monitoring Stack Grafana LGTM) |
왜 중요한가
운영 중인 플랫폼에서 **"지금 무슨 일이 일어나고 있는지"**를 즉시 답할 수 있어야 장애를 빨리 감지하고, 고객 문의에 근거로 답하며, 비용/사용량을 추적할 수 있습니다. 모니터링은 인프라의 필수 구성요소이지 선택 사항이 아닙니다.
multi-saas-kit은 멀티테넌트 SaaS 플랫폼이라 다음이 특히 중요합니다:
- 여러 고객 프로젝트의 로그가 한 화면에서 조회되지만 격리되어 있어야 함
- 에러 스파이크가 자동으로 감지되어 Slack/Email 알림
- 서비스 버전/배포 이력이 대시보드에서 추적 가능
- AI 점검 시스템이 프로그래밍 방식으로 로그/메트릭 쿼리 가능
Grafana LGTM 스택은 이 모든 것을 OSS 기반으로 충족합니다.
스택 구성
컴포넌트
| 컴포넌트 | 역할 | 내부 포트 |
|---|---|---|
| Grafana | 대시 보드, Explore UI, 알림 관리, 사용자 인증 | 3000 |
| Loki | 로그 저장소. Prometheus 스타일 레이블 기반 | 3100 |
| Prometheus | 메트릭 시계열 DB. /metrics 엔드포인트 pull | 9090 |
| Promtail | 로그 수집 agent. journald + 파일 + Docker 지원 | 9080 |
| (선택) Tempo | 분산 트레이싱 저장소 | 3200 |
| (선택) Alertmanager | 고급 알림 라우팅 | 9093 |
데이터 흐름
[관측 대상] ──► [수집] ──► [저장] ──► [조회/알림]
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
접속:
- 로컬:
http://localhost:7060 - 기본 계정:
admin/.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:
{host}:7060 - 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 자동 로드
└── 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.codebase.how:443']
metrics_path: /metrics
scheme: https
- job_name: speech-service
static_configs:
- targets: ['speech.apis.how:443']
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 포맷으로 Ansible role 안에서 Git 관리:
_infra/ansible/roles/grafana_lgtm/files/provisioning/alerting/rules.yaml_infra/ansible/roles/grafana_lgtm/files/provisioning/alerting/contact-points.yaml
구
_services/monitoring/폴더와 중간 변환 계층은 [ADR-021]에서 제거되어, 중간 단계 없이 바로 Grafana가 읽는 포맷으로 편집합니다.
동작 흐름
rules.yaml + contact-points.yaml (Git, Grafana 네이티브)
↓ Ansible playbook (scripts/ansible-account.sh self site --tags grafana_lgtm)
/etc/grafana/provisioning/alerting/ (Grafana 서버)
↓ Grafana Alerting
Slack / SMTP / Webhook(파일 리포트)
운영 UI
monitor.devtools.codebase.how → Alerting 메뉴에서 현재 상태, 무음, 히스토리 확인·편집.
Provisioning으로 관리되는 규칙은 UI 수정 불가(allowUiUpdates: false) — 규칙 변경은 반드시 alert-rules.yaml PR로.
심각도와 채널
| severity | 채널 | 예시 |
|---|---|---|
critical | Slack + Email + Report | 서비스 다운, panic |
error | Slack + Report | 5xx 스파이크 |
warning | Slack (daily digest) | 지연 증가 |
info | Report만 | 통계성 이벤트 |
로그 보존 정책
| 레이어 | 보존 기간 | 비고 |
|---|---|---|
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 권장 |