IL-0434 CyberAgent · 2016-06-10 (게시일)database
ElasticSearch 0.19 클러스터 전체 재시작 뒤 샤드 12개가 unassigned 로 남아 데이터 일부를 읽을 수 없었다
CyberAgent 의 Ameba 는 사용자 서비스에 ElasticSearch 0.19.10 을 OpenStack 가상 서버 30대로 운영했다. 인덱스는 3개, 샤드는 128개, 레플리카는 2개였고, 전체 데이터는 약 300GB 였다. OpenStack 호스트 서버가 다운되어 노드 하나가 내려가자 샤드 재배치가 돌아 그 노드의 샤드가 다른 노드로 흩어졌다. 클러스터 상태는 그린이었지만 API 서버 쪽 헬스 체크가 false 를 반환했다. 호스트가 복구된 뒤 노드를 다른 호스트로 옮겨 클러스터에 다시 넣었지만 헬스 체크는 여전히 false 였다.
팀은 다른 환경에서 같은 증상을 전체 재시작으로 고친 경험이 있어, 자동 재배치를 끄지 않은 채 1번 노드부터 로그를 보며 차례로 재시작했다. 스무 번째 노드까지 재시작했을 즈음부터 API 서버에 지연이 생기고 Head 플러그인으로 상태를 볼 수 없게 되었다. 재배치가 계속 돌아 클러스터가 불안정해진 것이었다. 작업을 멈췄다가 다시 1번 노드부터 전체 재시작하자 지연과 헬스 체크는 정상으로 돌아왔지만, 프라이머리를 포함한 샤드 12개가 unassigned 로 남아 데이터 일부를 잃은 상태가 되었다.
팀은 allocate 명령으로 샤드를 옮기는 데 실패하자 각 노드의 샤드 디렉터리와 _state 의 상태 파일을 직접 살폈다. 파일이 남은 노드의 샤드 파일을 다른 노드로 복사하고, 상태 파일의 "primary" 값을 고쳐 한 노드만 프라이머리로 두었다. B 인덱스는 lucene-core 로 인덱스를 검사해 망가진 쪽을 지우고 정상인 쪽을 복사했다. 프로세스를 다시 띄운 뒤 Head 플러그인으로 샤드가 클러스터에 돌아온 것을 확인했다.
- 심각도
- –
- 영향 범위
- 12 shards of the Elasticsearch cluster behind an Ameba user service stayed unassigned and part of the data was unavailable until the shard files were repaired by hand; the API servers were slow during the restarts
- 영향 사용자
- –
- 지역
- –
- 감지까지
- –
- 복구까지
- –
증상
OpenStack 호스트 서버가 다운되어 ElasticSearch 노드 하나가 내려간 뒤, API 서버 쪽 ElasticSearch 헬스 체크가 false 를 반환했다. Head 플러그인으로 본 클러스터 상태는 그린이었다.
노드를 다른 호스트로 옮겨 다시 클러스터에 넣은 뒤에도 클러스터 상태는 그린이었지만 API 서버 쪽 헬스 체크는 계속 false 였다.
1번 노드부터 차례로 전체 재시작을 하던 중 스무 번째 노드까지 재시작했을 즈음부터 API 서버 쪽에 지연이 생겼고, Head 플러그인으로 ElasticSearch 상태를 볼 수 없게 되었다. 샤드 재배치가 계속 돌면서 클러스터가 불안정해졌다.
다시 전체 재시작을 하자 Head 플러그인이 돌아오고 API 서버 지연이 풀렸으며 헬스 체크도 true 가 되었다. 그러나 샤드 12개가 클러스터로 돌아오지 않았고, 프라이머리 샤드까지 빠져 데이터 일부를 읽을 수 없었다.
_cluster/reroute 의 allocate 명령으로 unassigned 샤드를 옮겨 보려 했지만 오류가 나서 옮기지 못했다.
원인
원인 운영 실수
- 촉발
- OpenStack 호스트 서버가 다운되어 30대짜리 ElasticSearch 클러스터의 노드 하나가 내려갔다.
감지
API 서버가 ElasticSearch 에 하는 헬스 체크가 false 를 반환했다. 클러스터 상태는 Head 플러그인에서 그린으로 보였다.
조치
- 호스트가 언제 다시 다운될지 몰라, 복구된 ElasticSearch 노드 서버를 다른 호스트 서버로 마이그레이션했다.
- 마이그레이션 뒤 ElasticSearch 프로세스를 띄워 노드를 클러스터에 다시 넣었다.
- 헬스 체크를 되살리려고 1번 노드부터 로그를 보며 차례로 전체 재시작했지만, 스무 번째 노드까지 재시작했을 즈음 API 서버 지연이 생겨 멈췄다.
- 다시 1번 노드부터 차례로 전체 재시작해 API 서버 지연과 헬스 체크를 되살렸다.
- _cluster/reroute 의 allocate 명령으로 unassigned 샤드를 옮기려 했지만 오류가 나서 실패했다.
- A 인덱스의 53번 샤드는 22번 노드에만 파일이 남아 있었다. 그래서 프로세스를 멈추고 그 파일을 디렉터리만 남은 3번 노드와 10번 노드로 복사한 뒤, 22번 노드의 상태 파일만 "primary" 를 true 로 바꿨다.
- B 인덱스의 51번 샤드는 lucene-core 검사로 27번 노드 쪽이 망가진 것을 확인했다. 27번 노드의 파일을 지운 뒤 28번 노드의 파일을 27번 노드와 다른 노드로 복사하고, 28번 노드만 "primary" 를 true 로 바꿨다.
- C 인덱스의 2번 샤드는 27번 노드에만, 4번 샤드는 25번 노드에만 파일이 남아 있었다. 그래서 각 파일을 다른 두 노드로 복사하고, 복사한 쪽의 상태 파일은 "primary" 를 false 로 바꿨다.
후속 조치
- 오래된 ElasticSearch 버전을 그만 쓰고 버전을 올린다.
교훈
재시작할 때는 자동 재배치를 끄고 작업해야 했다. 노드 하나를 재시작한 뒤에는 로그만 볼 것이 아니라 재배치가 완전히 끝나는 것을 보고 나서 다음 노드를 재시작해야 했다.
출처
관련 사례
IL-0197Elasticsearch StatefulSet 을 새 퍼시스턴트 볼륨으로 옮기던 중 빈 볼륨으로 다시 뜬 elasticsearch-0 이 혼자 새 클러스터를 만들어 스플릿 브레인이 났다.Palark · 2021같은 원인 · 공통 Elasticsearch 와 OpenSearch
내 클러스터에도 있는지 확인하기
AI 도구와 스크립트용
- 이 사례의 JSON: https://incidentlib-api.benchlet.com/cases/IL-0434
- 가장 비슷한 사례들: https://incidentlib-api.benchlet.com/cases/IL-0434/similar
- MCP 서버(Streamable HTTP, 키 없음):
https://incidentlib-mcp.benchlet.com/mcp
API 는 키가 필요 없습니다. MCP 서버는 search_incidents, get_incident, list_incidents, get_topic, vocab 도구를 내놓습니다. 사용법은 API 와 MCP.