DB Study

11장. 모니터링과 트러블슈팅 — 살아있는 클러스터 다루기

운영 중인 클러스터는 살아있는 시스템입니다. 한 노드의 heap 압박이 다른 노드의 증상으로 전파되죠. green이 yellow가 되는 순간 무슨 일이 벌어지는지, 어떤 명령을 어떤 순서로 쳐야 하는지를 시나리오로 익힙니다.

중급 ⏱ 약 30분 🎬 애니메이션 3개 선수 지식: 2장 아키텍처 · 4장 샤드
🧭
이 장에서 배우는 것

① green / yellow / red의 정확한 의미와 노드 장애 → 복구의 전 과정 ② _cat · _nodes/stats · hot_threads — 진단 도구 3종 세트 ③ thread pool 429와 디스크 watermark의 동작 ④ 증상별("검색이 느려요", "색인이 안 돼요") 진단 플로우

11.1 Cluster health — green, yellow, red의 진짜 의미

GET /_cluster/health
{
  "status": "yellow",
  "number_of_nodes": 6,
  "active_primary_shards": 120,
  "unassigned_shards": 5,      ← 미할당 shard 수
  "number_of_pending_tasks": 0
}
상태의미즉시 행동
green모든 primary + replica 할당 완료없음
yellow모든 primary 할당, 일부 replica 미할당5분 내 자동복구 안 되면 진단
red일부 primary 미할당즉시 대응 — 해당 데이터 색인·검색 불가

노드 하나가 죽었을 때 클러스터가 어떻게 반응하는지, 상태 전이 전체를 따라가 봅시다.

status: green status: yellow status: green ✓ status: red node-1 P1 R3 P3 ⬆ node-2 P2 R2' R3' INITIALIZING… R3' STARTED ✓ node-3 P3 R1 💥 노드 다운 — P3, R1 소실 node-1의 replica R3가 primary로 승격 → 모든 primary 생존 = yellow (red 아님!) delayed_timeout(기본 1m) 경과 → 남은 노드에 새 replica 복제 시작 ✅ 복제 완료 — 사람의 개입 없이 green 복귀 R3 없음 만약 replica가 0이었다면? P3의 대체가 없어 red — 그 shard의 데이터만 색인·검색 불가 red ≠ 클러스터 전체 다운. 다른 인덱스는 정상 동작

11.2 진단 도구 3종 세트 — _cat, _nodes/stats, hot_threads

_cat API는 사람이 읽기 좋은 표를 반환합니다. 운영자가 가장 자주 치는 명령부터.

# 노드별 핵심 지표 한 줄씩
GET /_cat/nodes?v&h=name,node.role,heap.percent,cpu,load_1m,disk.used_percent

name     node.role  heap.percent  cpu  load_1m  disk.used_percent
es-01    cdfhirstw  68            45   2.10     67.3
es-03    cdfhirstw  85            62   3.01     71.8   ← heap 임계!

# shard 상태 (STARTED / RELOCATING / INITIALIZING / UNASSIGNED)
GET /_cat/shards?v&h=index,shard,prirep,state,docs,store,node
도구언제보는 것
_cat/nodes · indices · shards · allocation빠른 훑어보기heap/CPU/disk, 인덱스 크기, shard 상태·분포
_nodes/stats깊은 메트릭JVM heap·GC, indexing_pressure, thread_pool, breaker
_nodes/hot_threads"CPU 90%" 알람 직후바쁜 thread의 stack — merge 폭주? 비싼 쿼리? GC?
_cluster/pending_tasks클러스터가 굼뜰 때master의 state 변경 큐 (평소엔 비어 있어야 정상)
💡
hot_threads로 읽는 흔한 패턴

Lucene Merge Thread가 상위 → 작은 segment 폭증 또는 활성 인덱스 force_merge 의심. [search] thread가 aggregation에 머무름 → 비싼 쿼리. [write] thread가 mapping update에 머무름 → dynamic mapping 폭주(10장). GC thread → heap 압박.

11.3 색인이 거부될 때 — thread pool과 429

"클라이언트에서 429 에러가 폭주해요"는 ES가 고장 난 것이 아니라 스스로를 보호하는 중이라는 신호입니다. 각 노드의 write thread pool에는 큐가 있고, 큐가 가득 차면 요청을 거부합니다.

클라이언트 bulk 요청 유입 write queue write threads active 8/8 유입 ≈ 처리 — queue 여유, rejected 0 유입 > 처리 — queue가 차오른다 (heap·CPU·merge 병목 확인 신호) queue 가득! → 429 es_rejected_execution_exception 반환 _cat/thread_pool/write?v → rejected: 1234 ✅ 클라이언트가 exponential backoff로 재시도 + 유입 속도 조절 → queue 해소 근본 원인은 따로 진단: bulk 크기, refresh_interval, merge 폭주, 노드 부족 429는 장애가 아니라 backpressure — "천천히 보내라"는 신호

11.4 JVM heap과 circuit breaker

heap 진단의 위험 신호: heap_used_percent > 75% 지속, old GC가 분당 수 회 이상, breakertripped 카운터 증가. circuit breaker는 OOM 직전에 ES가 요청을 스스로 거부한 흔적입니다.

GET /_nodes/stats/breaker
"breakers": {
  "fielddata": { "tripped": 12 },   ← text 필드 fielddata 사용 의심 (10장)
  "parent":    { "tripped": 3 }
}

