Elasticsearch 입문 5편. Index 관리 핵심 — 생성·설정·Alias·Reindex·Template. 운영에서 alias 만 노출, zero-downtime 재색인 패턴.
이 글은 Elasticsearch 입문에서 운영까지 시리즈 38편 중 5편이에요. 1편에서 Index·Document·Shard·Replica·Mapping 다섯 단어로 ES 의 전체 그림을 그렸고, 2~4편에서 핵심 개념·Lucene 내부·로컬 quickstart 까지 한 호흡으로 훑었다면 — 5편부터는 손에 잡히는 운영 으로 내려가요. 그 첫 자리가 Index 관리.
이 글은 Elasticsearch 8.x 공식 docs(elastic.co/docs) 의 Index APIs · Index Templates · Reindex API 문서를 한국어 학습 노트로 풀어쓴 자료예요.
아래 curl·DSL 예시는 1편 quickstart 환경(Docker ES + Kibana Dev Tools) 에서 그대로 붙여 넣어 실행되도록 정리했어요.
인덱스가 한 번 생기면 변경이 까다로워서
Elasticsearch 운영을 처음 시작하면 가장 빨리 후회하는 순간 이 인덱스 생성 단계 에서 나와요. shard 갯수를 잘못 잡았다, mapping 에 빠진 필드가 있다, analyzer 를 한국어로 안 깔았다 — 셋 중 하나만 걸려도 운영 중 인덱스를 다시 만들어서 데이터를 옮겨야 해요. RDBMS 의 ALTER TABLE 처럼 부드럽게 안 풀려요.
이유는 한 줄이에요. ES 인덱스는 분산 시스템 이라 Primary Shard 갯수·analyzer·필드 타입 같은 코어 설정이 인덱스 생성 시점에 노드 단위로 고정 돼요. 한 번 박힌 뒤로는 split·shrink 같은 특수 API 로 일부만 손볼 수 있고, 그마저도 데이터 재배치 비용 이 막대해요.
그래서 운영에선 생성 단계에서 한 번에 잘 박는 것 + 갈아엎을 때 무중단으로 갈아엎는 패턴 두 가지를 함께 잡아야 해요. 전자가 Settings·Mappings·Templates 자리고, 후자가 Alias·Reindex 자리예요. 이번 5편이 그 두 자리를 한 통에 정리해요.
Index 생성 — PUT /my-index 기본형
가장 단순한 인덱스 생성은 한 줄이에요.
PUT /my-index
이렇게 호출하면 ES 가 기본 설정 으로 인덱스를 만들어요. 8.x 기본값은 Primary Shard 1 · Replica 1 이고, mapping 은 동적 매핑(Dynamic Mapping) ON — 들어오는 문서의 JSON 키를 그대로 필드로 만들어요. 학습·테스트엔 OK 하지만, 운영에선 반드시 settings + mappings 를 함께 박아서 만들어요.
운영에서 자주 쓰는 풀 형태는 이래요.
PUT /products-v1
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1,
"refresh_interval": "5s",
"analysis": {
"analyzer": {
"korean_nori": {
"type": "custom",
"tokenizer": "nori_tokenizer"
}
}
}
},
"mappings": {
"dynamic": "strict",
"properties": {
"name": { "type": "text", "analyzer": "korean_nori" },
"price": { "type": "long" },
"category": { "type": "keyword" },
"created_at": { "type": "date" },
"description": { "type": "text", "analyzer": "korean_nori" }
}
}
}
여기에 박힌 결정이 네 가지예요 — shard 3개로 분산 · replica 1개로 HA · refresh 5초로 색인 부하 완화 · 한국어 nori_tokenizer. 운영에서 후회하는 사고 70%가 이 네 줄을 기본값으로 놔둔 데서 나와요.
인덱스 이름에 -v1 같은 버전 접미사를 붙이는 게 운영 표준이에요. 뒤에서 다룰 alias + reindex 패턴이 실제 인덱스를 갈아끼우는 동안 앱은 alias 로만 본다 는 전제로 돌기 때문에, 실제 인덱스는 언제든 버려질 수 있는 이름 으로 만들어요.
Index Settings — static vs dynamic
ES 인덱스 설정은 크게 두 갈래예요.
Static settings 은 인덱스 생성 시점에만 박을 수 있는 설정이에요. 대표적으로 number_of_shards, index.codec, analysis.* 가 여기 속해요. 한 번 만들면 변경 불가 — 바꾸려면 새 인덱스 만들고 reindex 가 유일한 길.
Dynamic settings 은 운영 중에도 PUT /my-index/_settings 로 바꿀 수 있는 설정이에요. 대표적으로 number_of_replicas, refresh_interval, index.max_result_window 가 여기 속해요. 부하 패턴이 바뀌면 온라인으로 튜닝 가능해요.
운영에서 가장 자주 만지는 다섯 가지만 짚을게요.
number_of_shards — Primary Shard 갯수. 한 번 박으면 변경 부담 큼 (split·shrink 로 특수 변경은 가능). 1편의 Shard 미설계 사고 가 이 자리. 거친 가이드는 데이터 1TB → 20~30개, 데이터 < 50GB → 1~3개. 작성 시점(2026-05-19) 8.x 기준 한 샤드 권장 사이즈는 10~50GB.
number_of_replicas — Replica 갯수. 운영 중 자유롭게 변경 가능. 기본 1 (= 데이터 두 벌). 읽기 부하가 폭증하면 replica 를 2~3 으로 올려 읽기 노드를 늘리는 패턴이 일반.
refresh_interval — 색인된 문서가 검색 가능해지는 주기. 기본 1초 (= near real-time). 대량 색인 작업 직전엔 "-1" (= refresh OFF) 으로 끄고, 작업 끝난 뒤 "1s" 로 돌리면 색인 속도가 2~5배 빨라져요. 23편(Bulk API) 에서 다시.
index.max_result_window — from + size 의 합 한도. 기본 10,000. 즉 10,001번째 결과부터는 에러. 운영에서 Deep Pagination 폭망 사고 가 이 자리. 늘리지 말고 search_after 로 가야 해요. 19편에서 깊이.
index.mapping.total_fields.limit — 인덱스 안 총 필드 한도. 기본 1,000. 동적 매핑이 켜져 있을 때 Mapping Explosion 사고 의 안전망. 8편에서 다시.
설정을 운영 중 바꿀 땐 이렇게 호출해요.
PUT /products-v1/_settings
{
"index": {
"number_of_replicas": 2,
"refresh_interval": "10s"
}
}
Alias — 무엇이고 왜 운영에선 alias 만 노출하는가
Alias 는 하나 이상의 실제 인덱스를 가리키는 별명 이에요. RDBMS 의 VIEW 와 비슷한 자리. 앱 코드는 products 라는 alias 만 알고, 그 alias 가 실제로는 products-v1 또는 products-v2 를 가리켜요.
운영에서 alias 만 노출하는 이유는 한 줄이에요 — 인덱스를 갈아엎어도 앱은 코드 변경 없이 그대로 동작 해야 하기 때문. mapping 을 바꾸고 싶다, shard 갯수를 바꾸고 싶다, analyzer 를 추가하고 싶다 — 이 모든 작업이 새 인덱스 + reindex + alias 스위치 패턴으로 풀려요.
alias 를 만드는 방법은 두 가지예요.
POST /_aliases
{
"actions": [
{ "add": { "index": "products-v1", "alias": "products" } }
]
}
또는 인덱스 생성 시점에 같이 박는 방법.
PUT /products-v1
{
"settings": { "number_of_shards": 3 },
"mappings": { "properties": { "name": { "type": "text" } } },
"aliases": {
"products": {}
}
}
alias 의 핵심 action API 는 세 가지예요 — add · remove · is_write_index. 특히 zero-downtime 재색인 의 본체가 이 셋의 원자적 조합 이에요.
POST /_aliases
{
"actions": [
{ "remove": { "index": "products-v1", "alias": "products" } },
{ "add": { "index": "products-v2", "alias": "products" } }
]
}
이 한 번의 호출이 원자적 으로 실행돼요. 즉 어느 한 순간도 alias 가 양쪽을 동시에 가리키거나 어느 쪽도 가리키지 않는 상태가 없어요. 앱은 다음 요청부터 자연스럽게 v2 를 보게 돼요.
is_write_index 는 한 alias 가 여러 인덱스를 동시에 가리킬 때 쓰는 옵션이에요. 어느 인덱스로 쓰기 가 들어갈지 한 곳을 명시해요. 6편의 ILM Rollover 패턴에서 핵심 자리.
POST /_aliases
{
"actions": [
{ "add": { "index": "logs-2026-05-18", "alias": "logs", "is_write_index": false } },
{ "add": { "index": "logs-2026-05-19", "alias": "logs", "is_write_index": true } }
]
}
Reindex — _reindex API 와 zero-downtime 패턴
mapping 을 바꾸고 싶다, shard 갯수를 바꾸고 싶다, analyzer 를 새로 깔고 싶다 — 이 모든 작업이 기존 인덱스를 그대로 두고 새 인덱스를 만들어 데이터를 옮기는 Reindex 로 풀려요. ES 가 공식 API 로 _reindex 를 제공해요.
기본 형태는 이래요.
POST /_reindex
{
"source": { "index": "products-v1" },
"dest": { "index": "products-v2" }
}
이 호출이 동기 로 끝날 때까지 기다리면 대형 인덱스에선 수 시간~수십 시간 이 걸려요. 운영에선 비동기 로 띄우고 task 로 추적해요.
POST /_reindex?wait_for_completion=false
{
"source": { "index": "products-v1", "size": 5000 },
"dest": { "index": "products-v2" },
"conflicts": "proceed"
}
응답으로 받는 task_id 를 _tasks/<task_id> 로 폴링해서 진행률을 확인해요. size 가 한 번에 가져올 배치 크기, conflicts 가 충돌(버전 충돌·매핑 불일치) 처리 정책 이에요. proceed 로 두면 충돌 문서는 건너뛰고 끝까지 진행, 기본 abort 면 충돌 발견 즉시 중단.
대형 인덱스에선 scroll · slices · throttle 세 가지로 튜닝해요.
POST /_reindex?wait_for_completion=false&slices=auto
{
"source": {
"index": "products-v1",
"size": 5000,
"query": { "range": { "created_at": { "gte": "2026-01-01" } } }
},
"dest": { "index": "products-v2" },
"conflicts": "proceed"
}
slices=auto 는 ES 가 샤드 갯수에 맞춰 자동으로 병렬 스레드 를 띄워 줘요. 가장 자주 쓰는 옵션. source.query 로 부분 reindex 도 가능 — 최근 1년 데이터만 같은 패턴.
zero-downtime 재색인 패턴
alias + reindex 를 묶은 무중단 재색인 표준 흐름은 네 단계예요.
- 새 인덱스
products-v2를 원하는 새 settings·mappings 로 생성. _reindex로products-v1→products-v2데이터 복사 (수 시간 가능, 그동안 v1 은 계속 쓰기·읽기 OK).- reindex 완료 후, 증분 데이터 (reindex 시작 후 v1 에 들어온 신규·수정 문서) 를
source.query로 한 번 더 복사 — 작성 시점(2026-05-19) 기준 이중 쓰기 패턴을 함께 박는 게 안전. _aliases원자 스위치로 alias 를 v1 → v2 로 옮기고, v1 은 3~7일 후 삭제 (롤백 안전망).
이 패턴이 운영에서 mapping 변경·shard 재설계·analyzer 추가 의 거의 모든 경우를 다운타임 0 으로 풀어 줘요.
Index Templates — 자동 적용
인덱스를 수동으로 만드는 건 상품·사용자 같은 고정 인덱스 자리예요. 반대로 로그·메트릭 처럼 날짜별로 매일 새 인덱스가 생기는 자리에선 Index Template 으로 자동 적용 규칙 을 미리 박아 둬요.
8.0 이전엔 Legacy Index Template 한 종류였지만, 8.0+ 에선 Composable Index Template 으로 갈렸어요. 두 종류 모두 인덱스 이름 패턴이 매칭되면 자동 적용 되는 구조는 같아요.
PUT /_index_template/logs-template
{
"index_patterns": ["logs-*"],
"priority": 100,
"template": {
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1,
"refresh_interval": "10s"
},
"mappings": {
"dynamic": "strict",
"properties": {
"@timestamp": { "type": "date" },
"level": { "type": "keyword" },
"message": { "type": "text" }
}
},
"aliases": {
"logs": {}
}
}
}
이렇게 박아 두면 PUT /logs-2026-05-19 만 호출해도 자동으로 위 settings·mappings·aliases 가 적용돼요.
priority 는 여러 템플릿이 같은 인덱스에 매칭됐을 때 어느 게 이길지 결정해요. 숫자 클수록 이기고, 기본은 0. 6편(ILM)·25편(Logstash) 에서 다시.
Component Template (8.0+) 은 settings · mappings · aliases 를 재사용 가능한 조각 으로 쪼개 두는 방식이에요. 공통 한국어 analyzer 조각 을 한 번 만들어 두고, 여러 Index Template 이 그 조각을 조립 해서 쓰는 패턴.
PUT /_component_template/korean-analyzer
{
"template": {
"settings": {
"analysis": {
"analyzer": {
"korean_nori": {
"type": "custom",
"tokenizer": "nori_tokenizer"
}
}
}
}
}
}
PUT /_index_template/products-template
{
"index_patterns": ["products-*"],
"composed_of": ["korean-analyzer"],
"priority": 200,
"template": {
"settings": { "number_of_shards": 3 }
}
}
작성 시점(2026-05-19) 8.x 운영 표준은 Component Template 으로 공통 조각 + Composable Index Template 으로 조립 두 단계 패턴이에요. Legacy Template 은 8.x 에서도 동작은 하지만 deprecated 경로.
자주 만나는 사고
사고 1 — alias 없이 인덱스 이름 노출
원인 — 앱 코드가 products-v1 같은 실제 인덱스 이름 을 그대로 박아 둠. mapping 을 바꿔야 할 때 코드 변경 + 배포 + 재색인 이 한 묶음으로 묶여서 다운타임 없는 재색인 이 불가능해요.
해결 — 운영 인덱스는 처음부터 alias 만 노출 한다는 규칙을 박아요. 인덱스 생성 시 aliases 절로 alias 도 같이 만들고, 앱은 alias 로만 호출해요. 코드 리뷰 단계에서 인덱스 이름 직접 사용 을 막는 lint 룰 한 줄이 가장 싸요.
사고 2 — Reindex 중 dest mapping 불일치
원인 — _reindex 호출 시 dest 인덱스를 미리 만들지 않으면 ES 가 동적 매핑 으로 자동 생성해 버려요. 결과적으로 원본의 analyzer · 필드 타입 이 dest 에 그대로 안 옮겨져서 검색 품질이 깨져요.
해결 — _reindex 전에 dest 인덱스를 명시적으로 PUT 으로 먼저 만들어요. settings · mappings · aliases 를 전부 박은 다음 reindex 호출. 운영에선 dest 가 이미 존재하지 않으면 에러 가 나도록 script 단계에서 체크 까지 추가.
사고 3 — Index Template priority 충돌
원인 — logs-* 와 logs-app-* 두 패턴이 모두 logs-app-2026-05-19 에 매칭되는데, priority 가 같아서 어느 쪽이 이기는지 예측 불가. ES 가 경고만 띄우고 임의로 한쪽을 골라요.
해결 — Template priority 를 100 단위로 의도적으로 분리 해요. 공통 베이스 100, 도메인별 200, 특수 케이스 300 같은 규칙. _index_template/_simulate_index/<name> API 로 실제 매칭 결과를 미리 시뮬레이션 하는 단계를 CI 에 박아 둬요.
사고 4 — shard 갯수 만점 후 split 부담
원인 — Primary Shard = 1 로 시작했다가 데이터가 500GB 까지 자라서 split 필요. _split API 는 원본 shard 갯수의 정수배 (2·3·6·12...) 로만 가능하고, 원본 인덱스가 read-only 가 돼야 해요. 즉 split 중엔 쓰기 차단 — 사실상 다운타임.
해결 — 처음부터 데이터 1TB 예상이면 Primary Shard 20~30 으로 여유 있게 잡아요. 사이즈가 작아도 future-proof 차원에서 최소 3 으로 시작하는 게 운영 표준. 그래도 split 이 필요하면 alias + reindex 패턴이 훨씬 안전한 대안.
사고 5 — max_result_window 초과
원인 — 앱에서 페이지 1000번째까지 보여 주는 무한 스크롤 화면. from=10000&size=10 호출에서 ES 가 Result window is too large 에러를 던져요.
해결 — index.max_result_window 를 늘리지 마세요 (메모리 사용량이 폭증). 대신 search_after (정렬 키 기반 cursor) 또는 scroll (대량 export 용) 로 패턴을 갈아요. 19편(Search Features) 에서 깊이.
사고 6 — refresh_interval 운영 중 잠금
원인 — 대량 색인 작업 시작 시 refresh_interval 을 "-1" 로 꺼 두고, 작업 끝난 뒤 원복을 잊음. 결과적으로 몇 시간~며칠 동안 새 문서가 검색에 안 잡혀서 사용자 컴플레인 폭증.
해결 — bulk 색인 스크립트의 try-finally 블록에서 예외 발생해도 원복 되도록 박아요. 또는 작업 시작 시 refresh OFF + 끝나면 _refresh 한 번 명시 호출 + interval 원복 3단계를 한 함수로 감싸요.
사고 7 — alias 스위치 후 v1 즉시 삭제
원인 — 새 v2 인덱스로 alias 를 옮긴 직후 v1 을 바로 DELETE. v2 에 missing 한 데이터 나 mapping 버그 가 나중에 발견됐을 때 롤백 불가능. 백업 복구 외엔 길이 없음.
해결 — v1 은 최소 3~7일 보존 하는 운영 룰을 박아요. v2 가 완전히 안정 됐다고 확인한 뒤에야 삭제. 디스크가 부족하면 cold node 로 마이그레이션 해서 보관 비용을 낮춰요. 6편(ILM) 의 hot-warm-cold-frozen 패턴.
운영 권장 패턴
운영 인덱스는 처음부터 alias 로만 노출 합니다. 인덱스 생성 시점에 alias 를 같이 박고, 앱 코드는 alias 이름만 안다 는 규칙을 강제. 이 한 줄이 mapping 변경·shard 재설계·analyzer 추가 모두를 코드 변경 없는 zero-downtime 으로 풀어 줘요.
Settings 는 static 과 dynamic 의 구분 을 명시적으로 인지하고 박아요. 특히 number_of_shards 와 analyzer 는 생성 시점에 한 번에 결정해야 한다는 걸 머리에 박아 두고, mapping 도 strict + 명시적 으로 가져가세요. 1편의 Mapping Explosion 사고 의 70%가 여기서 사라져요.
Reindex 는 항상 zero-downtime 패턴 으로 가요. 새 인덱스 생성 → reindex → 증분 복사 → alias 원자 스위치 → 옛 인덱스 3~7일 보존. 동기 reindex 로 운영 인덱스를 갈아엎는 일은 학습·테스트 자리 에서만.
로그·메트릭처럼 날짜별로 자동 생성되는 인덱스는 Index Template + Component Template 으로 자동 적용 규칙을 박아 둡니다. priority 는 100 단위로 의도적으로 분리 하고, _simulate_index 로 실제 매칭을 CI 에서 미리 검증 하는 단계까지 추가.
운영 도구 표준 셋업으로 Kibana Dev Tools + _cat/indices?v · _cat/aliases?v · _cluster/health 세 가지를 항상 즐겨찾기에 박아 둬요. 인덱스 상태·alias 매핑·클러스터 헬스 셋이 가장 먼저 봐야 하는 자리. 30편(Monitoring) 에서 다시.
시험 직전 한 번 더 — 압축 노트
- Index 생성 =
PUT /<name>+ settings + mappings + aliases 한 번에. 운영은 기본값 금지. - Static settings = 생성 시점만. number_of_shards · analysis · codec.
- Dynamic settings = 운영 중 변경 가능. number_of_replicas · refresh_interval · max_result_window · mapping.total_fields.limit.
- refresh_interval = 기본 1초. 대량 색인 직전
"-1", 끝나면 원복. - max_result_window =
from + size한도 10,000. 늘리지 말고search_after로. - Alias = 별명. 운영은 alias 만 노출. RDBMS 의 VIEW 자리.
- alias actions = add · remove · is_write_index.
_aliases원자 호출로 zero-downtime 스위치. - Reindex =
_reindexAPI.wait_for_completion=false+slices=auto+conflicts=proceed가 운영 디폴트. - zero-downtime 재색인 = 새 인덱스 → reindex → 증분 → alias 원자 스위치 → 3~7일 보존.
- Index Template = 인덱스 이름 패턴 자동 적용.
priority큰 게 이김. 100 단위 분리. - Component Template (8.0+) = 재사용 조각. 공통 analyzer · 공통 mapping 을 조각으로 쪼개고, Composable Index Template 이 조립.
- 7대 사고 = alias 없이 인덱스 노출 · reindex dest mapping 불일치 · template priority 충돌 · shard split 부담 · max_result_window 초과 · refresh interval 원복 실패 · v1 즉시 삭제.
- 운영 즐겨찾기 =
_cat/indices?v · _cat/aliases?v · _cluster/health · _index_template/_simulate_index. - 인덱스 이름 규칙 =
<domain>-v<n>또는<domain>-<yyyy-MM-dd>. alias 가 논리 이름, 인덱스가 물리 자원.
시리즈 다른 편
- 이전 글 = 4편 Quickstart — Docker·curl·Kibana 첫 화면
- 다음 글 = 6편 ILM — Hot-Warm-Cold-Frozen · Rollover · Retention
- 7편 = Document CRUD — index·get·update·delete·bulk
- 8편 = Mapping Deep — Static·Dynamic·Multi-field·Runtime
- 9편 = Field Types — text·keyword·numeric·date·dense_vector
- 11편 = Korean Analyzer — Nori·mecab-ko·사용자 사전
- 23편 = Bulk API — _bulk·throughput·error handling
- 25편 = Logstash·Beats — 수집 파이프라인
- 32편 = Spring Data Elasticsearch — Repository·Template·POJO
한 줄 정리 — Elasticsearch 인덱스는 한 번 만들면 변경이 까다로워서 생성 단계가 결정적. 운영은 alias 로만 노출 + Index Template 자동 적용 + Reindex zero-downtime 패턴 세 가지 묶음이 표준이에요.