🔧 DevOps

Grafana

그라파나

오픈소스 시각화 플랫폼. 메트릭, 로그 대시보드. Prometheus와 함께 사용.

상세 설명

Grafana는 메트릭, 로그, 트레이스 데이터를 시각화하는 오픈소스 플랫폼입니다. 2014년 Torkel Odegaard가 시작한 프로젝트로, 현재 전 세계적으로 가장 인기 있는 모니터링 시각화 도구입니다. 다양한 데이터 소스를 지원하며, 특히 Prometheus와 함께 클라우드 네이티브 모니터링 스택의 핵심을 이룹니다.

핵심 기능

  • 대시보드: 드래그앤드롭으로 패널 구성, JSON으로 내보내기/가져오기
  • 다중 데이터 소스: Prometheus, Loki, InfluxDB, Elasticsearch, CloudWatch 등 지원
  • 알림(Alerting): 임계값 기반 알림, Slack/PagerDuty/이메일 연동
  • Explore: 즉석 쿼리로 데이터 탐색
  • 플러그인: 패널, 데이터 소스, 앱 확장
  • 팀/권한: 조직, 팀, 폴더 기반 접근 제어

Grafana 스택

  • Grafana: 시각화 및 대시보드
  • Prometheus: 메트릭 수집 및 저장
  • Loki: 로그 집계 시스템
  • Tempo: 분산 트레이싱 백엔드
  • Mimir: 장기 메트릭 저장소

코드 예제

Dashboard JSON 예제

{
  "dashboard": {
    "id": null,
    "uid": "api-dashboard",
    "title": "API Service Dashboard",
    "tags": ["api", "production"],
    "timezone": "browser",
    "refresh": "30s",
    "time": {
      "from": "now-1h",
      "to": "now"
    },
    "panels": [
      {
        "id": 1,
        "title": "Request Rate",
        "type": "timeseries",
        "gridPos": { "x": 0, "y": 0, "w": 12, "h": 8 },
        "datasource": { "type": "prometheus", "uid": "prometheus" },
        "targets": [
          {
            "expr": "sum(rate(http_requests_total{job=\"api\"}[5m])) by (status_code)",
            "legendFormat": "{{status_code}}"
          }
        ],
        "fieldConfig": {
          "defaults": {
            "unit": "reqps",
            "custom": {
              "drawStyle": "line",
              "lineWidth": 2,
              "fillOpacity": 10
            }
          }
        }
      },
      {
        "id": 2,
        "title": "Response Time (p95)",
        "type": "stat",
        "gridPos": { "x": 12, "y": 0, "w": 6, "h": 4 },
        "datasource": { "type": "prometheus", "uid": "prometheus" },
        "targets": [
          {
            "expr": "histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket{job=\"api\"}[5m])) by (le))",
            "legendFormat": "p95"
          }
        ],
        "fieldConfig": {
          "defaults": {
            "unit": "s",
            "thresholds": {
              "mode": "absolute",
              "steps": [
                { "value": 0, "color": "green" },
                { "value": 0.5, "color": "yellow" },
                { "value": 1, "color": "red" }
              ]
            }
          }
        }
      },
      {
        "id": 3,
        "title": "Error Rate",
        "type": "gauge",
        "gridPos": { "x": 18, "y": 0, "w": 6, "h": 4 },
        "targets": [
          {
            "expr": "sum(rate(http_requests_total{job=\"api\",status_code=~\"5..\"}[5m])) / sum(rate(http_requests_total{job=\"api\"}[5m])) * 100",
            "legendFormat": "Error %"
          }
        ],
        "fieldConfig": {
          "defaults": {
            "unit": "percent",
            "min": 0,
            "max": 100,
            "thresholds": {
              "steps": [
                { "value": 0, "color": "green" },
                { "value": 1, "color": "yellow" },
                { "value": 5, "color": "red" }
              ]
            }
          }
        }
      }
    ]
  }
}

Grafana Provisioning 설정

# /etc/grafana/provisioning/datasources/prometheus.yaml
apiVersion: 1

datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    url: http://prometheus:9090
    isDefault: true
    editable: false
    jsonData:
      timeInterval: "15s"

  - name: Loki
    type: loki
    access: proxy
    url: http://loki:3100
    editable: false
    jsonData:
      derivedFields:
        - datasourceUid: tempo
          matcherRegex: "traceID=(\\w+)"
          name: TraceID
          url: "$${__value.raw}"

  - name: Tempo
    type: tempo
    access: proxy
    url: http://tempo:3200
    editable: false
---
# /etc/grafana/provisioning/dashboards/dashboards.yaml
apiVersion: 1

providers:
  - name: 'default'
    orgId: 1
    folder: 'Production'
    type: file
    disableDeletion: false
    editable: true
    options:
      path: /var/lib/grafana/dashboards

Kubernetes에서 Grafana 배포

# grafana-values.yaml (Helm)
replicas: 1

persistence:
  enabled: true
  size: 10Gi

adminUser: admin
adminPassword: ${GRAFANA_ADMIN_PASSWORD}

datasources:
  datasources.yaml:
    apiVersion: 1
    datasources:
      - name: Prometheus
        type: prometheus
        url: http://prometheus-server.monitoring.svc:9090
        isDefault: true

dashboardProviders:
  dashboardproviders.yaml:
    apiVersion: 1
    providers:
      - name: 'default'
        orgId: 1
        folder: ''
        type: file
        options:
          path: /var/lib/grafana/dashboards

dashboards:
  default:
    kubernetes-cluster:
      gnetId: 6417
      revision: 1
      datasource: Prometheus
    node-exporter:
      gnetId: 1860
      revision: 31
      datasource: Prometheus

ingress:
  enabled: true
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: letsencrypt
  hosts:
    - grafana.example.com
  tls:
    - secretName: grafana-tls
      hosts:
        - grafana.example.com

실무 대화 예제

SRE 엔지니어
"Prometheus에서 메트릭은 수집되는데, 시각화가 힘들어요. 쿼리 결과를 그래프로 보고 싶은데..."
시니어 SRE
"Grafana를 연동하면 돼요. Prometheus를 데이터 소스로 추가하고, 대시보드를 만들어서 PromQL 쿼리 결과를 시각화할 수 있어요. 커뮤니티에 공유된 대시보드도 수천 개가 있으니 import해서 바로 쓸 수도 있고요."
SRE 엔지니어
"알림도 Grafana에서 설정할 수 있나요?"
시니어 SRE
"네, Grafana Alerting으로 가능해요. 패널에 알림 조건을 설정하고, Slack이나 PagerDuty로 알림을 보낼 수 있죠. 다만 복잡한 알림은 Prometheus Alertmanager를 쓰는 게 더 유연해요."

주의사항

대시보드 버전 관리

대시보드를 UI에서만 수정하면 변경 이력이 남지 않습니다. JSON을 Git에 저장하고 provisioning으로 배포하세요.

쿼리 성능

무거운 PromQL 쿼리가 많은 대시보드는 로딩이 느립니다. Recording Rules로 자주 쓰는 쿼리를 미리 계산해두세요.

인증/권한

기본 설정에서는 익명 접근이 가능합니다. 프로덕션에서는 LDAP, OAuth, SAML 등 인증을 필수로 설정하세요.

Persistence

Grafana 데이터베이스(SQLite 또는 외부 DB)를 영구 볼륨에 저장하지 않으면 재시작 시 대시보드가 사라집니다.

더 배우기