Elasticsearch 입문 27편 — Shard Allocation (allocation awareness·rack·zone·shrink·split·clone)

2026-05-19Elasticsearch 입문에서 운영까지

Elasticsearch 입문 27편 Shard Allocation. Awareness·Filtering·Watermark·Shrink·Split·Clone API.

📚 Elasticsearch 입문에서 운영까지 · 27편 — Shard Allocation (allocation awareness·rack·zone·shrink·split·clone)

이 글은 Elasticsearch 입문에서 운영까지 시리즈 38편 중 27편이에요. 26편(Cluster Operations) 에서 노드·마스터·rolling restart 같은 클러스터 라이프사이클 을 짚었다면, 27편은 한 단계 더 안쪽 — "그 노드들 위에 샤드를 어떻게 배치하는가" 라는 운영 깊이로 들어가요.

2편(Core Concepts) 에서 "인덱스가 샤드로 쪼개진다" 까지는 알았어요. 그런데 그 샤드가 어느 노드에 떨어지는지 는 운영자가 직접 결정하지 않으면, 어떤 날은 같은 랙(rack) 에 primary 와 replica 가 같이 떨어지고, 어떤 날은 디스크가 가득 찬 노드에 새 샤드가 또 들어와요. 이번 편이 잡는 자리.

📚 학습 노트

이 글은 Elasticsearch 8.x 공식 docs 의 Shard allocation 챕터를 한국어 학습 노트로 재구성한 자료예요.

실습은 도커로 3노드 클러스터를 띄우고 node.attr.zone 을 다르게 박은 뒤 awareness 를 켰다 껐다 해 보면 효과가 가장 빨리 박혀요.

Allocation 결정 — Shard Allocator 가 누구에게 줄지 정한다

샤드를 어느 노드에 둘지 정하는 주체는 클러스터 안의 Shard Allocator 예요. 마스터 노드가 들고 있는 컴포넌트고, 클러스터 상태 변화 가 있을 때마다 깨어나서 "지금 미할당 샤드가 어디 가야 가장 균형이 좋은가" 를 계산해요.

깨어나는 트리거가 네 가지 정도 있어요. 인덱스 생성 으로 새 primary 가 생기거나, replica 수 증가 로 새 replica 가 생기거나, 노드 추가·제거 로 기존 배치가 깨지거나, 노드 다운 으로 일부 샤드가 사라진 경우.

결정은 settings 우선순위 에 따라 단계적으로 걸러져요. 위에서부터 — cluster.routing.allocation.enableprimaries 만 허용이면 replica 후보 다 거르고, 그다음 filtering(include·exclude·require) 으로 노드 후보를 좁히고, 그다음 awareness 로 같은 zone 에 몰리지 않게 분산하고, 마지막에 disk watermark 로 디스크 여유가 부족한 노드를 빼요.

이 흐름이 막혀서 어떤 샤드가 어디에도 못 가는 상황이 생기면, 클러스터 상태가 yellow (replica 만 미할당) 또는 red (primary 미할당) 가 돼요. 그때 들고 가는 도구가 _cluster/allocation/explain API.

GET _cluster/allocation/explain
{
  "index": "products-2026.05.19",
  "shard": 0,
  "primary": true
}

응답에 "왜 이 노드에 못 가는가" 가 노드별로 한 줄씩 적혀요. disk watermark 초과·filtering 충돌·awareness 위반·max_shards_per_node 초과 같은 사유. 운영 중 가장 자주 들고 가는 진단 API 중 하나예요.

Allocation Awareness — 같은 rack·zone 에 primary·replica 가 몰리지 않게

Awareness 가 잡는 자리는 물리적 장애 도메인 분산 이에요. AWS 에서 availability zone 한 곳이 통째로 꺼지면, 그 zone 안의 모든 노드가 한꺼번에 사라져요. 이때 어떤 인덱스의 primary 와 replica 가 둘 다 그 zone 에 있었으면 — 데이터가 통째로 날아가요.

