ElasticSearchDevOpsTroubleshooting

ElasticSearch Essential 핵심만 뽑아보기_ 운영·트러블슈팅 관점 요약

·18 min read

ElasticSearch가 뭔지 모르는 사람들을 위해

간단히 말해 오픈소스 검색 엔진이다.

정확히 이야기하면 순수 오픈소스는 아닌데, 중요한건 아니니 넘어가자

난 원래 Solr 검색엔진을 다뤘는데, 아쉽게도 실무에서 ElasticSearch를 써볼 기회가 없었다.

인프런 강의 낭낭한 김에 강의좀 듣고 ElasticSearch 운영 및 트러블슈팅 관점을 배워보자

클러스터링

컴퓨터에서 클러스터 개념 살펴보면 여러 대의 컴퓨터들이 하나의 시스템처럼 동작한다는 뜻이다.

ElasticSearch도 여러 대의 노드들이 각자 역할 하면서 하나의 시스템처럼 연결되서 동작이 가능하다.

즉 성능 딸리면 노드 늘려서 대응이 가능하다는 뜻이다.

노드의 역할

하나의 시스템 노드의 분산처리 하면 마치 공산주의가 떠오르지만 노드 내부적으로도 정의에 차이가 존재한다.

마스터 노드: 클러스터 상태 관리 및 메타데이터 관리(선출을 통해 1대가 완장차고 관리함)

데이터 노드: 문서 색인 및 검색 실제 수행(요청 처리도 하는데 사용자 접근은 가급적 LB를 통해 우회)

코디네이팅 노드: 검색 요청 처리(성능 딸리면 별도 엔드포인트로 분리해 수평 확장)

인제스트 노드: 색인되는 문서의 데이터 전처리/필드 정규화 파이프라인 실행(필요시에만)

마스터 노드 생각하면 애 하나 있고 관리하는거 같아 보이지만 마스터 후보 노드라고 지금 마스터 죽으면 대체될 다른 노드도 있다.

하나의 시스템 아래에서는 모두가 톱니바퀴다.

내가 아까 정의에 차이라고 했는데 재밌게도 어떤 노드에 어떤 요청을 하든 동일한 응답을 준다.

즉 검색 처리하는 코디네이팅 노드두고 마스터, 데이터, 인제스트 어디든 검색 요청 처리 날리면 동일하게 처리해서 준다는 거다.

애네들 정의는 그냥 말 그대로 정의일 뿐이고 실제 하는 일은 다 동일하다고 본다. 생각보다 더 평등하다.

인덱스·샤드·레플리카

하나의 시스템! 연결! 부하 분산!

다만 어떻게 처리되는가를 알려면 다음 개념을 알야한다.

인덱스: 문서가 저장 되는 논리적인 공간

인덱스를 잘 설계하는 것이 ElasticSearch 사용의 가장 첫 단계다.

예시로 도서관 자료 검색 시스템 만들때

library 하나에 모두 떄려넣기 VS book, magazine, media 등 분산해서 정의하기

참고로 기술에 정답이 없듯이 설계에 정답은 없다.

단지 그 상황에 맞는 선택이 있을 뿐

하나의 인덱스에 몽땅?

  • 장점: 관리 리소스가 적음

  • 단점: 쿼리와 문서의 구조가 복잡

여러개로 나눠서?

  • 각각의 경우 최적화된 쿼리와 문서의 구조 사용 가능

  • 관리 리소스 발생 및 필요시 데이터 통합해야함

아무튼 정답 없다고 하나로 시작해서 여러개로 나누는 것도 방법이다.

샤드(shard): 인덱스에 색인되는 문서가 저장되는 공간

참고로 하나의 인덱스는 반드시 하나 이상의 샤드를 가진다.

물리적인 문서가 저장되는 것이니 HDD 가지고 파티션 나눠서 저장하듯 생각하면 된다.

인덱스라는 논리 컨테이너 안에 샤드라는 물리 파티션이 존재하는 것이다.

샤드도 종류가 나뉘는데

  • 프라이머리 샤드: 문서가 저장되는 원본 샤드, 색인과 검색 성능에 모두 영향을 준다.

  • 레플리카 샤드: 프라이머리 샤드의 복제 샤드, 검색 성능에 영향을 줌(애는 색인을 하는게 아니라 복제를 하는거니깐)

데이터 복제해 출구 늘려서 성능만 뽑는 용도는 아니다. 프라이머리 죽으면 레플리카가 프라이머리 샤드로 승격된다.(와 신분상승!)

참고로 프라이머리와 레플리카는 비례한다.

