본문으로 건너뛰기
Previous
Next
필수 기술 스택 | Kibana·Prometheus·Redis·Grafana·ELK 완벽 가이드

필수 기술 스택 | Kibana·Prometheus·Redis·Grafana·ELK 완벽 가이드

필수 기술 스택 | Kibana·Prometheus·Redis·Grafana·ELK 완벽 가이드

이 글의 핵심

현대 개발자가 반드시 알아야 할 오픈소스 기술 스택. Kibana 로그 분석, Prometheus 모니터링, Redis 캐싱, Grafana 대시보드, ELK Stack 구축까지 실전 예제로 마스터하세요.

들어가며: 현대 기술 스택의 필수 요소

2026년 현재, 성공적인 서비스 운영을 위해서는 모니터링, 로그 분석, 캐싱, 메트릭 수집이 필수입니다. 이 글에서는 각 영역의 대표 오픈소스 도구들을 실전 예제와 함께 소개합니다. 다룰 기술 스택:

  • Redis: 인메모리 데이터 저장소 및 캐시
  • Elasticsearch: 분산 검색 및 분석 엔진
  • Kibana: 로그 시각화 및 분석
  • Prometheus: 메트릭 수집 및 모니터링
  • Grafana: 메트릭 대시보드
  • Jaeger: 분산 추적 (Tracing)

실전 경험에서 배운 교훈

이 기술을 실무 프로젝트에 처음 도입했을 때, 공식 문서만으로는 알 수 없었던 많은 함정들이 있었습니다. 특히 프로덕션 환경에서 발생하는 엣지 케이스들은 로컬 개발 환경에서는 재현조차 되지 않았죠.

가장 어려웠던 점은 성능 최적화였습니다. 처음엔 “동작만 하면 되겠지”라고 생각했지만, 실제 사용자 트래픽이 몰리면서 병목 지점들이 하나씩 드러났습니다. 특히 데이터베이스 쿼리 최적화, 캐싱 전략, 에러 핸들링 구조 등은 여러 번의 장애를 겪으면서 개선해 나갔습니다.

이 글에서는 그런 시행착오를 통해 얻은 실전 노하우와, “이렇게 하면 안 된다”는 교훈들을 함께 정리했습니다. 특히 트러블슈팅 섹션은 실제 장애 대응 경험을 바탕으로 작성했으니, 비슷한 문제를 마주했을 때 참고하시면 도움이 될 것입니다.

1. Redis: 초고속 인메모리 데이터베이스

Redis란?

Redis(Remote Dictionary Server)는 인메모리 키-값 저장소로, 캐싱, 세션 관리, 실시간 분석에 사용됩니다.

주요 특징

  • 속도: 마이크로초 단위 응답 시간
  • 자료구조: String, List, Set, Hash, Sorted Set, Stream
  • 영속성: RDB, AOF 스냅샷
  • 클러스터링: 샤딩 및 복제 지원

Redis 아키텍처

flowchart TB
    subgraph App[Application Servers]
        A1[App 1]
        A2[App 2]
        A3[App 3]
    end
    
    subgraph Redis_Cluster[Redis Cluster]
        M1["Master 1\nSlots: 0-5460"]
        M2["Master 2\nSlots: 5461-10922"]
        M3["Master 3\nSlots: 10923-16383"]
        
        S1[Replica 1]
        S2[Replica 2]
        S3[Replica 3]
        
        M1 --> S1
        M2 --> S2
        M3 --> S3
    end
    
    A1 --> M1
    A2 --> M2
    A3 --> M3

실전 사용 예시

1. 캐싱

import redis
import json
r = redis.Redis(host='localhost', port=6379, decode_responses=True)
# 캐시 읽기
def get_user(user_id):
    # 캐시 확인
    cached = r.get(f"user:{user_id}")
    if cached:
        return json.loads(cached)
    
    # DB에서 조회
    user = db.query("SELECT * FROM users WHERE id = ?", user_id)
    
    # 캐시 저장 (10분)
    r.setex(f"user:{user_id}", 600, json.dumps(user))
    return user

2. 세션 관리

# 세션 저장
def create_session(user_id, session_data):
    session_id = generate_session_id()
    r.setex(
        f"session:{session_id}",
        3600,  # 1시간
        json.dumps({"user_id": user_id, **session_data})
    )
    return session_id
