들어가며
OpenSearch(또는 Elasticsearch)는 분산 검색 엔진으로, 클러스터 상태 관리와 성능 모니터링이 복잡합니다. 노드가 여러 개이고, 샤드가 분산되어 있으며, 인덱싱과 검색이 동시에 일어나기 때문입니다. 이 글에서는 OpenSearch 운영에 필수적인 모니터링 지표들을 정리합니다.
모니터링 아키텍처
OpenSearch → opensearch-exporter → Prometheus → Grafana또는 OpenSearch 자체의 _cat, _cluster, _nodes API를 활용할 수 있습니다.
핵심 모니터링 지표
1. 클러스터 헬스 (Cluster Health)
가장 먼저 확인해야 할 지표입니다. 클러스터 헬스는 신호등처럼 직관적으로 시스템 상태를 알려줍니다.
OpenSearch는 데이터를 샤드(Shard) 단위로 분산 저장합니다. 각 인덱스는 여러 Primary 샤드로 나뉘고, 각 Primary 샤드는 고가용성을 위해 Replica 샤드를 가집니다. 클러스터 헬스는 이 샤드들의 상태를 종합하여 보여줍니다.
# 클러스터 상태 확인
curl -X GET "localhost:9200/_cluster/health?pretty"상태 종류:
green: 모든 샤드 정상. Primary와 Replica가 모두 할당되어 있어 노드 장애에도 데이터 안전yellow: Primary 샤드 정상이지만 일부 Replica가 미할당. 검색/인덱싱은 작동하지만 장애 복구 능력이 저하된 상태red: Primary 샤드 일부가 미할당. 해당 샤드의 데이터에 접근할 수 없으므로 즉시 대응 필요
Yellow 상태는 단일 노드 클러스터에서 흔히 발생합니다. Replica를 배치할 다른 노드가 없기 때문입니다. 프로덕션에서는 최소 3개 노드로 구성하여 Green 상태를 유지해야 합니다.
주요 지표:
cluster_health_status: 클러스터 상태 (green=0, yellow=1, red=2)cluster_health_number_of_nodes: 노드 수cluster_health_active_primary_shards: 활성 Primary 샤드 수cluster_health_unassigned_shards: 미할당 샤드 수
알람 기준:
- status = yellow: Warning
- status = red: Critical
- unassigned_shards > 0: 조사 필요
# PromQL - 클러스터 상태 (0=green, 1=yellow, 2=red)
opensearch_cluster_health_status
# 미할당 샤드
opensearch_cluster_health_unassigned_shards2. 노드 상태 (Node Stats)
각 노드의 리소스 사용량을 모니터링합니다. OpenSearch 클러스터는 여러 노드로 구성되며, 각 노드가 데이터의 일부(샤드)를 담당합니다. 특정 노드에 문제가 생기면 해당 노드의 샤드가 다른 노드로 재배치되므로, 노드별 리소스 상태를 개별적으로 모니터링해야 합니다.
curl -X GET "localhost:9200/_nodes/stats?pretty"JVM 힙 메모리
OpenSearch는 JVM 위에서 동작하므로 JVM 힙 관리가 매우 중요합니다. 힙은 검색 결과 캐싱, 필드 데이터, 세그먼트 메모리 등에 사용됩니다. 힙이 부족하면 GC가 빈번해지고, 심하면 OOM으로 노드가 죽을 수 있습니다.
일반적으로 힙 크기는 시스템 메모리의 50% 이하, 그리고 32GB를 넘지 않도록 설정합니다. 32GB를 넘으면 JVM의 Compressed OOPs(Ordinary Object Pointers) 최적화를 사용할 수 없어 오히려 성능이 저하됩니다.
주요 지표:
jvm_mem_heap_used_bytes: 힙 사용량jvm_mem_heap_max_bytes: 최대 힙 크기jvm_gc_collection_time_seconds: GC 소요 시간
알람 기준:
- 힙 사용률 75% 이상 지속: Warning
- 힙 사용률 85% 이상: Critical
# PromQL - 힙 사용률
opensearch_jvm_mem_heap_used_bytes / opensearch_jvm_mem_heap_max_bytes * 100
# GC 발생률
rate(opensearch_jvm_gc_collection_seconds_count[5m])CPU 사용률
CPU는 검색 쿼리 처리, 인덱싱, 세그먼트 병합 등에 사용됩니다. CPU 사용률이 지속적으로 높다면 쿼리 최적화나 노드 증설을 고려해야 합니다.
# PromQL - OS 레벨 CPU 사용률
opensearch_os_cpu_percent
# 프로세스 CPU
opensearch_process_cpu_percent디스크 사용량
디스크 공간은 OpenSearch 운영에서 가장 흔한 문제 원인 중 하나입니다. OpenSearch는 디스크 사용률이 특정 임계값(watermark)에 도달하면 자동으로 동작을 제한합니다:
- Low watermark (기본 85%): 새 샤드 할당을 중지
- High watermark (기본 90%): 샤드를 다른 노드로 이동 시작
- Flood stage (기본 95%): 인덱스를 read-only로 전환
read-only로 전환되면 인덱싱이 멈추므로, 디스크 사용량이 80%에 도달하면 미리 조치를 취해야 합니다.
curl -X GET "localhost:9200/_cat/allocation?v"알람 기준:
- 디스크 사용률 80% 이상: Warning (watermark)
- 디스크 사용률 90% 이상: Critical (read-only 전환)
# PromQL - 디스크 사용률
opensearch_fs_total_used_bytes / opensearch_fs_total_bytes * 100
# 디스크 여유 공간
opensearch_fs_total_available_bytes3. 인덱싱 성능 (Indexing)
인덱싱은 문서를 OpenSearch에 저장하는 과정입니다. 문서는 먼저 메모리 버퍼에 쓰여지고, 주기적으로 디스크의 세그먼트로 플러시됩니다.
인덱싱 성능에 영향을 주는 요소들:
- Bulk 크기: 한 번에 너무 많은 문서를 보내면 메모리 부족, 너무 적으면 오버헤드 증가
- Refresh 간격: 검색 가능 상태로 만드는 주기. 짧으면 실시간성은 좋지만 CPU 부하 증가
- Replica 수: Replica가 많으면 인덱싱 시 각 Replica에도 써야 하므로 느려짐
로그 수집처럼 대량 인덱싱이 필요한 경우, refresh interval을 30초 이상으로 늘리고 bulk 크기를 조정하면 성능이 크게 향상됩니다.
curl -X GET "localhost:9200/_nodes/stats/indices/indexing?pretty"주요 지표:
indexing_index_total: 총 인덱싱 문서 수indexing_index_time_seconds: 인덱싱 소요 시간indexing_index_current: 현재 진행 중인 인덱싱
# PromQL - 인덱싱 처리량 (docs/sec)
rate(opensearch_indices_indexing_index_total[5m])
# 평균 인덱싱 시간 (ms/doc)
rate(opensearch_indices_indexing_index_time_seconds[5m]) /
rate(opensearch_indices_indexing_index_total[5m]) * 1000
# Bulk 거부율
rate(opensearch_thread_pool_bulk_rejected_count[5m])알람 기준:
- Bulk rejected > 0: 인덱싱 부하 과다
- 인덱싱 지연 시간 증가: 조사 필요
4. 검색 성능 (Search)
검색은 Query 단계와 Fetch 단계로 나뉩니다. Query 단계에서 각 샤드가 매칭되는 문서 ID를 찾고, Fetch 단계에서 실제 문서 내용을 가져옵니다.
검색 성능에 영향을 주는 요소들:
- 샤드 수: 샤드가 많으면 병렬 처리가 가능하지만, 오버헤드도 증가
- 쿼리 복잡도: 와일드카드, 정규식, 스크립트 쿼리는 느림
- 필드 데이터: 정렬, 집계에 사용되는 필드 데이터는 힙 메모리를 많이 사용
검색 지연이 발생하면 슬로우 로그를 활성화하여 어떤 쿼리가 느린지 파악합니다. 대부분의 경우 쿼리 최적화나 인덱스 매핑 개선으로 해결할 수 있습니다.
curl -X GET "localhost:9200/_nodes/stats/indices/search?pretty"주요 지표:
search_query_total: 총 검색 쿼리 수search_query_time_seconds: 검색 소요 시간search_fetch_total: Fetch 단계 수search_fetch_time_seconds: Fetch 소요 시간
# PromQL - 검색 처리량 (queries/sec)
rate(opensearch_indices_search_query_total[5m])
# 평균 검색 시간 (ms)
rate(opensearch_indices_search_query_time_seconds[5m]) /
rate(opensearch_indices_search_query_total[5m]) * 1000
# Search rejected
rate(opensearch_thread_pool_search_rejected_count[5m])알람 기준:
- 검색 지연 시간 P95 > 500ms: Warning
- Search rejected > 0: 검색 스레드 풀 부족
5. 스레드 풀 (Thread Pool)
OpenSearch는 작업 유형별로 별도의 스레드 풀을 운영합니다. 각 스레드 풀은 고정된 크기와 대기 큐를 가지며, 큐가 가득 차면 요청을 거부(reject)합니다.
스레드 풀 모니터링의 핵심은 rejected 횟수입니다. rejected가 발생하면 해당 작업이 실패한 것이므로, 클라이언트는 재시도해야 합니다. rejected가 지속적으로 발생하면 노드 증설이나 작업 부하 분산이 필요합니다.
curl -X GET "localhost:9200/_cat/thread_pool?v"주요 스레드 풀:
search: 검색 쿼리 처리write(또는bulk): 인덱싱/업데이트/삭제get: 문서 조회management: 클러스터 관리
# PromQL - 스레드 풀 상태
opensearch_thread_pool_threads{name="search",type="active"}
opensearch_thread_pool_threads{name="search",type="queue"}
opensearch_thread_pool_rejected_count{name="search"}
# Write 스레드 풀
opensearch_thread_pool_rejected_count{name="write"}알람 기준:
- rejected > 0: 해당 작업 부하 과다
- queue 지속 증가: 처리 지연
6. 샤드 상태 (Shard Stats)
샤드는 OpenSearch에서 데이터 분산의 기본 단위입니다. 적절한 샤드 수와 크기는 성능에 큰 영향을 미칩니다.
샤드 설계 시 고려할 점:
- 샤드 크기: 일반적으로 10-50GB가 적당. 너무 크면 복구 시간이 길어지고, 너무 작으면 오버헤드 증가
- 샤드 수: 노드당 20개 이하 권장. 샤드가 너무 많으면 힙 메모리 소비와 클러스터 관리 오버헤드 증가
- 균등 분배: 샤드가 특정 노드에 몰리면 핫스팟 발생
샤드 분배가 불균형하면 특정 노드에 부하가 집중됩니다. _cat/allocation API로 노드별 샤드 수와 디스크 사용량을 확인하세요.
curl -X GET "localhost:9200/_cat/shards?v"주요 지표:
- 샤드 수: Primary + Replica
- 샤드 크기
- 샤드 분배 균형
# PromQL - 인덱스별 샤드 수
opensearch_index_shards_primary
opensearch_index_shards_replica
# 샤드 크기
opensearch_index_store_size_bytes7. Refresh와 Flush
Refresh와 Flush는 OpenSearch의 데이터 영속성과 검색 가시성을 담당하는 중요한 작업입니다.
- Refresh: 메모리 버퍼의 데이터를 검색 가능한 세그먼트로 만드는 작업. 기본값은 1초 간격으로 실행되며, 이후 새로 인덱싱된 문서가 검색에 나타남
- Flush: 메모리의 데이터를 디스크에 영구 저장하고 translog를 정리하는 작업. 노드 재시작 시 데이터 복구에 사용
Refresh가 너무 자주 발생하면 CPU 부하가 증가하고 세그먼트가 많아집니다. 로그 수집처럼 실시간성이 덜 중요한 경우 refresh_interval을 늘려 성능을 개선할 수 있습니다.
curl -X GET "localhost:9200/_nodes/stats/indices/refresh,flush?pretty"주요 지표:
refresh_total: Refresh 횟수 (검색 가능 상태로 만듦)flush_total: Flush 횟수 (디스크에 영구 저장)
# PromQL - Refresh/Flush 발생률
rate(opensearch_indices_refresh_total[5m])
rate(opensearch_indices_flush_total[5m])부하 상황에서의 모니터링
실제 운영 환경에서는 검색 부하와 인덱싱 부하가 동시에 발생하는 경우가 많습니다. 어떤 작업이 병목인지 파악하고 적절히 대응해야 합니다.
검색 부하 (Search-Heavy)
검색 부하가 높을 때는 주로 CPU와 힙 메모리가 문제가 됩니다. 복잡한 쿼리, 대량의 집계, 정렬 등이 리소스를 많이 소비합니다.
관찰 포인트:
- 검색 지연 시간 증가
- Search 스레드 풀 queue 증가
- CPU 사용률 증가
# 슬로우 로그 확인 (opensearch.yml 설정 필요)
index.search.slowlog.threshold.query.warn: 10s
index.search.slowlog.threshold.query.info: 5s
index.search.slowlog.threshold.fetch.warn: 1s인덱싱 부하 (Indexing-Heavy)
대량 인덱싱 시에는 디스크 I/O와 세그먼트 병합이 병목이 됩니다. Bulk rejected가 발생하면 인덱싱 요청이 실패하는 것이므로, 클라이언트에서 재시도 로직을 구현하거나 인덱싱 속도를 조절해야 합니다.
관찰 포인트:
- Bulk rejected 증가
- Refresh 시간 증가
- 디스크 I/O 증가
대량 인덱싱 시 권장 사항:
- refresh_interval을 30s 이상으로 늘리기
- 인덱싱 완료 후 명시적으로 refresh 호출
- replica를 0으로 설정 후 인덱싱, 완료 후 replica 복원
# Translog 상태 확인
curl -X GET "localhost:9200/_cat/indices?v&h=index,pri,rep,docs.count,store.size,pri.store.size"메모리 부하
힙 메모리가 부족하면 GC가 빈번해지고, Circuit Breaker가 트립되어 요청이 거부됩니다. Circuit Breaker는 OOM을 방지하기 위한 안전장치로, 메모리 사용량이 임계값을 넘으면 작동합니다.
관찰 포인트:
- 힙 사용률 75% 이상 지속
- GC 빈도 증가
- Circuit breaker 트립
# Circuit breaker 트립 횟수
rate(opensearch_breakers_tripped[5m])대용량 인덱스 작업 모니터링
대용량 데이터 마이그레이션이나 매핑 변경은 클러스터에 상당한 부하를 줍니다. 서비스에 영향을 최소화하려면 작업을 모니터링하면서 점진적으로 진행해야 합니다.
Reindex 작업
Reindex는 한 인덱스의 데이터를 다른 인덱스로 복사하는 작업입니다. 매핑 변경, 샤드 수 조정, 데이터 마이그레이션 등에 사용됩니다.
대용량 인덱스를 reindex할 때는 wait_for_completion=false로 비동기 실행하고, Task API로 진행 상황을 모니터링합니다. slices 파라미터로 병렬 처리하면 속도를 높일 수 있지만, 클러스터 부하도 증가합니다.
# Reindex 시작
POST _reindex?wait_for_completion=false
{
"source": { "index": "old_index" },
"dest": { "index": "new_index" }
}
# 작업 상태 확인
GET _tasks?actions=*reindex&detailed모니터링 포인트:
- 인덱싱 처리량
- 소스 인덱스 검색 부하
- 대상 인덱스 크기 증가
인덱스 매핑 변경
OpenSearch에서는 기존 필드의 매핑을 직접 변경할 수 없습니다. 예를 들어, text 필드를 keyword로 바꾸거나, integer를 long으로 바꾸는 것이 불가능합니다. 새 인덱스를 생성하고 Reindex해야 합니다.
무중단으로 매핑을 변경하는 방법은 Alias를 활용하는 것입니다. 애플리케이션은 인덱스 이름 대신 Alias를 사용하고, 마이그레이션 완료 후 Alias를 새 인덱스로 전환합니다.
# 새 인덱스 생성 (새 매핑 적용)
PUT new_index
{
"mappings": { ... }
}
# Reindex
POST _reindex
{
"source": { "index": "old_index" },
"dest": { "index": "new_index" }
}
# Alias 전환
POST _aliases
{
"actions": [
{ "remove": { "index": "old_index", "alias": "my_alias" }},
{ "add": { "index": "new_index", "alias": "my_alias" }}
]
}Shard 리밸런싱
노드를 추가하거나 제거할 때 샤드가 재배치됩니다. 이 과정에서 네트워크 대역폭과 디스크 I/O가 증가하며, 검색/인덱싱 성능에 영향을 줄 수 있습니다.
대규모 작업 전에는 리밸런싱을 일시 중지하고, 작업 완료 후 다시 활성화하는 것이 좋습니다. 특히 rolling restart나 노드 교체 시 유용합니다.
# 클러스터 설정 조정 (일시적으로 리밸런싱 중지)
PUT _cluster/settings
{
"transient": {
"cluster.routing.rebalance.enable": "none"
}
}
# 작업 후 복원
PUT _cluster/settings
{
"transient": {
"cluster.routing.rebalance.enable": "all"
}
}모니터링 포인트:
- relocating_shards 수
- 노드별 디스크 사용량 균형
- 검색/인덱싱 성능 영향
# 리밸런싱 중인 샤드
opensearch_cluster_health_relocating_shardsGrafana 대시보드 구성
OpenSearch 대시보드는 클러스터 전체 상태와 노드별 상세 정보를 모두 보여줘야 합니다. 문제 발생 시 어느 노드가 원인인지 빠르게 파악할 수 있어야 합니다.
추천 패널 레이아웃
┌─────────────────────────────────────────────────────────┐
│ Overview: 클러스터 상태, 노드 수, 샤드 상태 │
├─────────────────────────────────────────────────────────┤
│ JVM Heap (노드별) │ CPU Usage (노드별) │
├───────────────────────┼────────────────────────────────┤
│ Indexing Rate │ Search Rate │
├───────────────────────┼────────────────────────────────┤
│ Search Latency │ Indexing Latency │
├───────────────────────┼────────────────────────────────┤
│ Thread Pool Rejected │ Disk Usage (노드별) │
├─────────────────────────────────────────────────────────┤
│ Index Sizes (Top 10) │ Shard Distribution │
└─────────────────────────────────────────────────────────┘커뮤니티 대시보드
- Elasticsearch Exporter Dashboard (ID: 2322)
- OpenSearch Overview (ID: 14191)
opensearch-exporter 설정
# docker-compose.yml
services:
opensearch-exporter:
image: prometheuscommunity/elasticsearch-exporter
command:
- '--es.uri=http://opensearch:9200'
- '--es.all'
- '--es.indices'
- '--es.indices_settings'
- '--es.shards'
ports:
- "9114:9114"인증 설정 (보안 활성화 시)
command:
- '--es.uri=https://admin:admin@opensearch:9200'
- '--es.ssl-skip-verify'알람 규칙 예시
# prometheus-rules.yml
groups:
- name: opensearch
rules:
- alert: OpenSearchClusterYellow
expr: opensearch_cluster_health_status == 1
for: 5m
labels:
severity: warning
annotations:
summary: "OpenSearch 클러스터 상태 Yellow"
- alert: OpenSearchClusterRed
expr: opensearch_cluster_health_status == 2
for: 1m
labels:
severity: critical
annotations:
summary: "OpenSearch 클러스터 상태 Red - 데이터 유실 위험"
- alert: OpenSearchUnassignedShards
expr: opensearch_cluster_health_unassigned_shards > 0
for: 5m
labels:
severity: warning
annotations:
summary: "미할당 샤드 존재"
- alert: OpenSearchHighJVMHeapUsage
expr: |
opensearch_jvm_mem_heap_used_bytes /
opensearch_jvm_mem_heap_max_bytes > 0.75
for: 10m
labels:
severity: warning
annotations:
summary: "OpenSearch JVM 힙 사용률 75% 초과"
- alert: OpenSearchDiskWatermarkHigh
expr: |
opensearch_fs_total_used_bytes /
opensearch_fs_total_bytes > 0.85
for: 5m
labels:
severity: warning
annotations:
summary: "디스크 사용률 85% 초과 - Watermark 임계값 근접"
- alert: OpenSearchBulkRejected
expr: rate(opensearch_thread_pool_rejected_count{name="write"}[5m]) > 0
for: 5m
labels:
severity: warning
annotations:
summary: "Bulk 인덱싱 요청 거부 발생"
- alert: OpenSearchSearchRejected
expr: rate(opensearch_thread_pool_rejected_count{name="search"}[5m]) > 0
for: 5m
labels:
severity: warning
annotations:
summary: "검색 요청 거부 발생"
- alert: OpenSearchHighSearchLatency
expr: |
rate(opensearch_indices_search_query_time_seconds[5m]) /
rate(opensearch_indices_search_query_total[5m]) > 0.5
for: 10m
labels:
severity: warning
annotations:
summary: "검색 평균 지연 시간 500ms 초과"성능 최적화 체크리스트
성능 문제가 발생했을 때 확인해야 할 주요 설정들입니다.
힙 메모리 설정
힙 크기 설정은 OpenSearch 성능의 핵심입니다. 너무 작으면 GC가 빈번해지고, 너무 크면 GC 시간이 길어집니다. 시스템 메모리의 나머지 절반은 파일 시스템 캐시로 사용되어 검색 성능에 기여합니다.
# opensearch.yml 또는 환경 변수
# 힙은 전체 메모리의 50% 이하, 최대 32GB 이하 권장
OPENSEARCH_JAVA_OPTS="-Xms16g -Xmx16g"Refresh Interval 조정
기본 refresh interval은 1초입니다. 실시간 검색이 필요 없다면 늘려서 성능을 개선할 수 있습니다.
# 인덱싱 부하가 클 때 refresh 간격 늘리기
PUT my_index/_settings
{
"index.refresh_interval": "30s"
}
# 벌크 인덱싱 완료 후 복원
PUT my_index/_settings
{
"index.refresh_interval": "1s"
}Watermark 설정 확인
디스크 watermark 설정은 클러스터가 디스크 부족에 어떻게 대응할지 결정합니다. 기본값이 대부분의 경우에 적절하지만, 대용량 디스크를 사용하거나 특수한 요구사항이 있을 때 조정이 필요할 수 있습니다.
# 디스크 워터마크 설정
PUT _cluster/settings
{
"transient": {
"cluster.routing.allocation.disk.watermark.low": "85%",
"cluster.routing.allocation.disk.watermark.high": "90%",
"cluster.routing.allocation.disk.watermark.flood_stage": "95%"
}
}정리
OpenSearch 모니터링의 핵심 포인트:
- 클러스터 헬스: Green 상태 유지, Red는 즉시 대응
- JVM 힙: 75% 이하 유지
- 디스크: Watermark 도달 전 확장
- 스레드 풀: Rejected > 0이면 부하 분산 필요
- 검색/인덱싱 지연: 지표 추이 관찰