PUT /library/_settings 
{
  “index”: { 
    “number_of_shards” : 5, 
    “number_of_replicas” : 2 
  }
} 

샤드는 5, 복제는 2 구성 = 프라이머리 5개와 복제 10개(5x2개) = 총 15개의 샤드

샤드는 한번 결정을 하면 바꾸기가 어려운데 바로 샤드 라우팅의 존재 때문이다.

문서가 어느 샤드에 들어가는지 결정하는 규칙으로

**Routing Rule = (문서의 ID) % (샤드의 개수)**로 정해진다.

즉 샤드 개수 바뀌면 규칙도 바뀌니깐 기존 데이터랑 충돌 일어난다.

색인 VS 검색

문서 분석하고 저장하는 과정을 색인이라고 하며, 저장된거 잘 가져오는게 검색 과정이다.

파이프라인 관점에서 보자면,

색인은 "데이터를 안전하고 빠르게 받아 디스크에 기록 -> 레플리카에 전파"가 핵심이다.

프라이머리 샤드에서 실제 기록이 일어나니 number_of_shards=1 설정이 실제 색인 성능과 직결한다.

검색은 "코디네이팅 노드가 관련 모든 샤드에 병렬 질의->취합/정렬 후 응답"의 흐음이다.

동시성 확장은 레플리카 수데이터 노드 수에 직결되어 있다.

색인 파이프라인(타임라인)
  1. (선택) 인제스트 파이프라인: 필요시 색인 직전 문서에 대한 전처리/정규화를 진행한다.

  2. 프라이머리 샤드 기록: 문서가 배정된 샤드에 먼저 쓰인다.(라우팅 규칙과 샤드 수 설계 기억하기)

  3. 레플리카 반영: 비동기 전파로 가용성 확보하고 검색에 대비한다.

  4. (관찰 포인트) 인덱싱 레이턴시/GC: 색인이 느려지면 먼저 인덱싱 레이턴시, GC 지표를 확인해 병목 원인을 좁혀야한다.

실무 팁: 트래픽이 간혈적을 폭증하는 경우(반짝 이벤트~) 데이터 노드를 증설하는 대신

Bulk 큐 사이즈 상향으로 write rejected를 0에 가깝게 만드는 전략이 효과적이다.

검색 파이프라인(타임라인)
  1. 진입: 클라이언트 -> 엔드포인트(보통 LB) -> 코디네이팅 노드

  2. 팬아웃: 관련 인덱스의 모든 샤드에 병렬 질의 전송

  3. 취합,정렬: 스코어/정렬 기준에 따라 머지-> 최종응답

  4. 용량 계획 공식: 검색량 증가 = 레플리카 증가(동일 쿼리를 더 많이 병렬 처리) + 필요시 데이터 노드 증가로 수평 확장

실무 팁: 검색이 잦은 서비스는 **검색 전용 엔드포인트(코디네이팅 전용)**를 따로 두면, 색인과 경합을 줄이고 확장이 쉬워짐

색인 vs 검색 운영 체크리스트
  • 색인 병목 점검

    • 최근 인덱싱 레이턴시 급증 여부 확인(임계치보다 “변화”를 우선) => 문서 보냈는데 저장이 느려지네? 디스크 I/O 성능 저하인가? 메모리 부족함? 특정 샤드가 부하걸렸나?

    • write rejected 증감/시간대 상관관계 확인(이벤트 피크인지) => "더이상 처리 못하니깐 다음에 다시 보내?" 일시적인 이벤트 피크야 아니면 처리 용량이 부족한거야?

    • Bulk 큐 사이즈와 동시성 재조정으로 스파이크 흡수 => 손님 감당 안되니 예약표 많이 뿌리고 동시에 일하는 작업자 늘려서 잠깐 버티자

    • 프라이머리 샤드 수가 너무 적어 단일 샤드에 부하가 몰리는지 => 과부하 샤드 지점을 핫스팟이라고 부르는데 "여기 핫한데?" 랑 비슷한 느낌

  • 검색 병목 점검

    • 레플리카 수가 검색 동시성에 충분한가. (레플리카↑ = 병렬 팬아웃 대상↑)

    • 데이터 노드 수/역할 분리가 적절한가(코디네이팅 분리) => 복잡한 집계 같은 쿼리는 데이터 노드보다 취합하는 코디네이팅 노드에서 부하가 더 걸림 역할을 적절하게 분리 필요

    • 상위 N 쿼리의 응답시간·집계 비용 급증 여부(레이턴시 트렌드로 초기 탐지) => 보통 상위 5%가 전체 문제에 절반 가까이 잡아먹는 경우도 봤음

장애 미니 시나리오!