이걸 막는 표준 패턴이 Allocation Awareness. 노드마다 zone 속성 을 박고, "같은 zone 안에는 primary 와 replica 가 같이 못 들어간다" 는 규칙을 클러스터에 알려요.

node.attr 으로 속성 박기

각 노드의 elasticsearch.yml 에 임의의 키-값 속성을 박을 수 있어요. 키 이름은 자유.

# zone-a 노드들
node.attr.zone: zone-a
node.attr.rack: rack-1

# zone-b 노드들
node.attr.zone: zone-b
node.attr.rack: rack-2

# zone-c 노드들
node.attr.zone: zone-c
node.attr.rack: rack-3

여러 속성을 동시에 박아도 OK 예요. rack · zone · region · pod 처럼 여러 계층을 동시에 인식시킬 수 있어요.

cluster.routing.allocation.awareness.attributes 로 켜기

속성을 박는 것만으로는 효과가 X — 클러스터에 "이 속성을 awareness 기준으로 써라" 라고 알려야 해요.

PUT _cluster/settings
{
  "persistent": {
    "cluster.routing.allocation.awareness.attributes": "zone"
  }
}

이 한 줄을 박은 뒤로 "같은 zone 의 노드들끼리는 동일 샤드 그룹(primary + replica) 을 공유하지 않는다" 는 제약이 들어가요. Replica = 2 인 인덱스라면 zone-a · zone-b · zone-c 셋에 골고루 한 부씩 떨어지는 결과.

Forced Awareness — zone 하나가 죽었을 때 재배치 막기

여기서 한 단계 더 강한 모드가 있어요. Forced Awareness. 보통 모드 awareness 는 "zone 한 곳이 죽으면 남은 zone 들 안에서 replica 를 다시 만들어요" 가 기본 행동이에요. 그런데 zone 한 곳이 죽었을 때 남은 zone 의 디스크·CPU 가 원래 부하의 1.5배 로 부풀어서 같이 죽는 사고가 흔해요.

Forced 모드는 이걸 막아요. "zone-c 가 죽었을 때 zone-c 에 있던 replica 를 재할당하지 마라" — 즉 zone 이 살아 돌아올 때까지 yellow 상태를 유지하되 남은 노드 부하는 늘리지 않아요.

PUT _cluster/settings
{
  "persistent": {
    "cluster.routing.allocation.awareness.attributes": "zone",
    "cluster.routing.allocation.awareness.force.zone.values": "zone-a,zone-b,zone-c"
  }
}

force.<attr>.values예상되는 모든 값 을 미리 적어 두는 게 핵심이에요. 이 목록 중 하나가 사라져도 나머지로 재배치 X. AWS 3-AZ 운영의 표준 셋업이라고 보면 됩니다.

Filtering Allocation — hot/warm/cold 처럼 명시적 노드 지정

awareness 가 "같은 그룹끼리 몰리지 마라" 라면, Filtering"이 인덱스는 이 노드(또는 노드 그룹) 에만 들어가라" 라는 강한 제약이에요.

세 가지 키워드가 있어요. include적어도 하나 의 속성이 일치해야 OK, exclude하나라도 일치하면 거기 못 들어가요, require전부 일치 해야 OK.

가장 자주 쓰는 자리가 hot/warm/cold 분리 예요. 시간 데이터(로그·메트릭) 에서 최근 7일 은 SSD 노드(hot), 7~30일 은 일반 디스크 노드(warm), 30일 이상 은 저렴한 노드(cold) 에 두는 패턴.

# hot 노드
node.attr.data_tier: hot

# warm 노드
node.attr.data_tier: warm

# cold 노드
node.attr.data_tier: cold

인덱스별로 어느 tier 에 둘지 박아요.

PUT logs-2026.05.19/_settings
{
  "index.routing.allocation.require.data_tier": "hot"
}