# 세션 조회
def get_session(session_id):
    data = r.get(f"session:{session_id}")
    return json.loads(data) if data else None

3. Rate Limiting

def check_rate_limit(user_id, limit=100, window=60):
    key = f"rate_limit:{user_id}"
    current = r.incr(key)
    
    if current == 1:
        r.expire(key, window)
    
    return current <= limit

4. 실시간 리더보드

# 점수 추가
r.zadd("leaderboard", {"user1": 1000, "user2": 1500})
# 상위 10명 조회
top10 = r.zrevrange("leaderboard", 0, 9, withscores=True)
for user, score in top10:
    print(f"{user}: {score}")

Redis 성능 팁

# ✅ Pipeline으로 네트워크 왕복 최소화
pipe = r.pipeline()
for i in range(1000):
    pipe.set(f"key:{i}", f"value:{i}")
pipe.execute()
# ✅ Lua 스크립트로 원자적 연산
lua_script = """
local current = redis.call('GET', KEYS[1])
if tonumber(current) < tonumber(ARGV[1]) then
    return redis.call('SET', KEYS[1], ARGV[1])
end
return 0
"""
r.eval(lua_script, 1, "counter", 100)

일상 비유로 이해하기: 메모리를 아파트 건물로 생각해보세요. 스택은 엘리베이터 같아서 빠르지만 공간이 제한적입니다. 힙은 창고처럼 넓지만 물건을 찾는 데 시간이 걸립니다. 포인터는 “3층 302호”처럼 주소를 가리키는 메모지라고 보면 됩니다.

2. ELK Stack: 로그 수집과 분석

ELK Stack이란?

ELK는 Elasticsearch, Logstash, Kibana의 조합으로, 로그 수집·저장·분석·시각화를 위한 통합 솔루션입니다.

flowchart LR
    subgraph Sources[로그 소스]
        App[Application]
        Web[Web Server]
        DB[Database]
    end
    
    subgraph Logstash[Logstash]
        Input[Input]
        Filter[Filter]
        Output[Output]
        Input --> Filter --> Output
    end
    
    subgraph Elasticsearch[Elasticsearch Cluster]
        ES1[Node 1]
        ES2[Node 2]
        ES3[Node 3]
    end
    
    subgraph Kibana[Kibana]
        Dashboard[Dashboard]
        Discover[Discover]
        Visualize[Visualize]
    end
    
    App --> Logstash
    Web --> Logstash
    DB --> Logstash
    
    Logstash --> ES1
    ES1 <--> ES2
    ES2 <--> ES3
    
    ES1 --> Kibana

Elasticsearch

분산 검색 및 분석 엔진으로, JSON 문서를 인덱싱하고 빠르게 검색합니다.

인덱스 생성 및 문서 추가

# 인덱스 생성
curl -X PUT "localhost:9200/logs" -H 'Content-Type: application/json' -d'
{
  "mappings": {
    "properties": {
      "timestamp": {"type": "date"},
      "level": {"type": "keyword"},
      "message": {"type": "text"},
      "user_id": {"type": "keyword"}
    }
  }
}'
# 문서 추가
curl -X POST "localhost:9200/logs/_doc" -H 'Content-Type: application/json' -d'
{
  "timestamp": "2026-04-01T10:00:00Z",
  "level": "ERROR",
  "message": "Database connection failed",
  "user_id": "user123"
}'

검색 쿼리

from elasticsearch import Elasticsearch
es = Elasticsearch(['http://localhost:9200'])
# 에러 로그 검색
response = es.search(index="logs", body={
    "query": {
        "bool": {
            "must": [
                {"match": {"level": "ERROR"}},
                {"range": {"timestamp": {"gte": "now-1h"}}}
            ]
        }
    },
    "sort": [{"timestamp": {"order": "desc"}}],
    "size": 100
})
for hit in response['hits']['hits']:
    print(hit['_source'])

Logstash

데이터 수집 및 변환 파이프라인입니다.

Logstash 설정 예시

