Files
project-infra/docs/standards/infra/observability-health.md
T

220 lines
9.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# observability / health 기준
## 목적
이 문서는 1000+ 서비스가 공통으로 따르는 observability 기준선이다. metrics 수집 경로, golden signal 정의, 로그 포맷 / 수집 stack, trace 수집(OTel), health endpoint 외부 비공개 원칙, cardinality 가드를 한 파일에 고정한다.
## 공식 / 업계 근거
- **Google SRE Book (Ch.6)**: Four Golden Signals = **Latency, Traffic, Errors, Saturation**. 운영 대시보드의 기본 구성 원칙.
- **RED method (Tom Wilkie, Weaveworks)**: request-driven service에 대해 **Rate, Errors, Duration**.
- **USE method (Brendan Gregg)**: resource에 대해 **Utilization, Saturation, Errors**.
- **kube-prometheus-stack**: Prometheus Operator를 통한 `ServiceMonitor` / `PodMonitor` CRD가 primary scrape path.
- **Prometheus annotation fallback**: `prometheus.io/scrape: "true"` 등은 Operator가 없을 때만 사용.
- **OpenTelemetry**: OTLP protocol + OTel Collector (Deployment gateway + DaemonSet agent) 가 표준.
- **Log shipping canonical stacks**: Loki + Grafana Alloy (또는 Promtail) / Fluent Bit → OpenSearch. 한 플랫폼에서 둘 이상 섞지 않는다.
- `kubectl events` (1.27+ stable) — 기존 `kubectl get events`보다 sort/watch 기본 제공.
- metrics-server: HPA/VPA와 `kubectl top` 을 위한 최소 resource metric. full metrics와 분리.
## 기본 규칙
### 1. Four Golden Signals를 모든 서비스 대시보드의 골격으로
각 traffic-facing service는 최소 4개 signal을 노출한다.
- **Latency**: `request_duration_seconds` histogram (p50/p95/p99).
- **Traffic**: `requests_per_second` by method/status.
- **Errors**: `error_rate` (5xx / 전체).
- **Saturation**: resource utilization (CPU / memory / connection pool / queue depth).
SLO / alert / dashboard가 이 4개에서 시작한다.
### 2. RED는 request-driven, USE는 resource에 쓴다
- HTTP / gRPC 서비스 → **RED**.
- Node / disk / CPU / DB pool → **USE**.
- 두 방법론을 동시에 활용 가능 (golden signal은 양쪽 합집합).
### 3. ServiceMonitor / PodMonitor 를 primary scrape path로
kube-prometheus-stack을 운영하는 플랫폼에서는 `ServiceMonitor` CRD가 표준이다.
- `selector.matchLabels` 로 대상 Service 매칭.
- `namespaceSelector` 명시 (암묵적 전체 허용 금지).
- `endpoints[].port`**named port**, 숫자 port 금지.
- `interval` (기본 30s), `scrapeTimeout` (interval < interval) 명시.
- `scheme` (http/https) 명시.
- `bearerTokenSecret` / `tlsConfig` 로 인증 scrape.
- `relabelings` 로 label 위생 (pod_template_hash drop 등).
Pod에 직접 연결되는 경우 (Service가 없는 워크로드) `PodMonitor` 사용.
### 4. Annotation-based scrape 는 fallback
`prometheus.io/scrape: "true"` 계열 annotation은 Prometheus가 Operator 없이 kubernetes_sd_configs로 직접 discover하는 방식이다. ServiceMonitor 대비 label relabel / auth / tls 제어가 약하다.
- kube-prometheus-stack이 있는 환경: **사용 금지**, ServiceMonitor 통일.
- legacy / 교체 진행 중인 플랫폼: 전환 기간 동안만 사용.
지원 annotation:
- `prometheus.io/scrape: "true"`
- `prometheus.io/port: "8081"`
- `prometheus.io/path: "/metrics"`
- `prometheus.io/scheme: "http"`
### 5. metrics port는 외부 비공개, NetworkPolicy로 scraper만 허용
- `/metrics` 는 절대 Ingress 경로에 노출하지 않는다.
- metrics port는 별도 containerPort (ex: 8081, 9000).
- NetworkPolicy로 **monitoring namespace의 prometheus pod만** 해당 port에 ingress 허용.
### 6. Cardinality는 label 설계 단계에서 가드
Prometheus TSDB에서 **각 label value 조합 = 새 time series**. cardinality 폭발은 쿼리 OOM / storage 폭증의 가장 흔한 원인.
금지 label:
- `user_id`, `tenant_id` (높은 기수) — 대신 top-N aggregation 또는 별도 logging.
- `path` (path에 UUID / numeric ID 포함) — template된 route로 바꾼다 (`/users/:id`).
- `url` 전체, `request_id`, `trace_id`, `session_id`.
- timestamp, epoch value.
허용 label 예:
- `method` (GET/POST/…), `status_code` (bucketed 2xx/4xx/5xx가 더 안전), `route` (template).
규칙: **한 metric당 series 수 ≤ 10,000** 목표. 10만 넘어가면 review.
### 7. Histogram 을 p99 표현 기본값으로
- summary는 aggregatable 하지 않다 (서비스 간 p99 합산 불가).
- `histogram_quantile()` 를 위한 `_bucket` + `_count` + `_sum` 를 쓴다.
- bucket boundary는 SLO에 맞춰 튜닝 (`le: 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10`).
### 8. 로그는 JSON structured, stdout/stderr 로만
application log는 **JSON one-line per record**, stdout/stderr로 출력. PVC / hostPath / 컨테이너 내부 file 금지.
필수 field:
- `timestamp` (ISO 8601 RFC3339, UTC).
- `level` (`DEBUG`/`INFO`/`WARN`/`ERROR`).
- `service` (= `app.kubernetes.io/name`).
- `trace_id`, `span_id` (OTel에서 주입).
- `message`.
- `error` (object with `type`, `message`, `stacktrace` when level=ERROR).
- optional: `user_id` (hashed), `request_id`, `http_status`.
### 9. 로그 수집 stack은 한 플랫폼당 하나
canonical choice:
- **Loki + Grafana Alloy (권장)**: 낮은 storage cost, Grafana 통합.
- **Fluent Bit → OpenSearch/Elasticsearch**: full-text search 중심, 높은 storage cost.
플랫폼 하나에서 둘 다 운영하지 않는다. AI agent가 매니페스트 생성할 때 플랫폼 선택을 context에서 받아 일관되게 적용한다.
node-level: DaemonSet으로 agent 배포 → tail `/var/log/containers/*.log`.
### 10. 민감정보는 로그 금지 + 자동 masking
금지:
- access/refresh token, bearer, API key.
- DB password, connection string의 password 부분.
- Vault secret value.
- full Authorization header.
- PII (email, phone, SSN 등) 원문.
구현:
- logging framework의 structured field 에서만 쓰고 `toString()` 흐름 차단.
- 중앙 수집 파이프라인에 redaction filter 추가.
- 의심스러운 pattern은 debug 로그에서도 masking.
### 11. OpenTelemetry / OTLP를 trace / metrics 통로로
- 애플리케이션: OTel SDK로 계측, OTLP (gRPC 4317 또는 HTTP 4318) 로 export.
- 수집: **OTel Collector DaemonSet (agent)****OTel Collector Deployment (gateway)** → backend (Tempo / Jaeger / New Relic / Datadog).
- gateway에서 sampling / tail-based sampling / PII scrubbing 적용.
- app은 cluster 내부 agent endpoint만 알면 됨 (localhost:4317 → DaemonSet).
### 12. health / metrics / admin endpoint 는 외부 비공개 기본값
외부 비공개 대상:
- `/health`, `/health/*`, `/actuator/*`.
- `/metrics`.
- `/admin`, `/internal`, `/debug`.
- Keycloak management port 9000.
- Vault `/sys/*` endpoint.
외부 공개는 명시적 review 필요.
### 13. probe는 health endpoint 와 목적을 구분
- probe용 endpoint는 shallow, 빠른 응답.
- 운영자 점검용 deep health는 별도 endpoint (ex: `/ops/deep-health`), 인증 필요.
- Prometheus 가 `/metrics` 를 스크레이프하더라도 probe가 `/metrics` 를 쓰지 않는다 (cost 문제).
### 14. `kubectl events` 를 기본 event 조회 수단으로 (1.27+)
Kubernetes 1.27+ 부터 `kubectl events` 가 stable.
- `kubectl events -A --watch` — cluster-wide live view.
- `kubectl events -n <ns> --for pod/<name>` — 특정 오브젝트.
- `kubectl events --types=Warning` — 경고만.
`kubectl get events` 대비 sort-by-timestamp 기본, watch 안정적.
### 15. 알림 기준: Golden Signal 에 SLO 를 먼저 정의
- availability SLO: 99.9% / 99.95% 등.
- latency SLO: p99 < 500ms.
- error budget: (1 - SLO) × 기간.
- alert는 **burn rate** 기준 (1h/6h fast burn + 6h/3d slow burn 이중 창).
단순 "CPU > 80%" alert 는 actionable 하지 않다 (saturation은 dashboard용, 알림은 SLO 기반).
### 16. 워크로드별 기본 권장안
#### auth-server (Spring Boot)
- metrics: micrometer + prometheus registry, `/actuator/prometheus`.
- ServiceMonitor with named port `metrics` (8081).
- tracing: OTel Java agent, OTLP to DaemonSet.
- logging: logback JSON encoder → stdout.
#### keycloak
- metrics: management port 9000 `/metrics`.
- ServiceMonitor 대상, `/admin``9000` 외부 비공개.
- event metric cardinality는 `event_type` level 까지만, user / session ID 금지.
#### vault
- `/sys/metrics?format=prometheus` (token 필요) → ServiceMonitor with `bearerTokenSecret`.
- `/sys/health` 는 sealed/standby 구분해서 alert 룰 따로.
#### minio
- `/minio/v2/metrics/cluster` + `/node` + `/bucket`.
- bucket metric은 bucket 수 폭증 시 cardinality 주의.
#### db (PostgreSQL / MySQL)
- postgres_exporter / mysqld_exporter sidecar 또는 별도 Deployment.
- USE method (connection pool saturation, lock wait).
#### ingress-controller
- RED + upstream response time.
- path label은 반드시 template 화.
## 프로젝트 기준 요약
- Four Golden Signals를 dashboard 골격으로, RED/USE를 세부 방법론으로.
- ServiceMonitor / PodMonitor 를 primary scrape, annotation은 fallback.
- metrics port는 NetworkPolicy로 monitoring namespace만 허용.
- Cardinality는 label 설계에서 가드 (user_id / raw path / timestamp 금지).
- 로그는 JSON structured stdout, trace_id/span_id 포함.
- log shipping stack은 플랫폼당 하나 (Loki+Alloy 또는 Fluent Bit→OpenSearch).
- 로그에 민감정보 금지, 중앙 파이프라인 redaction.
- OpenTelemetry DaemonSet agent + Deployment gateway.
- health / metrics / admin endpoint 외부 비공개.
- `kubectl events` 를 기본 event 조회 수단으로 (1.27+).
- alert는 SLO burn rate 기반, CPU% 같은 단순 threshold 금지.