운영 중인 Elasticsearch 클러스터에서 갑자기 CircuitBreakerException 에러가 발생하면서 서비스가 중단된 경험이 있으신가요? 이 에러는 단순해 보이지만 실제로는 메모리 관리와 직결된 중요한 문제입니다.

오늘은 현업에서 자주 마주치는 이 문제의 정확한 원인과 실제로 효과가 검증된 해결방법들을 차근차근 살펴보겠습니다.

 

1. CircuitBreakerException이란?

Elasticsearch의 Circuit Breaker는 OutOfMemory Error 발생전 CircuitBreakerException을 발생시켜 차단시키는 역할을 합니다. 쉽게 말해, 전기회로의 차단기처럼 시스템을 보호하는 안전장치인 셈이죠.

만약 특정 요청이 메모리 문제로 인해 노드에 오류를 일으킬 수 있다면, Elasticsearch는 전체 노드가 크래시될 위험을 감수하기보다는 “CircuitBreakerException”을 발생시키고 요청을 거부합니다.

실제 에러 예시:

{
  "error": {
    "type": "circuit_breaking_exception",
    "reason": "[parent] Data too large, data for [<http_request>] would be [16355096754/15.2gb], which is larger than the limit of [16213167308/15gb]",
    "bytes_wanted": 16355096754,
    "bytes_limit": 16213167308
  },
  "status": 429
}

 

 

2. 주요 Circuit Breaker 유형별 특징

Elasticsearch는 여러 종류의 Circuit Breaker를 운영합니다. 각각의 역할과 기본 설정값을 알아보겠습니다.

유형별 설정값:

Circuit Breaker 유형 기본 한계값 역할
Parent Breaker JVM Heap의 95% 전체 메모리 사용량 제어
Request Breaker JVM Heap의 60% 요청 처리 시 메모리 사용량 제어
Fielddata Breaker JVM Heap의 40% Fielddata 캐시 메모리 사용량 제어
In-flight Requests JVM Heap의 100% 진행 중인 요청의 메모리 사용량 제어

 

 

3. 근본 원인 분석 방법

문제 해결의 첫 단계는 정확한 원인 파악입니다. 다음 명령어들로 현재 상태를 확인해보세요.

메모리 사용량 확인:

# 노드별 힙 메모리 사용률 확인
curl -X GET "localhost:9200/_cat/nodes?v&h=name,heap.percent,heap.current,heap.max"

# Circuit Breaker별 상세 정보 확인
curl -X GET "localhost:9200/_nodes/stats/breaker"

Fielddata 사용량 점검:

# Fielddata 메모리 사용량 확인
curl -X GET "localhost:9200/_nodes/stats/indices/fielddata"

 

4. 즉시 적용 가능한 해결방법

4-1. JVM 힙 크기 조정

가장 직접적인 해결책은 힙 메모리 증가입니다. JVM 힙 크기를 30-32GB로 설정하고 전체 사용 가능 메모리의 50% 미만으로 유지하는 것이 권장됩니다.

Docker 환경에서 설정:

# docker-compose.yml
services:
  elasticsearch:
    environment:
      ES_JAVA_OPTS: "-Xmx8g -Xms8g"  # 기존보다 증가

jvm.options 파일 수정:

# 기존
-Xms4g
-Xmx4g

# 수정 후
-Xms8g
-Xmx8g

4-2. Circuit Breaker 한계값 조정

임시 해결 (재시작 불필요):

# Request Circuit Breaker 한계값 증가
curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
  "persistent": {
    "indices.breaker.request.limit": "65%"
  }
}'

# Parent Circuit Breaker 한계값 조정
curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
  "persistent": {
    "indices.breaker.total.limit": "80%"
  }
}'

영구 설정 (elasticsearch.yml):

indices.breaker.total.limit: 80%
indices.breaker.request.limit: 65%
indices.breaker.fielddata.limit: 45%

 

 

5. Fielddata 관련 문제 해결

