검색 로그를 ES에 쌓기 시작한 지 6개월. NVMe SSD 클러스터 용량이 80%를 넘겼다. 증설 견적을 받아보니 매달 나가는 돈이 부담스러웠다. 그런데 막상 검색 트래픽의 95%는 최근 7일 데이터에 몰려 있었다. 6개월 전 로그는 거의 안 보는데 제일 비싼 SSD를 차지하고 있었다.
모든 데이터를 같은 하드웨어에 두는 게 문제였다. 데이터에도 온도가 있다.
시계열 데이터를 다 SSD에 두면 어떻게 되나?
주문, 검색 로그, 노출 로그 같은 데이터는 시간이 지나면 거의 안 본다. 그런데 ES 인덱스에 계속 쌓으면 오래된 데이터도 최신 데이터와 똑같이 비싼 SSD를 차지한다.
데이터량은 시간에 비례해 선형으로 늘고, 비용도 같이 늘어난다. 반면 검색 가치는 시간이 지날수록 급격히 떨어진다. 이 불일치가 비용 낭비의 원인이다.
해결의 핵심: 데이터 온도에 따라 다른 하드웨어에 배치한다.
데이터 온도 티어란?
ES는 데이터를 네 단계 온도로 나눈다. 노드에 역할(data_hot, data_warm, data_cold, data_frozen)을 부여하고, 인덱스가 식어가면서 티어 사이를 이동한다.
| 티어 | 특성 | 하드웨어 | 보관 예시 |
|---|---|---|---|
| Hot | 색인 활발 + 검색 빈번 | NVMe SSD, 고사양 | 최근 1~7일 |
| Warm | 색인 끝, 검색 가끔 | SSD/빠른 HDD | 7~30일 |
| Cold | 거의 안 봄, 가끔 조회 | HDD, 저사양, 레플리카↓ | 1~3개월 |
| Frozen | 거의 안 봄, 느려도 됨 | 오브젝트 스토리지(S3) | 규정상 장기 보관 |
핫은 빠르고 비싸고, 프로즌은 느리고 싸다. 데이터를 가치에 맞는 곳에 둔다.
ILM이 뭘 자동화하나?
이 티어 이동을 사람이 손으로 할 순 없다. ILM(Index Lifecycle Management) 이 인덱스의 생애주기를 자동으로 관리한다.
[Hot] rollover — 크기 50GB 또는 1일 도달 시 새 인덱스 생성
│
[Warm] shrink(샤드 수 축소) + force_merge(세그먼트 병합)
│ 레플리카 조정 후 warm 노드로 이동
│
[Cold] cold 노드로 이동, 레플리카 줄이거나 searchable snapshot
│
[Frozen] S3 searchable snapshot으로 전환 (로컬 디스크 거의 안 씀)
│
[Delete] 보관 기간(예: 90일) 지나면 인덱스 삭제
정책을 한 번 정의해두면 인덱스가 나이를 먹으며 자동으로 단계를 밟는다.
rollover가 뭔가?
핵심 메커니즘이다. 하나의 거대한 인덱스에 계속 쓰는 대신, 현재 쓰기 인덱스가 일정 크기/나이에 도달하면 새 인덱스로 갈아탄다.
orders-000001 (꽉 참) → orders-000002 (새 쓰기 대상)
↑ write alias "orders"가 항상 최신을 가리킴
애플리케이션은 항상 orders라는 쓰기 alias로만 쓴다. 뒤에서 ES가 실제 인덱스를 바꿔준다. 이렇게 하면 인덱스가 적당한 크기로 유지되고, 티어 이동과 삭제를 인덱스 단위로 할 수 있다.
force_merge와 shrink를 핫에 하면 왜 안 되나?
- shrink: 핫일 때는 색인 병렬도를 위해 샤드를 여러 개 뒀다가, 식으면 검색 오버헤드를 줄이려고 샤드 수를 줄인다.
- force_merge: 세그먼트를 하나로 병합해 삭제 문서를 제거하고 읽기를 최적화한다.
둘 다 색인이 끝나 더 이상 안 변하는 인덱스에 해야 한다. 아직 색인 중인 핫 인덱스에 force_merge를 하면, 새로 들어오는 문서가 또 세그먼트를 만들어 의미가 없고 리소스만 낭비된다. 그래서 ILM은 warm 단계에서 이걸 실행한다.
오래된 데이터 삭제는 왜 인덱스 단위인가?
RDB라면 DELETE WHERE created_at < ...로 지운다. ES에서 이렇게 행 단위로 지우면 끔찍하다. 세그먼트가 불변이라 삭제는 “삭제 마킹”일 뿐이고, 실제 공간 회수는 머지 때까지 안 된다. 대량 삭제는 머지 폭풍을 부른다.
대신 시간 단위 인덱스(orders-2026.06.28)로 쪼개두면, 보관 기간이 지난 인덱스를 통째로 drop하면 된다. 인덱스 삭제는 파일을 지우는 것에 가까워 즉각적이고 싸다. 이게 시계열 데이터를 시간 단위 인덱스로 관리하는 큰 이유다.
요즘은 이 패턴을 Data Stream(append-only 시계열 추상화)으로 자동화한다. rollover와 백킹 인덱스 관리를 ES가 알아서 해준다.
Frozen과 Searchable Snapshot
가장 차가운 단계다. 거의 안 보지만 규정상 1년, 3년씩 보관해야 하는 데이터가 있다.
Searchable Snapshot은 데이터를 S3 같은 오브젝트 스토리지에 스냅샷으로 두고도 검색이 가능하게 한다. 로컬 디스크에는 거의 안 두고 필요할 때만 S3에서 캐시로 끌어온다. 검색은 느리지만, 디스크 비용이 급감한다. “안 보지만 버릴 순 없는” 데이터에 적합하다.
Shard Allocation 제어
티어 배치를 강제하려면 샤드가 어느 노드로 갈지 제어해야 한다.
- Allocation filtering:
index.routing.allocation.require._tier_preference: data_warm식으로 특정 티어 노드에만 배치. - Allocation awareness: 랙/AZ 단위로 레플리카를 분산해 한 랙이 죽어도 데이터가 살아있게.
이커머스 적용 예
데이터 성격에 따라 전략이 다르다.
- 상품 카탈로그: 변경 적고 검색 폭주 → 항상 Hot 유지. ILM보다 레플리카 확장과 캐시가 중요.
- 주문 / 검색 로그 / 노출 로그: 시계열, 최근만 뜨거움 → Hot→Warm→Cold→Frozen→Delete ILM. 예를 들어 7일 Hot, 30일 Warm, 90일 Cold, 1년 Frozen, 그 후 삭제.
비용 관점의 한 줄 정리: 전 데이터를 SSD에 두면 비용이 데이터량에 선형으로 증가한다. ILM으로 식은 데이터를 HDD와 S3로 내리면, 검색 성능은 최근 데이터에 집중되고 비용은 크게 절감된다.
운영상 주의사항
ILM 정책은 한 번 잘못 설정하면 데이터가 의도치 않게 삭제되거나 엉뚱한 티어에 쌓인다. 정책 변경은 신규 인덱스부터 적용되므로, 기존 인덱스에 소급 적용하려면 명시적으로 정책을 다시 붙여야 한다. rollover 조건(크기 vs 나이)은 트래픽 패턴에 맞춰야 하고, 너무 잦은 rollover는 인덱스/샤드 수를 폭증시켜 클러스터 메타데이터 부담을 키운다. 삭제 단계는 특히 신중하게 — 한 번 지운 인덱스는 스냅샷이 없으면 복구 불가다.