# 7일 지난 인덱스를 warm 으로 이동
PUT logs-2026.05.12/_settings
{
  "index.routing.allocation.require.data_tier": "warm"
}

6편(ILM·Aliases·Rollover) 에서 본 ILM 정책이 시간 흐름에 따라 이 require 값을 자동으로 갈아끼우는 자동화예요. 27편은 그 require 값이 실제로 노드에 어떻게 매칭되는가 의 메커니즘.

노드 제거 전 데이터 빼기

운영 중 노드 한 대를 빼야 할 때 자주 쓰는 패턴이에요. exclude 로 해당 노드 IP 를 박아 두면 클러스터가 알아서 그 노드의 모든 샤드를 다른 노드로 옮겨요.

PUT _cluster/settings
{
  "transient": {
    "cluster.routing.allocation.exclude._ip": "10.0.1.42"
  }
}

_ip 외에 _name(노드 이름)·_host(호스트명)·_id(노드 ID) 도 다 가능해요. 모든 샤드 이동이 끝났는지는 _cat/allocation?v 로 확인 — 해당 노드의 shard 수가 0 으로 줄면 안전하게 노드 종료 OK.

Disk-based Allocation — 디스크 차오를수록 단계적으로 제동

샤드 배치에서 디스크 여유 만큼 무서운 변수가 없어요. 노드 디스크가 가득 차면 색인은 물론 기존 데이터까지 read-only 로 잠겨요. 이걸 3단계 watermark 로 단계적으로 제동.

단계 기본값 행동
Low (low) 85% 새 샤드 할당 중단 — 기존 샤드는 그대로
High (high) 90% 기존 샤드도 다른 노드로 이동 시작
Flood Stage (flood_stage) 95% 해당 노드의 인덱스를 read-only 로 잠금

flood_stage 에 한 번 걸리면 사고예요. 인덱스가 read-only-allow-delete: true 상태가 되고, 디스크를 비운 뒤에도 자동으로 풀리지 않아요. 풀려면 명시적으로 settings 를 다시 false 로 박아 줘야 해요.

PUT logs-2026.05.19/_settings
{
  "index.blocks.read_only_allow_delete": null
}

기본값을 운영 환경에 맞게 바꾸는 게 표준이에요. SSD 가 비싸지 않은 시대라 low=80% · high=85% · flood=90% 로 조금 더 보수적으로 가져가는 회사가 많아요.

PUT _cluster/settings
{
  "persistent": {
    "cluster.routing.allocation.disk.watermark.low": "80%",
    "cluster.routing.allocation.disk.watermark.high": "85%",
    "cluster.routing.allocation.disk.watermark.flood_stage": "90%"
  }
}

절대값("200gb")도 가능 — 노드 디스크 크기가 노드마다 다르면 절대값이 더 안전해요.

Shrink API — Primary Shard 갯수 줄이기

2편에서 "Primary Shard 갯수는 한 번 정하면 못 바꾼다" 라고 했어요. 정확히는 기본 동작이 못 바꾼다 이고, 특수 API 셋(shrink·split·clone) 이 우회 경로예요. 셋 다 새 인덱스를 만들고 기존 인덱스를 거기로 옮기는 형태라 대상 인덱스 자체는 read-only 가 전제예요.

Shrink갯수를 줄여요. 5개를 1개로, 12개를 3개로 같은 식. 단, 기존 갯수의 약수 여야 해요. 12 → 3 OK, 12 → 5 X.

세 가지 전제조건이 있어요. (1) 인덱스를 read-only 로 잠그고 (2) 모든 primary 와 replica 를 같은 한 노드 에 모으고 (3) 인덱스 health 가 green 이어야 해요.

# (1) 한 노드에 모으기 + read-only
PUT logs-2026.05.19/_settings
{
  "index.routing.allocation.require._name": "node-1",
  "index.blocks.write": true
}

