[k8s] Monitoring 체계 구축 — Prometheus, ServiceMonitor, Grafana

Woong·2026년 4월 15일

Docker, k8s

목록 보기
37/38

개요

  • EKS 에서 서빙 앱의 메트릭을 수집하고 Grafana 대시보드로 모니터링하는 구조를 정리
    • 앱에서 Prometheus 메트릭 노출 → ServiceMonitor 로 수집 → Grafana 에서 시각화
    • kubectl 로그 조회 실전 명령어

메트릭 수집 흐름

App (/metrics) → Prometheus (ServiceMonitor 로 scrape) → Grafana (PromQL 쿼리)
  • 앱에서 /metrics 엔드포인트를 노출
  • ServiceMonitor 가 Prometheus 에 scrape 대상 등록
  • Grafana 에서 PromQL 로 대시보드 구성

앱에서 메트릭 노출 (Python)

prometheus_client 설치
pip install prometheus_client
메트릭 클래스 작성
  • Counter (호출 수) 와 Histogram (레이턴시) 두 가지 기본 메트릭
# metrics.py
import time
from prometheus_client import Counter, Histogram


class RequestMetrics:
    def __init__(self, namespace: str = "app"):
        self.request_total = Counter(
            name=f"{namespace}_requests_total",
            documentation="Total number of requests",
            labelnames=["api_name", "status"],
        )
        self.request_latency = Histogram(
            name=f"{namespace}_request_latency_seconds",
            documentation="Request latency in seconds",
            labelnames=["api_name"],
            buckets=[0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0],
        )

    def start_timer(self) -> float:
        return time.time()

    def observe_request(self, api_name: str, start_time: float, status: str = "success"):
        elapsed = time.time() - start_time
        self.request_latency.labels(api_name=api_name).observe(elapsed)
        self.request_total.labels(api_name=api_name, status=status).inc()

    def observe_error(self, api_name: str, start_time: float):
        self.observe_request(api_name=api_name, start_time=start_time, status="error")
/metrics 엔드포인트 등록 (FastAPI)
from fastapi import FastAPI
from prometheus_client import make_asgi_app
from metrics import RequestMetrics

app = FastAPI()
metrics = RequestMetrics(namespace="app")

# Prometheus 가 scrape 할 엔드포인트
metrics_app = make_asgi_app()
app.mount("/metrics", metrics_app)
사용 예시
@app.post("/search")
async def search(query: str):
    start = metrics.start_timer()
    try:
        result = await do_search(query)
        metrics.observe_request("search", start, "success")
        return result
    except Exception as e:
        metrics.observe_error("search", start)
        raise e

ServiceMonitor 등록

  • Prometheus 가 앱의 /metrics 를 수집하도록 ServiceMonitor 리소스 생성
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: <app_name>
  namespace: monitoring
  labels:
    release: prometheus-stack    # Prometheus Helm release 이름과 매칭
spec:
  namespaceSelector:
    matchNames:
      - <app_namespace>
  selector:
    matchLabels:
      app: <app_name>-svc
  endpoints:
    - port: http
      path: /metrics
      interval: 30s
  • release: prometheus-stack : Prometheus 가 이 ServiceMonitor 를 인식하기 위한 label
    • kube-prometheus-stack Helm release 이름과 매칭
  • deployment repo 의 kustomization.yaml patches 에 추가하여 관리

Grafana 대시보드

  • Import Dashboard 로 JSON 을 붙여넣어 생성
  • 기본 패널 구성
패널PromQL단위
Total QPSsum(rate(app_requests_total{service="<svc>"}[5m]))req/s
Requests/sec by APIsum(rate(app_requests_total{service="<svc>"}[5m])) by (api_name)req/s
Error Rate (%)100 * sum(rate(..{status="error"}[5m])) by (api_name) / sum(rate(..[5m])) by (api_name)percent
Latency p50/p90/p99histogram_quantile(0.9, sum(rate(..bucket[5m])) by (le))seconds
p90 by APIhistogram_quantile(0.9, sum(rate(..bucket[5m])) by (le, api_name))seconds
  • ex) Latency p90 PromQL
histogram_quantile(0.9,
  sum(rate(app_request_latency_seconds_bucket{service="<app_name>-svc"}[5m])) by (le)
)

PushGateway (배치 메트릭)

  • pull 방식이 어려운 배치 작업에서 push 로 메트릭 전송
from prometheus_client import CollectorRegistry, Counter, push_to_gateway

registry = CollectorRegistry()
counter = Counter(
    "batch_processed_total",
    "Total processed items",
    registry=registry,
)

# 작업 수행
counter.inc(100)

# PushGateway 로 전송
push_to_gateway(
    gateway="<pushgateway_endpoint>:9091",
    job="batch_job_name",
    registry=registry,
)

kubectl 로그 조회

기본 조회
# Pod 목록
kubectl get pods -n <namespace>

# 로그 조회
kubectl logs <pod_name> -n <namespace>

# 실시간 스트리밍
kubectl logs -f <pod_name> -n <namespace>

# label 로 조회
kubectl logs -l app=<app_name> -n <namespace>
유용한 옵션
# 최근 100줄
kubectl logs <pod_name> -n <namespace> --tail=100

# 최근 1시간
kubectl logs <pod_name> -n <namespace> --since=1h

# 이전 컨테이너 로그 (재시작된 경우)
kubectl logs <pod_name> -n <namespace> -p

# 타임스탬프 포함
kubectl logs <pod_name> -n <namespace> --timestamps=true

# 멀티 컨테이너 Pod — 특정 컨테이너 지정
kubectl logs <pod_name> -n <namespace> -c <container_name>
에러 필터링
kubectl logs <pod_name> -n <namespace> --since=1h | grep -i error
트러블슈팅 순서
  1. Pod 상태 확인: kubectl get pods -n <namespace>
  2. Pod 이벤트 확인: kubectl describe pod <pod_name> -n <namespace>
  3. 현재 로그 확인: kubectl logs <pod_name> --tail=100
  4. 이전 로그 확인 (재시작): kubectl logs <pod_name> -p
  • Pod 삭제 시 로그도 삭제됨 → 장기 보관 필요 시 Fluent-bit → CloudWatch Logs 사용

reference

0개의 댓글