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 에이전트가 1차 초안을 작성한 뒤, 사람 편집자가 사실관계·출처·톤과 맥락을 검토하여 발행했습니다. 오류나 부정확한 내용이 확인되면 24시간 이내에 정정합니다.
댓글
불러오는 중...