고유값이 많은 텍스트 필드의 경우 fielddata가 대량의 JVM 메모리를 사용할 수 있어, Elasticsearch는 기본적으로 text 필드에서 fielddata를 비활성화합니다.

5-1. Fielddata 캐시 즉시 정리

# 전체 클러스터의 fielddata 캐시 정리
curl -X POST "localhost:9200/*/_cache/clear?fielddata=true"

# 특정 인덱스의 fielddata 캐시 정리
curl -X POST "localhost:9200/your_index/_cache/clear?fielddata=true"

5-2. 매핑 구조 개선

// 문제가 되는 text 필드를 keyword로 변경
{
  "mappings": {
    "properties": {
      "category": {
        "type": "keyword"    // text 대신 keyword 사용
      }
    }
  }
}

 

 

6. 쿼리 최적화를 통한 근본 해결

6-1. Aggregation 크기 제한

// 문제가 될 수 있는 쿼리
{
  "aggs": {
    "categories": {
      "terms": {
        "field": "category",
        "size": 10000      // 너무 큰 값
      }
    }
  }
}

// 최적화된 쿼리
{
  "aggs": {
    "categories": {
      "terms": {
        "field": "category",
        "size": 100        // 적절한 크기로 제한
      }
    }
  }
}

6-2. 검색 범위 제한

// 시간 범위를 명확히 제한
{
  "query": {
    "range": {
      "@timestamp": {
        "gte": "now-1d",
        "lte": "now"
      }
    }
  }
}

 

 

7. 모니터링 및 예방 조치

7-1. 실시간 메모리 모니터링 설정

# 메모리 사용량 지속 모니터링을 위한 스크립트
watch -n 30 'curl -s "localhost:9200/_cat/nodes?v&h=name,heap.percent,heap.current" | column -t'

7-2. 알림 시스템 구축

기본적으로 parent circuit breaker는 JVM 메모리 사용량이 95%에 도달하면 트리거되므로, 사용량이 지속적으로 85%를 초과하면 오류를 방지하기 위한 조치를 취하는 것이 권장됩니다.

Kibana Watcher를 활용한 알림 설정:

{
  "trigger": {
    "schedule": {
      "interval": "1m"
    }
  },
  "input": {
    "search": {
      "request": {
        "indices": [".monitoring-es-*"],
        "body": {
          "query": {
            "bool": {
              "filter": [
                {"range": {"timestamp": {"gte": "now-2m"}}},
                {"range": {"node_stats.jvm.mem.heap_used_percent": {"gte": 85}}}
              ]
            }
          }
        }
      }
    }
  }
}

 

 

8. 클러스터 확장 고려사항

메모리 증가나 설정 조정으로도 해결되지 않는다면 클러스터 확장을 검토해야 합니다.

노드 추가 시 고려사항:

  • 데이터 노드 추가: 샤드 분산으로 부하 분산
  • 메모리 사양 향상: 32GB 이상의 노드 사용 시 Compressed OOP 비활성화 주의
  • 네트워크 대역폭: 노드 간 통신 부하 고려

 

 

9. 마지막으로 정리하자면…

실제 장애 상황에서 빠르게 대응할 수 있도록 체크리스트를 준비했습니다.

즉시 확인사항:

  1. ✅ 현재 힙 메모리 사용률 85% 이상인가?
  2. ✅ 어떤 Circuit Breaker가 트리거되었나?
  3. ✅ 대용량 aggregation 쿼리가 실행 중인가?
  4. ✅ Fielddata 사용량이 비정상적으로 높은가?

즉시 조치사항:

  1. 🔧 진행 중인 heavy 쿼리 중단
  2. 🔧 Fielddata 캐시 정리
  3. 🔧 Circuit Breaker 한계값 임시 증가
  4. 🔧 JVM 힙 크기 조정 후 재시작

 

정확한 원인 파악과 단계적 접근으로 CircuitBreakerException 문제를 효과적으로 해결할 수 있습니다. 무엇보다 평상시 모니터링을 통한 예방이 가장 중요하다는 점을 기억하세요.

 

댓글 남기기