Elasticsearch FORBIDDEN/12 index read-only 에러 5분 복구 가이드
새벽에 알림이 울립니다. 애플리케이션 로그에는 이런 메시지가 도배되고 있습니다.
ClusterBlockException[index [logs-2026.06.14] blocked by:
[TOO_MANY_REQUESTS/12/disk usage exceeded flood-stage watermark,
index has read-only-allow-delete block];]또는 클라이언트 측에서는 이렇게 보일 겁니다.
FORBIDDEN/12/index read-only / allow delete (api)색인(인덱싱)이 전부 막혔고, 검색은 되는데 쓰기만 죽었습니다. 결론부터 말하면 열에 아홉은 디스크 워터마크 flood_stage(95%) 초과가 범인입니다. 이 글은 "지금 장애 중인 운영자"를 기준으로, 상단에 응급 복구 명령을 배치하고 아래에서 근본 원인 제거까지 다룹니다.
응급 흐름도: 지금 당장 뭐부터?
[쓰기 막힘]
│
├─ 1. 디스크 사용률 확인 (GET _cat/allocation?v)
│ └─ 95% 넘었나? → 거의 확정: flood_stage 자동 차단
│
├─ 2. 디스크 먼저 확보 (오래된 인덱스 삭제 / 볼륨 증설)
│
├─ 3. read_only_allow_delete 블록 해제
│
└─ 4. 모니터링·알림·ILM 설정으로 재발 방지⚠️ 순서가 핵심입니다. 디스크를 확보하지 않고 블록만 풀면 워터마크 체크 주기(기본 30초)에 곧바로 다시 차단됩니다. 아래에서 다시 강조하겠습니다.
5분 진단: 에러 원문 → 원인 분류표
에러 코드의 숫자가 원인을 알려줍니다. 먼저 이 표로 어떤 종류의 차단인지 분류하세요.
| 에러 원문 | 트리거 | 해제 키 | 비고 |
|---|---|---|---|
FORBIDDEN/12 index read-only / allow delete (api) | 디스크 자동 (flood_stage 초과) | index.blocks.read_only_allow_delete | 가장 흔함. 삭제는 허용 |
FORBIDDEN/8 index write (api) | 수동/스냅샷 복구 등 쓰기 차단 | index.blocks.write | 쓰기만 막힘 |
FORBIDDEN/5 index read-only (api) | 수동 읽기전용 설정 | index.blocks.read_only | 운영자가 직접 건 경우 |
cluster_block_exception ... no master | 클러스터 레벨 블록 | 마스터/노드 상태 복구 | 디스크 외 원인일 수 있음 |
진단 명령은 curl과 Kibana DevTools 양쪽으로 제공합니다.
curl 버전
# 노드별 디스크 사용률 (가장 중요)
curl -s "localhost:9200/_cat/allocation?v"
# 클러스터 상태
curl -s "localhost:9200/_cluster/health?pretty"
# 어떤 인덱스에 read_only 블록이 걸렸는지 확인
curl -s "localhost:9200/_all/_settings?flat_settings=true&filter_path=**.read_only*&pretty"Kibana DevTools 버전
GET _cat/allocation?v
GET _cluster/health
GET _all/_settings?flat_settings=true&filter_path=**.read_only*_cat/allocation의 disk.percent 컬럼이 95를 넘었다면 진단 끝입니다. flood_stage 자동 차단입니다.
디스크 워터마크 3단계, 이렇게 동작한다
Elasticsearch/OpenSearch는 노드 디스크 사용률을 3단계로 감시합니다.
| 단계 | 기본값 | 동작 |
|---|---|---|
| low | 85% | 새 샤드를 이 노드에 할당하지 않음 |
| high | 90% | 기존 샤드를 다른 노드로 이동 시도 |
| flood_stage | 95% | 해당 노드의 모든 인덱스에 read_only_allow_delete 자동 적용 |
핵심은 flood_stage입니다. 95%를 넘는 순간 데이터 손실(디스크 풀로 인한 인덱스 깨짐)을 막기 위해 ES가 스스로 쓰기를 차단합니다. 이름 그대로 읽기와 삭제는 되지만 새 문서 색인은 막힙니다. 즉 이건 버그가 아니라 보호 장치입니다.
즉시 복구: 쓰기 차단 해제
다시 강조합니다. 반드시 디스크를 먼저 확보한 뒤 아래 명령을 실행하세요.
curl 버전
curl -X PUT "localhost:9200/_all/_settings" \
-H 'Content-Type: application/json' -d '
{
"index.blocks.read_only_allow_delete": null
}'Kibana DevTools 버전
PUT _all/_settings
{
"index.blocks.read_only_allow_delete": null
}null로 설정하면 명시적으로 걸린 블록이 제거됩니다(false보다 null을 권장 — 자동 관리 상태로 되돌립니다). 해제 후 다시 GET _cluster/health로 색인이 정상화됐는지 확인하세요.
💡 실무 팁: 저는 장애 대응 때 항상
_cat/allocation을 먼저 띄워놓고 디스크 % 가 90 아래로 떨어지는 걸 눈으로 확인한 다음 해제 명령을 칩니다. 디스크를 안 비우고 해제부터 하면, 명령은 성공 응답을 주는데 30초 뒤 또 막혀서 "왜 안 풀리지?"로 시간을 날리게 됩니다. 이게 가장 흔한 함정입니다.
근본 원인 제거 & 디스크 확보 실전
1) 무엇이 디스크를 먹고 있나
# 데이터 디렉터리에서 용량 큰 것부터
du -sh /var/lib/elasticsearch/* | sort -rh | head
df -h2) 오래된 인덱스 삭제 (즉효약)
로그성 데이터라면 오래된 날짜 인덱스가 대부분 용량을 차지합니다.
# 와일드카드로 과거 인덱스 일괄 삭제
DELETE logs-2024.*삭제 직후 디스크가 비워지고, 위 해제 명령을 실행하면 곧바로 쓰기가 복구됩니다.
3) ILM/롤오버로 자동화 (재발 방지)
수동 삭제는 임시방편입니다. ILM(Index Lifecycle Management) 정책으로 자동 삭제를 걸어두세요.
PUT _ilm/policy/logs-policy
{
"policy": {
"phases": {
"hot": {
"actions": { "rollover": { "max_size": "50gb", "max_age": "1d" } }
},
"delete": {
"min_age": "30d",
"actions": { "delete": {} }
}
}
}
}OpenSearch에서는 ILM 대신 ISM(Index State Management) 으로 동일한 롤오버/삭제 정책을 구성합니다.
4) 워터마크 임계값 조정 (신중하게)
디스크 증설 전 임시로 임계값을 올려야 할 때가 있습니다. 단, 근본 해결이 아니라는 점을 기억하세요.
PUT _cluster/settings
{
"transient": {
"cluster.routing.allocation.disk.watermark.low": "90%",
"cluster.routing.allocation.disk.watermark.high": "95%",
"cluster.routing.allocation.disk.watermark.flood_stage": "97%"
}
}작은 디스크에서는 % 대신 "50gb"처럼 절대값으로 지정하는 게 더 예측 가능합니다.
결론: 재발 방지 체크리스트
- 디스크 사용률 알림을 워터마크보다 낮은 80%에 설정 (low 도달 전 인지)
- ILM(ES) / ISM(OpenSearch)로 롤오버·자동 삭제 정책 적용
- Data Tiers·Searchable Snapshot으로 콜드 데이터를 저비용 스토리지로 이전
- 워터마크 설정값을 문서화하고 운영 환경별로 명시
- 복구 순서(디스크 확보 → 해제 → 모니터링)를 런북에 기록
로그·관측성 데이터가 폭증하면서 디스크 포화는 ES/OpenSearch 운영자에게 가장 흔한 장애가 됐습니다. flood_stage는 적이 아니라 데이터를 지키는 마지막 방어선이라는 점, 그리고 "해제만으로는 안 풀린다"는 점만 기억하면 5분 안에 복구할 수 있습니다.
자주 묻는 질문 (FAQ)
Q. 읽기전용이라는데 디스크는 여유가 있습니다. 왜죠?
A. 한 번 flood_stage를 넘으면 디스크가 다시 내려가도 블록이 자동으로 풀리지 않는 버전이 있습니다(특히 구버전 ES 7.x 이전). 디스크 확보 후 index.blocks.read_only_allow_delete: null 해제를 수동으로 실행해야 합니다. 또는 수동으로 read_only(FORBIDDEN/5)를 걸어둔 경우일 수도 있으니 settings를 확인하세요.
Q. 노드 일부만 차단된 것 같습니다.
A. flood_stage는 노드 단위로 동작합니다. 특정 노드만 디스크가 꽉 차면 그 노드의 샤드를 가진 인덱스만 차단됩니다. _cat/allocation으로 노드별 사용률을 보고, 샤드 리밸런싱이나 해당 노드 디스크 정리를 진행하세요.
Q. AWS OpenSearch Service(매니지드)에서도 같나요?
A. 동작 원리는 같지만 매니지드 환경에서는 SSH·df 접근이 막혀 있어 디스크를 직접 비울 수 없습니다. 콘솔의 스토리지 사용량을 확인하고, 인덱스 삭제 또는 노드 스토리지 증설(또는 인스턴스 확장)로 대응해야 합니다. UltraWarm·Cold Storage로 오래된 데이터를 옮기는 것이 근본 해결책입니다.
AI 도구는 자료 조사와 초안 작성의 보조 수단으로 사용될 수 있습니다. Nodelog는 공개 전 내용과 출처를 검토하고, 환경(OS·버전)에 따라 결과가 달라질 수 있는 기술 정보는 공식 문서를 함께 확인하도록 안내합니다. 오류를 발견하시면 이메일로 제보해 주세요 — 확인 후 신속히 정정합니다.
댓글
첫 번째 댓글을 남겨보세요.