# logstash.conf
input {
  file {
    path => "/var/log/app/*.log"
    start_position => "beginning"
  }
  
  tcp {
    port => 5000
    codec => json
  }
}
filter {
  # JSON 파싱
  json {
    source => "message"
  }
  
  # 날짜 파싱
  date {
    match => ["timestamp", "ISO8601"]
  }
  
  # 필드 추가
  mutate {
    add_field => { "environment" => "production" }
  }
  
  # Grok 패턴으로 로그 파싱
  grok {
    match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:level} %{GREEDYDATA:msg}" }
  }
}
output {
  elasticsearch {
    hosts => [localhost:9200]
    index => "logs-%{+YYYY.MM.dd}"
  }
  
  stdout {
    codec => rubydebug
  }
}

Kibana

Elasticsearch 데이터 시각화 도구입니다.

주요 기능

  1. Discover: 로그 검색 및 필터링
  2. Visualize: 차트, 그래프 생성
  3. Dashboard: 여러 시각화를 하나의 대시보드로
  4. Alerting: 조건 기반 알림

Kibana 쿼리 예시 (KQL)

# 에러 로그 검색
level: 중급
# 특정 사용자의 로그
user_id: "user123" AND status: 500
# 느린 API 요청
response_time > 1000 AND endpoint: "/api/*"

3. Prometheus: 메트릭 기반 모니터링

Prometheus란?

Prometheus는 시계열 데이터베이스 기반의 모니터링 및 알림 시스템입니다. Pull 모델로 메트릭을 수집합니다.

Prometheus 아키텍처

flowchart TB
    subgraph Targets[모니터링 대상]
        App1["App Server 1\n/metrics"]
        App2["App Server 2\n/metrics"]
        Node1[Node Exporter]
        DB[Database Exporter]
    end
    
    subgraph Prometheus[Prometheus Server]
        Scraper[Scraper]
        TSDB[Time Series DB]
        Rules[Alert Rules]
        
        Scraper --> TSDB
        TSDB --> Rules
    end
    
    subgraph Alerting[Alerting]
        AM[Alertmanager]
        Email[Email]
        Slack[Slack]
        PagerDuty[PagerDuty]
    end
    
    subgraph Visualization[시각화]
        Grafana[Grafana]
        PromUI[Prometheus UI]
    end
    
    App1 --> Scraper
    App2 --> Scraper
    Node1 --> Scraper
    DB --> Scraper
    
    Rules --> AM
    AM --> Email
    AM --> Slack
    AM --> PagerDuty
    
    TSDB --> Grafana
    TSDB --> PromUI

메트릭 타입

1. Counter (카운터)

증가만 하는 값 (요청 수, 에러 수)

from prometheus_client import Counter
request_count = Counter('http_requests_total', 'Total HTTP requests', ['method', 'endpoint'])
@app.route('/api/users')
def get_users():
    request_count.labels(method='GET', endpoint='/api/users').inc()
    return users

2. Gauge (게이지)

증가/감소하는 값 (CPU 사용률, 메모리)

from prometheus_client import Gauge
memory_usage = Gauge('memory_usage_bytes', 'Memory usage in bytes')
def update_metrics():
    import psutil
    memory_usage.set(psutil.virtual_memory().used)

3. Histogram (히스토그램)

값의 분포 (응답 시간, 요청 크기)

from prometheus_client import Histogram
response_time = Histogram('http_response_time_seconds', 'HTTP response time')
@app.route('/api/data')
@response_time.time()
def get_data():
    # 자동으로 응답 시간 측정
    return process_data()

4. Summary (요약)

분위수 계산 (P50, P95, P99)

from prometheus_client import Summary
latency = Summary('request_latency_seconds', 'Request latency')
@latency.time()
def process_request():
    # 처리
    pass

Prometheus 설정

# prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s
scrape_configs:
  - job_name: 'node'
    static_configs:
      - targets: ['localhost:9100']
  
  - job_name: 'app'
    static_configs:
      - targets: ['localhost:8080']
    metrics_path: '/metrics'
  
  - job_name: 'kubernetes'
    kubernetes_sd_configs:
      - role: pod

PromQL 쿼리 예시

# CPU 사용률 (5분 평균)
rate(cpu_usage_seconds_total[5m])
# 에러율 (지난 1시간)
sum(rate(http_requests_total{status=~"5.."}[1h])) / 
sum(rate(http_requests_total[1h]))
# P95 응답 시간
histogram_quantile(0.95, 
  rate(http_response_time_seconds_bucket[5m]))