# (2) shrink 실행
POST logs-2026.05.19/_shrink/logs-2026.05.19-shrunk
{
  "settings": {
    "index.number_of_shards": 1,
    "index.number_of_replicas": 1,
    "index.routing.allocation.require._name": null,
    "index.blocks.write": null
  }
}

내부 동작은 segment hardlink 로 새 인덱스 디렉터리를 만드는 거라, 데이터 크기가 커도 디스크·시간 비용이 거의 안 들어요. 다만 한 노드에 모든 샤드가 들어갈 디스크 여유 가 있어야 해요 — 1TB 인덱스를 shrink 하려면 그 노드 한 대에 1TB 여유 필수.

자주 쓰는 자리가 ILM 의 cold phase 예요. 핫일 때 30개로 쪼개 두던 인덱스를 오래되어서 검색·쓰기가 거의 없을 때 1~3개로 줄여 클러스터 전체 샤드 수 를 낮추는 패턴.

Split API — Primary Shard 갯수 늘리기

Split 은 반대 — 갯수를 늘려요. 1개를 5개로, 3개를 12개로. 단, 기존 갯수의 배수 여야 해요. 1 → 5 OK, 3 → 12 OK, 3 → 10 X.

전제조건은 read-only 한 가지 + 대상 인덱스가 처음 만들어질 때 index.number_of_routing_shards 가 충분히 큰 값으로 잡혔어야 해요. 8.x 기본값이 알아서 큰 값을 잡아 줘서 대부분의 인덱스는 자동으로 split 가능하지만, 7.x 이전 인덱스는 명시 안 했으면 split X.

# read-only 로 잠그기
PUT logs-2026.05.19/_settings
{
  "index.blocks.write": true
}

# 1 → 5 로 split
POST logs-2026.05.19/_split/logs-2026.05.19-split
{
  "settings": {
    "index.number_of_shards": 5,
    "index.number_of_replicas": 1,
    "index.blocks.write": null
  }
}

자주 쓰는 자리가 예상보다 인덱스가 커져서 단일 샤드 50GB 권장치를 넘은 경우. 처음 1개로 만들었다가 데이터가 폭증해 100GB 가 된 인덱스를 5개·각 20GB 로 쪼개는 식. 또는 읽기 부하가 한쪽 샤드에 쏠리는 hotspot 을 분산할 때.

내부 동작은 shrink 와 같은 hardlink 라 데이터 카피가 거의 없어요. 다만 기존 라우팅 알고리즘이 새 샤드 갯수와 호환되도록 내부 hash 함수가 바뀌어요.

Clone API — 같은 갯수로 복제

Clone 은 갯수는 그대로 두고 복제본을 하나 더 만들어요. 1→1, 5→5, 12→12. 사용 자리가 좁아 보이지만 의외로 자주 써요.

  • 버전 업그레이드 전 백업 — 운영 인덱스를 clone 해 두고, 새 인덱스 만 변경. 문제 발생 시 alias 만 원본으로 되돌리면 즉시 복귀.
  • 다른 settings 적용 미리보기 — 같은 데이터에 다른 analyzer·replica 수 를 박아 검색 결과를 비교.
  • 테스트 데이터 분리 — 운영 데이터 그대로를 별도 인덱스로 떠서 테스트 환경 에서 실험.
PUT logs-2026.05.19/_settings
{
  "index.blocks.write": true
}

POST logs-2026.05.19/_clone/logs-2026.05.19-clone
{
  "settings": {
    "index.number_of_replicas": 2,
    "index.blocks.write": null
  }
}

세 API(shrink·split·clone) 모두 대상이 read-only + 결과 인덱스는 새 이름 + 디스크는 hardlink 라 거의 무료 + alias 갈아끼우면 무중단 전환 이라는 같은 패턴.

자주 만나는 사고 7가지

사고 1 — Disk Watermark 초과 후 인덱스가 read-only 로 잠김

원인 — 디스크 사용량이 95%(flood_stage) 를 넘으면 해당 노드의 인덱스가 read-only_allow_delete: true 로 잠겨요. 디스크를 비워도 자동으로 풀리지 않아요.

