jongkwan.dev
개발 · Essay №068

Elasticsearch 운영과 성능 최적화

JVM 힙, 색인 처리량, 필터 캐시, 인덱스 수명 주기를 기준으로 보는 Elasticsearch 운영 점검표

이종관2026년 3월 26일12 min read
Contents

검색 알고리즘보다 메모리, 디스크, 세그먼트, 보존 정책을 다루는 일이 Elasticsearch 운영의 대부분을 차지합니다.

운영이 다루는 자원

Elasticsearch는 Apache Lucene 기반의 분산 검색·분석 엔진입니다. 운영에서 응답 시간을 가르는 것은 검색 기능보다 자원 배분입니다. JVM 힙과 파일 시스템 캐시의 균형, 샤드 수, 인덱스 수명 주기 정책이 성능을 좌우합니다.

기본 설정만으로 클러스터를 방치하면 적재량이 늘수록 문제가 드러납니다. 잦은 OOM(Out of Memory) 종료, 가비지 컬렉션(GC, Garbage Collection)으로 인한 멈춤, 색인 지연이 대표적인 증상입니다. 그래서 운영은 힙, 캐시, 세그먼트, 보존을 함께 점검해야 합니다.

JVM 힙과 파일 시스템 캐시

힙 메모리는 너무 작아도, 너무 커도 문제를 만듭니다. 검색 엔진 데몬이 JVM 위에서 돌기 때문에 힙 크기가 안정성의 출발점입니다.

기준은 두 가지입니다. 첫째는 50% 룰입니다. 물리 RAM의 절반 이하만 힙(-Xms, -Xmx)에 할당하고, 나머지 절반은 운영체제가 파일 시스템 캐시로 쓰게 둡니다. 이 캐시가 Lucene 세그먼트를 디스크에서 읽는 속도를 결정하므로 검색 지연에 직접 영향을 줍니다.

둘째는 32GB 경계입니다. 힙을 32GB 부근 이상으로 키우면 JVM이 압축된 객체 포인터(Compressed Object Pointers)를 잃고 64비트 포인터를 씁니다. 이때 객체 메모리 오버헤드가 약 1.5배 늘어, 힙을 키웠는데 가용 객체 수가 오히려 줄 수 있습니다(Elasticsearch 공식 권고).

GC는 Elasticsearch 7 이상에서 멈춤 시간이 짧은 G1GC(Garbage First Garbage Collector)가 기본값입니다. 운영 중 힙이 동적으로 늘었다 줄면 무거운 멈춤이 생기므로, 최소 힙(-Xms)과 최대 힙(-Xmx)을 같은 값으로 고정합니다.

text
# jvm.options
-Xms16g
-Xmx16g

힙 사용량이 한계에 가까워지면 Elasticsearch는 서킷 브레이커로 위험한 요청을 미리 거부합니다. 이는 노드가 OOM으로 죽는 것보다 일부 요청 실패를 택하는 보호 장치입니다.

색인 성능과 refresh interval

대량 색인(bulk) API로 데이터를 밀어 넣을 때는 실시간 검색 가시성을 잠시 양보합니다. refresh는 새로 들어온 문서를 검색 가능한 상태로 만드는 작업이고, 기본 주기는 1초입니다.

refresh가 잦으면 작은 세그먼트가 계속 생기고, 백그라운드 세그먼트 병합(Merge) 비용이 커집니다. 그래서 대량 적재 구간에서는 refresh 주기를 늘리거나 끄고, 복제본(replica) 수를 0으로 낮춰 쓰기 부하를 줄입니다.

json
PUT /logs-2026.06/_settings
{
  "index": {
    "refresh_interval": "30s",
    "number_of_replicas": 0
  }
}

적재가 끝나면 refresh 주기와 복제본 수를 운영값으로 되돌려 검색 가시성과 가용성을 회복합니다. 한 인덱스에 대량 적재와 실시간 검색을 동시에 극단적으로 요구하면 둘 다 나빠지므로, 워크로드를 시간대나 인덱스로 분리하는 편이 안전합니다.

검색 성능과 필터 컨텍스트

질의는 점수가 필요한 쿼리 컨텍스트와 예/아니오만 판정하는 필터 컨텍스트로 나뉩니다. 이 구분이 검색 지연을 가르는 핵심입니다.

필터 컨텍스트는 관련도 점수 계산을 건너뛰어 CPU 소모가 적습니다. 게다가 필터 결과는 노드의 비트셋(Bitset) 캐시에 저장되어 같은 조건을 다시 만나면 즉시 재사용됩니다. 상태값이나 날짜 범위처럼 점수가 필요 없는 조건은 bool 쿼리의 filter 절에 두는 것이 성능 최적화의 제1원칙입니다.