heap을 늘리기 전에 순서대로 점검하세요: ① 진짜 메모리 부족인가(cardinality 폭증, fielddata, 비싼 쿼리) ② cluster state가 큰가(shard 폭증 — 10장) ③ 쿼리가 비싼가(deep agg, deep paging). 해결 후에도 부족할 때만 RAM 추가 — 단, heap 31GB는 절대 넘기지 않습니다(compressed oops, 10장).

11.5 디스크 watermark — 85 / 90 / 95%의 3단 방어선

디스크가 차오르면 ES는 단계적으로 강하게 개입합니다. 마지막 단계는 꽤 충격적입니다: 모든 인덱스가 read-only가 됩니다.

node disk 사용률 85% 90% 95% ~70% — 정상 운영 shard 할당·이동 자유롭게 85% low watermark 돌파 이 노드에 새 shard 할당 중단 90% high watermark 돌파 기존 shard를 다른 노드로 재배치 시도 (RELOCATING) 🚨 95% flood_stage — 모든 인덱스 read-only 전환! index.blocks.read_only_allow_delete: true 해제는 디스크 확보 후: PUT /my-index/_settings {"index.blocks.read_only_allow_delete": null} 영구 해결: 오래된 인덱스 삭제(ILM) · 노드/디스크 추가 · cold/frozen 티어 이전 대용량 디스크는 %보다 절대값(예: low 200gb / high 100gb / flood 50gb)이 합리적

11.6 shard가 UNASSIGNED일 때 — allocation/explain

미할당 shard의 원인은 추측하지 말고 물어보세요. 왜 할당이 안 되는지 ES가 직접 설명해 줍니다.

GET /_cluster/allocation/explain
{
  "unassigned_info": { "reason": "NODE_LEFT" },
  "node_allocation_decisions": [{
    "node_name": "es-03",
    "deciders": [{
      "decider": "disk_threshold",
      "explanation": "the node is above the high watermark..."   ← 원인이 그대로
    }]
  }]
}
reason의미처방
INDEX_CREATED신규 인덱스 할당 대기대기, disk/awareness 확인
NODE_LEFT노드 이탈노드 복귀 대기 또는 재할당 확인
ALLOCATION_FAILED5회 재시도 모두 실패POST /_cluster/reroute?retry_failed=true
CLUSTER_RECOVERED재시작 후 복구 진행자동 복구 대기
🚨
allocate_empty_primary는 데이터를 지운다

POST /_cluster/rerouteallocate_empty_primary 명령은 빈 primary를 강제 할당합니다 — 그 shard의 기존 데이터는 사라집니다. 스냅샷 복원조차 불가능할 때의 마지막 수단으로만.

11.7 증상별 진단 플로우

# "검색이 갑자기 느려졌다"
1. _cluster/health          → red? yellow? unassigned?
2. _cat/nodes?v             → 어느 노드가 heap·CPU 높은가
3. _nodes/hot_threads       → 지금 무슨 일을 하고 있나
4. _cat/thread_pool/search?v → queue·rejected?
5. slow log                 → 어떤 쿼리가 느린가 (query vs fetch phase 구분)
6. _nodes/stats/breaker     → fielddata tripped?

# "색인이 느리다 / 429 폭주"
1. _cat/thread_pool/write?v      → queue·rejected
2. _nodes/stats/indexing_pressure → 거부 카운트 (8.x)
3. _cat/allocation?v             → 디스크 watermark 임박?
4. _cluster/pending_tasks        → master 큐 적체 (mapping 폭증?)
5. _nodes/hot_threads            → merge 폭주? mapping update?
💡
모니터링 데이터는 별도 클러스터로

운영 클러스터의 모니터링 데이터를 자기 자신에 저장하면, 클러스터가 죽는 순간 모니터링도 같이 죽습니다. Metricbeat/Elastic Agent로 별도 monitoring 클러스터에 보내는 것이 정석이며, Kibana Stack Monitoring이 이를 시각화해 줍니다.

✍️ 이해도 체크

replica 1로 운영 중인 클러스터에서 노드 하나가 다운되어 그 노드의 primary shard가 사라졌다. 직후 클러스터의 동작으로 옳은 것은?
✅ replica가 있으면 즉시 primary로 승격되어 모든 primary가 활성 상태를 유지합니다 = yellow(일부 replica 미할당). delayed_timeout 후 새 replica 복제가 끝나면 green으로 복귀합니다. red는 승격할 replica조차 없어 primary가 미할당일 때만 발생하며, 그때도 다른 인덱스는 정상 동작합니다.
디스크 사용률이 95%(flood_stage watermark)를 넘으면 Elasticsearch는 무엇을 하는가?
✅ flood_stage(95%)에서는 index.blocks.read_only_allow_delete가 걸려 색인이 전면 차단됩니다. 데이터를 마음대로 지우지는 않습니다(삭제는 허용). 디스크 확보 후 블록을 수동 해제해야 하며, "새 shard 할당 중단"은 85%(low), "shard 재배치"는 90%(high)의 동작입니다.
📚
원문으로 더 깊이

이 장의 원문 문서: chapters/ch11_monitoring_troubleshooting.md — slow log 설정, OOM 진단 플로우, 진단 쿼리 15선이 담겨 있습니다.