해결 — 디스크를 충분히 비운 뒤(low watermark 85% 아래까지) index.blocks.read_only_allow_delete 를 명시적으로 null 로 박아 풀어야 해요. 운영 차원에서는 low/high/flood 기본값을 80/85/90% 로 좀 더 보수적으로 가져가는 게 표준.

사고 2 — Awareness 없이 같은 rack 에 primary·replica 가 같이 떨어짐

원인node.attr.zone 까지는 박아 뒀는데 cluster.routing.allocation.awareness.attributes 를 설정 안 해 둠. awareness 가 비활성 상태라 zone-a 노드 두 대에 primary 와 replica 가 같이 들어가 있어요. zone-a 가 죽으면 데이터 통째로 X.

해결_cluster/settingsawareness.attributes: zone 박기. 운영 시작 첫날에 박는 게 표준 셋업. 박은 뒤로 기존 잘못된 배치도 알아서 재분산.

사고 3 — Shrink 전 노드 부족

원인 — 5개 → 1개로 shrink 하려는데 한 노드에 5개 모두 모으는 단계 에서 디스크 여유가 부족해서 진행 안 됨. 또는 모은 노드의 hardware 가 약해서 색인 검색이 막힘.

해결 — shrink 대상 노드를 디스크 충분히 큰 노드 로 명시 지정(require._name). 작업 시간 동안 그 노드의 메모리·CPU 사양도 평소보다 여유 있게.

사고 4 — Split 후 routing 충돌

원인 — 7.x 시절에 만들어진 인덱스를 8.x 로 옮기고 split 시도 → index.number_of_routing_shards 가 작아서 split 불가. 또는 split 후 라우팅 hash 가 바뀌어서 외부에서 ?routing=user_id 로 보내던 트래픽이 흩어짐.

해결 — 인덱스 생성 시점에 number_of_routing_shards 를 미리 크게 잡아 두기(8.x 기본값은 충분, 7.x 이하는 명시 필수). routing 키 사용 인덱스는 split 보다 reindex 가 더 안전.

사고 5 — Forced Awareness 빠짐

원인awareness.attributes 만 박고 force.zone.values 는 안 박음. zone-c 한 곳이 죽었을 때 클러스터가 zone-a·zone-b 에 replica 를 두 배로 만들면서 두 zone 의 디스크가 같이 차서 연쇄 사고.

해결 — AZ-aware 운영은 항상 force.<attr>.values 까지 박는 게 짝꿍. 예상되는 zone 값을 전부 명시 — 새 zone 을 추가할 때만 갱신.

사고 6 — Exclude 후 정리 안 함

원인 — 노드 교체 작업으로 exclude._ip 박아 데이터 빼낸 뒤, 작업이 끝나도 settings 정리 안 함. 한참 뒤 같은 IP 대역에 새 노드를 띄웠는데 왜 이 노드만 샤드가 안 들어오는지 모름.

해결 — exclude 는 transient 로 박고 작업 직후 null 로 명시 해제. 또는 persistent 로 박았다면 노드 교체 PR 절차에 정리 단계 를 한 줄 추가.

사고 7 — 인덱스당 최대 샤드 한도 초과

원인 — 8.x 기본 cluster.max_shards_per_node1000. 하루 인덱스 100개 × 30일 × shard 5개 = 15,000 — 노드 15대로 분산해도 빠듯. 한도 초과하면 새 인덱스 생성 X.

해결Rollover · ILM · Shrink오래된 인덱스 샤드 수 줄이기 가 1차. 그래도 부족하면 cluster.max_shards_per_node2000~3000 으로 올리되, 한 노드당 샤드 수가 너무 많으면 heap pressure노드 추가 가 우선.

운영 권장 패턴 5가지