# 메모리 사용률
(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / 
node_memory_MemTotal_bytes * 100

Alert Rules

# alert.rules.yml
groups:
  - name: example
    rules:
      - alert: HighErrorRate
        expr: |
          sum(rate(http_requests_total{status=~"5.."}[5m])) /
          sum(rate(http_requests_total[5m])) > 0.05
        for: 10m
        labels:
          severity: critical
        annotations:
          summary: "High error rate detected"
          description: 'Error rate is {{ $value }}%. 필수 기술 스택에 대한 완전한 가이드입니다. 실전 예제와 함께 핵심 개념부터 고급 활용까지 다룹니다.'
      
      - alert: HighMemoryUsage
        expr: node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes < 0.1
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "High memory usage on {{ $labels.instance }}"

4. Grafana: 통합 대시보드

Grafana란?

Grafana는 메트릭, 로그, 트레이스를 시각화하는 오픈소스 플랫폼입니다. Prometheus, Elasticsearch, MySQL 등 다양한 데이터 소스를 지원합니다.

주요 기능

  1. 대시보드: 여러 패널을 조합한 시각화
  2. 알림: 임계값 기반 알림
  3. 템플릿: 변수를 사용한 동적 대시보드
  4. 플러그인: 다양한 데이터 소스 및 패널

Grafana 대시보드 예시

{
  "dashboard": {
    "title": "Application Monitoring",
    "panels": [
      {
        "title": "Request Rate",
        "targets": [
          {
            "expr": "rate(http_requests_total[5m])",
            "legendFormat": "{{method}} {{endpoint}}"
          }
        ],
        "type": "graph"
      },
      {
        "title": "Error Rate",
        "targets": [
          {
            "expr": "sum(rate(http_requests_total{status=~\"5..\"}[5m]))"
          }
        ],
        "type": "stat",
        "thresholds": [
          {"value": 0, "color": "green"},
          {"value": 0.01, "color": "yellow"},
          {"value": 0.05, "color": "red"}
        ]
      },
      {
        "title": "Response Time (P95)",
        "targets": [
          {
            "expr": "histogram_quantile(0.95, rate(http_response_time_seconds_bucket[5m]))"
          }
        ],
        "type": "graph"
      }
    ]
  }
}

Grafana 알림 설정

# Grafana Alert
apiVersion: 1
groups:
  - name: app-alerts
    interval: 1m
    rules:
      - uid: high-error-rate
        title: High Error Rate
        condition: A
        data:
          - refId: A
            queryType: '
            relativeTimeRange:
              from: 600
              to: 0
            datasourceUid: prometheus-uid
            model:
              expr: 'rate(http_errors_total[5m]) > 10'
        noDataState: NoData
        execErrState: Alerting
        for: 5m
        annotations:
          summary: 'Error rate is too high'
        labels:
          severity: critical

5. Jaeger: 분산 추적

Jaeger란?

Jaeger는 마이크로서비스 환경에서 요청의 전체 흐름을 추적하는 분산 추적 시스템입니다.

Jaeger 아키텍처

flowchart TB
    subgraph Services[Microservices]
        S1[API Gateway]
        S2[Auth Service]
        S3[Order Service]
        S4[Payment Service]
    end
    
    subgraph Jaeger[Jaeger]
        Agent[Jaeger Agent]
        Collector[Collector]
        Storage["Storage\nElasticsearch/Cassandra"]
        UI[Jaeger UI]
    end
    
    S1 --> Agent
    S2 --> Agent
    S3 --> Agent
    S4 --> Agent
    
    Agent --> Collector
    Collector --> Storage
    Storage --> UI

OpenTelemetry로 Tracing 구현

from opentelemetry import trace
from opentelemetry.exporter.jaeger.thrift import JaegerExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
# Tracer 설정
trace.set_tracer_provider(TracerProvider())
tracer = trace.get_tracer(__name__)
jaeger_exporter = JaegerExporter(
    agent_host_name="localhost",
    agent_port=6831,
)
trace.get_tracer_provider().add_span_processor(
    BatchSpanProcessor(jaeger_exporter)
)
# Span 생성
@app.route('/api/order')
def create_order():
    with tracer.start_as_current_span("create_order") as span:
        span.set_attribute("user_id", user_id)
        span.set_attribute("order_id", order_id)
        
        # 하위 작업
        with tracer.start_as_current_span("check_inventory"):
            check_inventory(order_id)
        
        with tracer.start_as_current_span("process_payment"):
            process_payment(order_id)
        
        return {"order_id": order_id}

Trace 시각화

Jaeger UI에서 다음을 확인할 수 있습니다:

Trace: create_order (총 500ms)
├── check_inventory (100ms)
│   └── database_query (80ms)
├── process_payment (350ms)
│   ├── validate_card (50ms)
│   ├── charge_card (250ms)
│   └── send_receipt (50ms)
└── update_inventory (50ms)

6. 기술 스택 비교와 선택

종합 비교표

기술목적데이터 타입지연시간저장 방식주요 사용 사례
Redis캐싱, 세션키-값마이크로초인메모리캐시, 세션, Rate Limiting
Elasticsearch검색, 분석JSON 문서밀리초디스크로그 검색, 전문 검색
Prometheus메트릭 수집시계열초 단위디스크CPU, 메모리, 요청 수 모니터링
Grafana시각화---대시보드, 알림
Jaeger분산 추적Span밀리초디스크마이크로서비스 추적

선택 플로우차트

flowchart TD
    Start[무엇을 하고 싶나요?] --> Q1{데이터 타입은?}
    
    Q1 -->|로그| Q2{검색 필요?}
    Q2 -->|Yes| ELK[Elasticsearch + Kibana]
    Q2 -->|No| Loki[Loki + Grafana]
    
    Q1 -->|메트릭| Q3{시계열 데이터?}
    Q3 -->|Yes| Prom[Prometheus + Grafana]
    Q3 -->|No| InfluxDB[InfluxDB]
    
    Q1 -->|캐시/세션| Q4{영속성 필요?}
    Q4 -->|Yes| Redis_AOF[Redis + AOF]
    Q4 -->|No| Redis_Mem[Redis 인메모리]
    
    Q1 -->|분산 추적| Jaeger[Jaeger/Zipkin]

7. 실전 아키텍처

완전한 관측성(Observability) 스택

flowchart TB
    subgraph App[Application Layer]
        API[API Server]
        Worker[Background Worker]
        Service[Microservices]
    end
    
    subgraph Logs[로그 수집]
        Filebeat[Filebeat]
        Logstash[Logstash]
        ES[Elasticsearch]
        Kibana[Kibana]
    end
    
    subgraph Metrics[메트릭 수집]
        Prometheus[Prometheus]
        Grafana[Grafana]
    end
    
    subgraph Traces[분산 추적]
        Jaeger[Jaeger]
    end
    
    subgraph Cache[캐싱]
        Redis[Redis]
    end
    
    API --> Filebeat
    Worker --> Filebeat
    Service --> Filebeat
    Filebeat --> Logstash
    Logstash --> ES
    ES --> Kibana
    
    API --> Prometheus
    Worker --> Prometheus
    Service --> Prometheus
    Prometheus --> Grafana
    
    API --> Jaeger
    Service --> Jaeger
    
    API --> Redis
    Service --> Redis

Docker Compose 예시

version: '3.8'
services:
  # Redis
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data
    command: redis-server --appendonly yes
  
  # Elasticsearch
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.12.0
    environment:
      - discovery.type=single-node
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ports:
      - "9200:9200"
    volumes:
      - es-data:/usr/share/elasticsearch/data
  
  # Kibana
  kibana:
    image: docker.elastic.co/kibana/kibana:8.12.0
    ports:
      - "5601:5601"
    environment:
      - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
    depends_on:
      - elasticsearch
  
  # Prometheus
  prometheus:
    image: prom/prometheus:latest
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus-data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
  
  # Grafana
  grafana:
    image: grafana/grafana:latest
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    volumes:
      - grafana-data:/var/lib/grafana
    depends_on:
      - prometheus
  
  # Jaeger
  jaeger:
    image: jaegertracing/all-in-one:latest
    ports:
      - "5775:5775/udp"
      - "6831:6831/udp"
      - "6832:6832/udp"
      - "5778:5778"
      - "16686:16686"  # UI
      - "14268:14268"
      - "14250:14250"
    environment:
      - COLLECTOR_ZIPKIN_HOST_PORT=:9411
volumes:
  redis-data:
  es-data:
  prometheus-data:
  grafana-data:

8. 베스트 프랙티스

Redis 베스트 프랙티스

1. 적절한 만료 시간 설정

# ✅ 캐시에는 항상 TTL 설정
r.setex("user:123", 600, json.dumps(user_data))  # 10분
# ❌ TTL 없으면 메모리 부족 위험
r.set("user:123", json.dumps(user_data))

2. 키 네이밍 규칙

# ✅ 명확한 네이밍
"user:123:profile"
"session:abc123"
"cache:product:456"
# ❌ 모호한 네이밍
"u123"
"s1"

3. 대용량 데이터 처리

# ✅ SCAN으로 안전하게 순회
cursor = 0
while True:
    cursor, keys = r.scan(cursor, match="user:*", count=100)
    for key in keys:
        process(key)
    if cursor == 0:
        break
# ❌ KEYS는 프로덕션에서 금지 (블로킹)
keys = r.keys("user:*")  # 위험!

Elasticsearch 베스트 프랙티스

1. 인덱스 라이프사이클 관리

{
  "policy": {
    "phases": {
      "hot": {
        "actions": {
          "rollover": {
            "max_size": "50GB",
            "max_age": "1d"
          }
        }
      },
      "warm": {
        "min_age": "7d",
        "actions": {
          "shrink": {"number_of_shards": 1}
        }
      },
      "delete": {
        "min_age": "30d",
        "actions": {
          "delete": {}
        }
      }
    }
  }
}

2. 샤드 크기 최적화

# 샤드당 20-40GB 권장
# 인덱스 크기 100GB → 샤드 3-5개

3. 쿼리 최적화

# ✅ 필터 컨텍스트 사용 (캐싱)
{
  "query": {
    "bool": {
      "filter": [
        {"term": {"status": "active"}},
        {"range": {"timestamp": {"gte": "now-1h"}}}
      ]
    }
  }
}
# ❌ 쿼리 컨텍스트 (스코어 계산 오버헤드)
{
  "query": {
    "match": {"status": "active"}
  }
}

Prometheus 베스트 프랙티스

1. 레이블 카디널리티 주의

# ✅ 낮은 카디널리티
http_requests_total{method="GET", endpoint="/api/users"}
# ❌ 높은 카디널리티 (user_id는 수백만 개)
http_requests_total{user_id="123456"}  # 위험!

2. 적절한 스크랩 간격

# 일반 애플리케이션: 15-30초
scrape_interval: 15s
# 고빈도 메트릭: 5초
scrape_interval: 5s
# 저빈도 메트릭: 1분
scrape_interval: 1m

3. Recording Rules로 쿼리 최적화

# 자주 사용하는 쿼리를 미리 계산
groups:
  - name: aggregations
    interval: 30s
    rules:
      - record: job:http_requests:rate5m
        expr: sum(rate(http_requests_total[5m])) by (job)
      
      - record: job:http_errors:rate5m
        expr: sum(rate(http_requests_total{status=~"5.."}[5m])) by (job)

실전 시나리오

시나리오 1: 전자상거래 모니터링

# 애플리케이션 메트릭
from prometheus_client import Counter, Histogram, Gauge
import redis
import logging
# Redis 캐시
cache = redis.Redis(host='localhost', port=6379)
# Prometheus 메트릭
order_count = Counter('orders_total', 'Total orders', ['status'])
order_amount = Histogram('order_amount_dollars', 'Order amount')
active_users = Gauge('active_users', 'Currently active users')
# Elasticsearch 로깅
logger = logging.getLogger('app')
logger.addHandler(LogstashHandler('localhost', 5000))
@app.route('/api/order', methods=['POST'])
def create_order():
    # 캐시 확인
    user = cache.get(f"user:{user_id}")
    if not user:
        user = db.get_user(user_id)
        cache.setex(f"user:{user_id}", 600, json.dumps(user))
    
    # 주문 처리
    order = process_order(request.json)
    
    # 메트릭 기록
    order_count.labels(status='success').inc()
    order_amount.observe(order['amount'])
    
    # 로그 기록
    logger.info(f"Order created: {order['id']}", extra={
        'user_id': user_id,
        'order_id': order['id'],
        'amount': order['amount']
    })
    
    return jsonify(order)

시나리오 2: 로그 기반 알림

# Logstash 필터로 에러 감지 후 Slack 알림
filter {
  if [level] == "ERROR" {
    mutate {
      add_tag => [alert]
    }
  }
}
output {
  if "alert" in [tags] {
    http {
      url => "https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
      http_method => "post"
      format => "json"
      content_type => "application/json"
      message => '{"text": "🚨 Error detected: %{message}"}'
    }
  }
}

정리

기술 스택 역할 요약

레이어기술역할
캐싱Redis데이터 캐싱, 세션 관리, Rate Limiting
로그ELK Stack로그 수집, 검색, 분석, 시각화
메트릭Prometheus시스템/애플리케이션 메트릭 수집
시각화Grafana통합 대시보드, 알림
추적Jaeger분산 요청 추적, 성능 분석

관측성의 3가지 기둥

flowchart TB
    Observability["관측성\nObservability"]
    
    Logs["로그\nLogs"]
    Metrics["메트릭\nMetrics"]
    Traces["추적\nTraces"]
    
    Observability --> Logs
    Observability --> Metrics
    Observability --> Traces
    
    Logs --> ELK[ELK Stack]
    Metrics --> Prom[Prometheus + Grafana]
    Traces --> Jaeger[Jaeger]

시작 가이드

1단계: 기본 스택 구축

# Docker Compose로 한 번에 실행
docker-compose up -d
# 접속 URL
# Kibana: http://localhost:5601
# Grafana: http://localhost:3000
# Prometheus: http://localhost:9090
# Jaeger: http://localhost:16686

2단계: 애플리케이션 계측

# 1. Redis 캐싱 추가
# 2. Prometheus 메트릭 노출
# 3. Structured 로깅 (JSON)
# 4. OpenTelemetry Tracing

3단계: 대시보드 구성

1. Grafana에서 Prometheus 데이터 소스 추가
2. 주요 메트릭 대시보드 생성
3. Kibana에서 로그 인덱스 패턴 설정
4. Jaeger에서 서비스 추적 확인


자주 묻는 질문 (FAQ)

Q. 이 내용을 실무에서 언제 쓰나요?

A. 현대 개발자가 반드시 알아야 할 오픈소스 기술 스택. Kibana 로그 분석, Prometheus 모니터링, Redis 캐싱, Grafana 대시보드, ELK Stack 구축까지 실전 예제로 마스터하세요. Start… 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.

Q. 선행으로 읽으면 좋은 글은?

A. 각 글 하단의 이전 글 또는 관련 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.

Q. 더 깊이 공부하려면?

A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.

참고 자료

심화 부록: 구현·운영 관점

이 부록은 앞선 본문에서 다룬 주제(「필수 기술 스택 | Kibana·Prometheus·Redis·Grafana·ELK 완벽 가이드」)를 구현·런타임·운영 관점에서 다시 압축합니다. 도메인별 세부 구현은 글마다 다르지만, 입력 검증 → 핵심 연산 → 부작용(I/O·네트워크·동시성) → 관측의 흐름으로 장애를 나누면 원인 추적이 빨라집니다.

내부 동작과 핵심 메커니즘

flowchart TD
  A[입력·요청·이벤트] --> B[파싱·검증·디코딩]
  B --> C[핵심 연산·상태 전이]
  C --> D[부작용: I/O·네트워크·동시성]
  D --> E[결과·관측·저장]
sequenceDiagram
  participant C as 클라이언트/호출자
  participant B as 경계(런타임·게이트웨이·프로세스)
  participant D as 의존성(API·DB·큐·파일)
  C->>B: 요청/이벤트
  B->>D: 조회·쓰기·RPC
  D-->>B: 지연·부분 실패·재시도 가능
  B-->>C: 응답 또는 오류(코드·상관 ID)
  • 불변 조건(Invariant): 버퍼 경계, 프로토콜 상태, 트랜잭션 격리, FD 상한 등 단계별로 문장으로 적어 두면 디버깅 비용이 줄어듭니다.
  • 결정성: 순수 층과 시간·네트워크·스케줄에 의존하는 층을 분리해야 테스트와 장애 분석이 쉬워집니다.
  • 경계 비용: 직렬화, 인코딩, syscall 횟수, 락 경합, 할당·GC, 캐시 미스를 의심 목록에 둡니다.
  • 백프레셔: 생산자가 소비자보다 빠를 때 버퍼·큐·스트림에서 속도를 줄이는 신호를 어디에 둘지 정의합니다.

프로덕션 운영 패턴

영역운영 관점 질문
관측성요청 단위 상관 ID, 에러율·지연 p95/p99, 의존성 타임아웃·재시도가 대시보드에 보이는가
안전성입력 검증·권한·비밀·감사 로그가 코드 경로마다 일관적인가
신뢰성재시도는 멱등 연산에만 적용되는가, 서킷 브레이커·백오프·DLQ가 있는가
성능캐시·배치 크기·커넥션 풀·인덱스·백프레셔가 데이터 규모에 맞는가
배포롤백 룬북, 카나리/블루그린, 마이그레이션·피처 플래그가 문서화되어 있는가
용량피크 트래픽·디스크·FD·스레드 풀 상한을 주기적으로 검증하는가

스테이징은 데이터 양·네트워크 RTT·동시성을 프로덕션에 가깝게 맞출수록 재현율이 올라갑니다.

확장 예시: 엔드투엔드 미니 시나리오

앞선 본문 주제(「필수 기술 스택 | Kibana·Prometheus·Redis·Grafana·ELK 완벽 가이드」)를 배포·운영 흐름에 맞춰 옮긴 체크리스트입니다. 도메인에 맞게 단계 이름만 바꿔 적용할 수 있습니다.

  1. 입력 계약 고정: 스키마·버전·최대 페이로드·타임아웃·에러 코드를 경계에 둔다.
  2. 핵심 경로 계측: 요청 ID, 단계별 지연, 외부 호출 결과 코드를 로그·메트릭·트레이스에서 한 흐름으로 본다.
  3. 실패 주입: 의존성 타임아웃·5xx·부분 데이터·락 대기를 스테이징에서 재현한다.
  4. 호환·롤백: 설정/마이그레이션/클라이언트 버전을 되돌릴 수 있는지 확인한다.
  5. 부하 후 검증: 피크 대비 p95/p99, 에러율, 리소스 상한, 알림 임계값을 점검한다.
handle(request):
  ctx = newCorrelationId()
  validated = validateSchema(request)
  authorize(validated, ctx)
  result = domainCore(validated)
  persistOrEmit(result, idempotentKey)
  recordMetrics(ctx, latency, outcome)
  return result

문제 해결(Troubleshooting)

증상가능 원인조치
간헐적 실패레이스, 타임아웃, 외부 의존성, DNS최소 재현 스크립트, 분산 트레이스·로그 상관관계, 재시도·서킷 설정 점검
성능 저하N+1, 동기 I/O, 락 경합, 과도한 직렬화, 캐시 미스프로파일러·APM으로 핫스팟 확인 후 한 가지씩 제거
메모리 증가캐시 무제한, 구독/리스너 누수, 대용량 버퍼, 커넥션 미반납상한·TTL·힙/FD 스냅샷 비교
빌드·배포만 실패환경 변수, 권한, 플랫폼 차이, lockfileCI 로그와 로컬 diff, 런타임·이미지 버전 핀
설정 불일치프로필·시크릿·기본값, 리전스키마 검증된 설정 단일 소스와 배포 매트릭스 표준화
데이터 불일치비멱등 재시도, 부분 쓰기, 캐시 무효화 누락멱등 키·아웃박스·트랜잭션 경계 재검토

권장 순서: (1) 최소 재현 (2) 최근 변경 범위 축소 (3) 환경·의존성 차이 (4) 관측으로 가설 검증 (5) 수정 후 회귀·부하 테스트.

배포 전에는 git addgit commitgit pushnpm run deploy 순서를 권장합니다.


같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.


이 글에서 다루는 키워드 (관련 검색어)

Kibana, Prometheus, Redis, Grafana, ELK, Elasticsearch, 모니터링, 캐싱, 로그분석 등으로 검색하시면 이 글이 도움이 됩니다.