시나리오 A) "검색이 갑자기 느려졌다"

가설: 검색 동시성 부족 or 특정 시간대 트래픽 스파이크

확인: 레플리카/데이터 노드 현황과 검색 레이턴시 추이 확인

조치: 레플리카 수 증가 -> 필요시 데이터 노드 확장

시나리오 B) "피크 떄 쓰기 오류가 난발(write rejected)"

가설: 순간 유입 폭증으로 write 큐 포화.

확인: 시간대별 write rejected 카운트 확인하고 유실 로그 양 확인

조치: Bulk 큐 사이즈 상향 및 생산자 배치 조정(데이터 보내는 쪽에서 너무 많이 보내지 않게 조절)

시나리오 C) "색인이 영 느리다"

  • 가설: 샤드 설계 미스(프라이머리 과소), GC/스토리지 병목

  • 확인: 샤드 수와 클러스터 활용도, GC rate/duration 변동 확인. => JVM 모니터링으로 GC 발생 빈도랑 소요 시간 확인 가능

  • 조치: 프라이머리 샤드 수 재설계(필요 시 재분배 전략 수립), GC 튜닝 전 쿼리/세그먼트 원인부터 차단. => GC는 복잡하니깐 간단한 거부터(보통 GC는 잘못 없음 최적화 잘 되어있음)

매핑 최소 가이드: text VS keyword

검색할때 보통 텍스트로 검색어로 찾는 경우와 태그로 정확하게 찾는 경우로 나뉜다.

text: 풀텍스트 검색용으로 **분석기(Analyzer)**로 토큰화(젠장 또 토큰화야)되어 역색인에 저장 -> 형태/단어 단위 검색에 유리

keyword: 정확 일치/집계/정렬/필터용 토큰화하지 않고 원문 그대로 색인

강조 표시 여러개 된거 보면 알겠지만 text를 어떻게 다루느냐가 검색엔진의 성능을 좌우한다.

keyword야 뭐 데이터 잘 전처리 했으면 그냥 검색하면 되는거다.

과잉 매핑 줄이기

ElasticSearch가 도큐먼트 구조를 기반으로 해서 postgresql과 어울리는데

문서 구조를 강제하지 않는 형제답게 애도 새로운거 들어오면 지가 알아서 매핑해주는 동적 매핑 기능이 있다.

동적 매핑을 켜두면 문자열 필드에 text가 생성되고, 자동으로 keyword 서브필드도 함께 생성되는 구성이 흔하다.

다만 편한 방법이 그리하듯이 불필요한 이중 색인이 일어날 수 있다.

불필요한 keyword가 붙으면 색인 비용이 한 번 더 발생한다.

집계/정렬이 전혀 없는 필드라면 제거하는 것이 이득이다.

정적 매핑으로 명시

  • “이 필드는 검색만 한다(집계·정렬 없음)” → text 남기기

  • “이 필드는 필터/집계/정렬만” → keyword 남기기

  • “둘 다 필요” → text + keyword 병행 (정말 필요한 필드에 한정)

데이터에 대한 토큰화 결과를 확인 가능한 Analyze API가 존재하는데

데이터 매핑 전 기대 토큰 목록이 검색/하이라이트/집계 의도와 맞는지 검토할 것을 권고한다.

미니 시나리오!

시나리오 A) “상품명으로 검색은 잘 되는데, 브랜드별 집계가 이상함”

  • 원인 후보: brand 필드가 text만 있고 keyword가 없음(토큰화된 값으로 집계되어 왜곡).

  • 해결: brandkeyword로 추가(또는 text + keyword). 재색인으로 집계 정상화.

시나리오 B) “URL이 중간에서 분해되어 검색/필터가 깨짐”

  • 원인 후보: URL을 text로 색인해 토큰화됨.

  • 해결: URL은 항상 keyword. 필요 시 normalizer만 최소 적용.

시나리오 C) “불필요한 .keyword 때문에 색인 비용↑”

  • 원인 후보: 동적 매핑 기본값으로 모든 문자열에 .keyword 부착.

  • 해결: 해당 필드에서 집계·정렬이 없다면 정적 매핑으로 keyword 제거.

노드 -> 인덱스 -> 샤드 -> 색인 vs 검색 -> 매핑을 운영 관점에서 살펴봤다.

이 다음에는 클러스터의 상태를 진단하기 위한 CAT API 치트시트가 존재하고

추가로 주요 모니터링 지표에 대한 내용이 있지만...

이거까지 다 문서에 담기는 어려워서 관심이 있다면 한번 보는걸 추천한다.

← Previous
LLM의 가치
Next →
시각 인공지능 입문