json
GET /products/_search
{
  "query": {
    "bool": {
      "must":   [ { "match": { "title": "무선 이어폰" } } ],
      "filter": [
        { "term":  { "status": "active" } },
        { "range": { "price": { "lte": 100000 } } }
      ]
    }
  }
}

위 질의에서 텍스트 적합도는 must 절이 계산하고, 상태와 가격 조건은 filter 절이 캐시와 함께 처리합니다. 점수가 필요 없는 조건을 must에 넣으면 매 검색마다 불필요한 계산이 붙으므로 분리해 둡니다.

인덱스 수명 주기 관리(ILM)

로그나 감사 기록 같은 시계열 데이터는 매일 쌓이고 오래 보관해야 합니다. 인덱스 수명 주기 관리(ILM, Index Lifecycle Management)는 데이터의 나이에 따라 하드웨어 등급을 바꿔 비용과 검색 속도를 함께 맞춥니다.

  • Hot: 빠른 디스크에서 쓰기와 잦은 검색을 받습니다
  • Warm: 쓰기를 멈추고 검색 위주로 비용이 낮은 노드에 둡니다
  • Cold: 드물게 조회하는 장기 보관 구간입니다
  • Delete: 보존 기간이 끝나면 자동으로 삭제합니다

수동 이동은 사실상 불가능하므로 노드 속성(예: node.attr.box_type: hot)을 태깅하고 ILM 정책으로 자동화합니다. 예를 들어 인덱스가 30GB를 넘거나 생성 후 7일이 지나면 롤오버(Rollover)로 새 Hot 인덱스에 쓰기를 넘기고, 90일이 지나면 삭제하도록 정책을 짭니다. 이 30GB, 7일, 90일은 예시 정책값이며, 보존 규제와 적재량에 맞춰 정합니다.

운영체제 커널 튜닝

Elasticsearch는 JVM 위에서 돌지만 안정성은 운영체제 커널 설정에도 달려 있습니다. 아래 값은 Elasticsearch 공식 가이드가 프로덕션에서 권고하는 기준입니다.

항목흔한 기본값권고값이유
vm.max_map_count65530262144 이상Lucene의 mmap 세그먼트 매핑 한도 확보
vm.swappiness배포판 기본1힙 페이지가 디스크로 스왑되는 멈춤 방지
ulimit -n1024~409665536 이상세그먼트 병합·검색의 파일 핸들 확보

스와핑은 가장 눈에 안 띄는 성능 저하 원인입니다. 힙 페이지가 느린 디스크로 밀려나면 짧아야 할 GC 사이클이 디스크 입출력을 기다리며 수백 밀리초 멈출 수 있습니다. 이를 막으려면 swappiness를 1로 두고 힙을 RAM에 고정합니다.

yaml
# elasticsearch.yml
bootstrap.memory_lock: true

vm.swappiness를 0으로 두는 것은 권장하지 않습니다. 리눅스 커널의 OOM 킬러를 자극할 수 있어 1이 안전한 하한입니다.

장애 신호와 점검 항목

운영 악화는 대개 같은 패턴으로 옵니다. 아래 신호가 보이면 원인을 좁혀 들어갑니다.

  • 매핑 폭증: 동적 매핑으로 필드가 무한히 늘면 클러스터 상태 메타데이터가 비대해집니다
  • 과도한 샤드: 샤드와 세그먼트가 많을수록 메타데이터 관리 비용이 커집니다
  • 무분별한 집계: 카디널리티가 큰 필드의 집계는 힙을 빠르게 소진합니다
  • refresh·replica·ingest 비용: 색인이 느리면 이 세 값을 함께 들여다봅니다

집계나 색인이 갑자기 느려지면 먼저 힙 사용률과 GC 빈도, 세그먼트 수를 확인합니다. 단일 인덱스에 대량 적재와 실시간 검색을 동시에 몰면 둘 다 나빠지므로, 충돌하는 워크로드는 인덱스로 갈라 둡니다.

정리

Elasticsearch 운영은 힙, 색인, 검색, 보존이라는 네 축을 같은 손으로 잡는 일입니다. 힙은 RAM의 절반 이하와 32GB 경계를 지키고 -Xms와 -Xmx를 고정하며, 대량 색인 구간에서는 refresh 주기와 복제본 수를 조절해 쓰기 부하를 낮춥니다. 검색에서는 점수가 필요 없는 조건을 filter 절로 옮겨 비트셋 캐시를 살리고, 시계열 데이터는 ILM으로 Hot에서 Delete까지 자동으로 흘려보냅니다.

여기 적은 50% 룰, 32GB, 권고 커널값은 Elasticsearch 공식 가이드 기준입니다. 30GB·7일·90일 같은 롤오버 수치는 예시이므로 각자의 적재량과 보존 규제에 맞춰 조정하면 됩니다.