(1) AZ-aware 셋업은 첫날에 박아 둔다. awareness.attributes + force.<attr>.values 두 줄을 클러스터 생성 직후 가장 먼저 박으세요. 운영 중에 박으려고 미루면 이미 어긋난 배치를 재분산하는 비용 이 커요.

(2) Disk Watermark 는 기본값보다 보수적으로. low=80% · high=85% · flood=90% 가 권장. 5% 더 여유 있게 잡아 두는 게 새벽 사고 한 건을 막아요.

(3) Hot/Warm/Cold tier 분리는 ILM 과 함께 설계. node.attr.data_tier 셋팅을 노드에 박아 두고 ILM 정책으로 7일 → warm, 30일 → cold 자동 전환. 6편(ILM) 과 27편(이 글) 이 짝꿍.

(4) Shrink·Split 은 alias 와 함께. 결과 인덱스 이름을 새로 만들고 alias 만 갈아끼우면 무중단 전환. 직접 인덱스 이름을 앱에서 참조하면 작업 시점에 다운타임 발생.

(5) _cluster/allocation/explain 을 운영 표준 도구로. yellow·red 가 뜨면 가장 먼저 들고 가는 API. 왜 이 샤드가 못 가는가 의 정답이 노드별 한 줄로 적혀 있어요.

시험 직전 한 번 더 — 압축 노트

  • Shard Allocator = 마스터 노드 안의 컴포넌트, 클러스터 상태 변화 때 깨어남.
  • 결정 순서: enablefilteringawarenessdisk watermarkmax_shards_per_node.
  • 진단 API = _cluster/allocation/explain — 노드별 거부 사유 한 줄씩.
  • Allocation Awareness = node.attr.<key> + cluster.routing.allocation.awareness.attributes — 같은 그룹에 primary·replica 동시 X.
  • Forced Awareness = awareness.force.<attr>.values — zone 죽었을 때 재배치 안 함.
  • Filtering 3종 = include · exclude · require_name · _ip · _host · _id + 임의 node.attr.* 전부 가능.
  • Hot/Warm/Cold = data_tier 속성 + ILM 자동 전환.
  • Disk Watermark 3단계 = low 85% · high 90% · flood 95% (기본값), 보수 운영은 80/85/90.
  • Flood stage = 인덱스 read-only_allow_delete 잠금, 디스크 비워도 자동 안 풀림 → 명시 해제 필수.
  • Shrink = 갯수 줄임, 약수, 한 노드에 모은 뒤 hardlink.
  • Split = 갯수 늘림, 배수, number_of_routing_shards 필요.
  • Clone = 같은 갯수로 복제, settings 미리보기·테스트 분리 자리.
  • 세 API 공통 = 대상 read-only + 새 이름 + hardlink + alias 로 전환.
  • 노드 제거 표준 = cluster.routing.allocation.exclude._ip 박기 → _cat/allocation 으로 샤드 0 확인 → 노드 종료 → exclude 해제.
  • cluster.max_shards_per_node 기본 1000 — Rollover·ILM·Shrink 로 줄이는 게 1차, 늘리는 건 2차.

시리즈 다른 편

  • 2편 = Elasticsearch 핵심 개념 — Index·Document·Shard·Replica·Mapping 깊이
  • 6편 = ILM·Aliases·Rollover — 시간 데이터 라이프사이클 자동화
  • 26편 = Cluster Operations — Node 종류·Master·rolling restart·quorum
  • 28편 = Snapshot·Restore — S3 백업·복구·SLM
  • 30편 = Monitoring — _cluster/health·_nodes/stats·slow log
  • 31편 = Performance — JVM heap·refresh interval·circuit breaker

한 줄 정리 — Shard Allocation = Awareness(rack·zone) + Filtering(hot/warm/cold) + Disk Watermark + Shrink·Split·Clone 네 갈래로 어느 노드에 어느 샤드를 둘지 명시 제어하는 운영 깊이. AZ-aware + ILM tier + 보수 watermark + alias 전환이 4-pack 표준.

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

답글 남기기

error: Content is protected !!