본문으로 건너뛰기
Elasticsearch 클러스터 아키텍처 가이드
검색 엔진 / · PT5M read

Elasticsearch 아키텍처: 클러스터, 노드, 샤드 이해하기

클러스터가 Yellow 상태로 바뀌었을 때

새벽 2시, PagerDuty 알림이 울렸다.

⚠️ [WARNING] Elasticsearch Cluster Status: YELLOW
Unassigned shards: 5
Cluster: production-search

검색 서비스는 동작하고 있었지만, 한 노드가 다운된 상태였다. 이 상황에서 추가로 노드가 하나 더 죽으면? 데이터 유실 가능성이 있었다.

Elasticsearch 클러스터를 운영하려면 클러스터, 노드, 샤드의 관계를 정확히 이해해야 한다. 이 글에서는 Elasticsearch의 분산 아키텍처를 운영 관점에서 깊이 있게 다룬다.

클러스터(Cluster)란?

클러스터는 하나 이상의 노드(서버)가 모여 형성하는 논리적 그룹이다. 같은 클러스터 이름을 가진 노드들이 자동으로 연결된다.

클러스터 구성 예시

# elasticsearch.yml
cluster.name: production-search
node.name: node-1

클러스터 상태(Cluster Health)

curl -X GET "localhost:9200/_cluster/health?pretty"
{
  "cluster_name": "production-search",
  "status": "green",
  "number_of_nodes": 3,
  "number_of_data_nodes": 3,
  "active_primary_shards": 10,
  "active_shards": 20,
  "unassigned_shards": 0
}

상태별 의미

상태의미조치
🟢 Green모든 Primary/Replica 샤드 정상정상 상태
🟡 YellowPrimary 정상, 일부 Replica 미할당노드 확인, Replica 할당
🔴 Red일부 Primary 샤드 미할당긴급 대응 필요, 데이터 유실 가능

Yellow 상태의 흔한 원인:

  • 노드 1개로 운영 (Replica 할당 불가)
  • 노드 장애로 Replica 재할당 대기 중
  • 디스크 용량 부족 (워터마크 초과)

노드(Node)란?

노드는 클러스터를 구성하는 단일 서버 인스턴스다. 각 노드는 역할에 따라 다른 작업을 수행한다.

노드 역할(Node Roles)

# elasticsearch.yml - 노드 역할 설정
node.roles: [ master, data, ingest ]
역할설명리소스 요구사항
master클러스터 상태 관리, 샤드 할당 결정낮은 CPU/메모리, 안정성 중요
data데이터 저장, 검색/집계 쿼리 실행높은 디스크/메모리/CPU
data_content일반 콘텐츠 데이터 (Hot 데이터)높은 성능 SSD
data_hot최신 데이터, 빈번한 쿼리고성능 SSD
data_warm덜 빈번한 쿼리, 오래된 데이터대용량 HDD
data_cold거의 접근 안 함, 아카이브저비용 스토리지
ingest인덱싱 전 파이프라인 처리CPU 중심
ml머신러닝 작업높은 CPU/메모리
transform데이터 변환 작업CPU/메모리
remote_cluster_client크로스 클러스터 검색네트워크
coordinating요청 라우팅 (역할 없음)CPU/네트워크

Master Node 심층 분석

Master 노드는 클러스터의 두뇌 역할을 한다:

Master Node 역할:
├── 클러스터 상태(Cluster State) 관리
│   ├── 인덱스 메타데이터
│   ├── 노드 정보
│   └── 샤드 할당 정보
├── 인덱스 생성/삭제
├── 노드 추가/제거 처리
└── 샤드 할당 결정

Master 선출 과정:

1. 클러스터 시작 시 모든 master-eligible 노드가 투표
2. 과반수(quorum)를 얻은 노드가 Master로 선출
3. Master 장애 시 나머지 노드들이 재선출

Split-Brain 방지:

# 최소 과반수 설정 (3노드 클러스터)
discovery.zen.minimum_master_nodes: 2

# Elasticsearch 7.x 이상에서는 자동 계산
cluster.initial_master_nodes: ["node-1", "node-2", "node-3"]

Data Node 심층 분석

Data 노드는 실제 데이터를 저장하고 쿼리를 처리한다:

Data Node 역할:
├── 샤드(Shard) 저장
│   ├── Primary Shards
│   └── Replica Shards
├── CRUD 작업 수행
├── 검색 쿼리 실행
└── 집계(Aggregation) 처리

Hot-Warm-Cold 아키텍처:

# Hot 노드 (신규 데이터, 빈번한 쿼리)
node.roles: [ data_hot ]
node.attr.data: hot

# Warm 노드 (오래된 데이터, 가끔 쿼리)
node.roles: [ data_warm ]
node.attr.data: warm

# Cold 노드 (아카이브, 드문 접근)
node.roles: [ data_cold ]
node.attr.data: cold

Coordinating Node

모든 노드는 기본적으로 Coordinating 역할을 수행한다. 전용 Coordinating 노드를 두면 대규모 쿼리 처리에 유리하다:

# 전용 Coordinating 노드 (역할 없음 = Coordinating 전용)
node.roles: [ ]

Coordinating 노드 동작:

1. 클라이언트 요청 수신
2. 관련 샤드가 있는 Data 노드로 요청 분배
3. 각 노드의 응답 수집 및 병합
4. 최종 결과 클라이언트에 반환

샤드(Shard)란?

인덱스는 하나 이상의 샤드로 분할된다. 샤드는 Lucene 인덱스 그 자체다.

Primary Shard와 Replica Shard

Index: products (3 Primary, 1 Replica)
┌─────────────────────────────────────────────────────────┐
│                        Cluster                          │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐     │
│  │   Node 1    │  │   Node 2    │  │   Node 3    │     │
│  │  ┌───────┐  │  │  ┌───────┐  │  │  ┌───────┐  │     │
│  │  │ P0    │  │  │  │ P1    │  │  │  │ P2    │  │     │
│  │  └───────┘  │  │  └───────┘  │  │  └───────┘  │     │
│  │  ┌───────┐  │  │  ┌───────┐  │  │  ┌───────┐  │     │
│  │  │ R1    │  │  │  │ R2    │  │  │  │ R0    │  │     │
│  │  └───────┘  │  │  └───────┘  │  │  └───────┘  │     │
│  └─────────────┘  └─────────────┘  └─────────────┘     │
└─────────────────────────────────────────────────────────┘

P0, P1, P2 = Primary Shards
R0, R1, R2 = Replica Shards (각 Primary의 복제본)
구분Primary ShardReplica Shard
역할원본 데이터, 쓰기 처리복제본, 읽기 분산
개수인덱스 생성 시 결정 (변경 불가)동적 변경 가능
장애 대응장애 시 Replica가 승격Primary 복제
쿼리 처리읽기/쓰기 모두읽기만

샤드 수 결정하기

인덱스 생성 시 샤드 수 지정:

curl -X PUT "localhost:9200/products" -H "Content-Type: application/json" -d'
{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 1
  }
}'

샤드 수 결정 가이드라인:

고려 사항권장
샤드 당 크기10GB ~ 50GB
노드 당 샤드 수힙 1GB당 20개 이하
총 샤드 수1000개 이하 권장

나쁜 예 vs 좋은 예:

# ❌ 나쁜 예: 1GB 데이터에 100개 샤드 (오버샤딩)
{
  "settings": {
    "number_of_shards": 100,
    "number_of_replicas": 1
  }
}

# ✅ 좋은 예: 데이터 크기에 맞는 샤드 수
{
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 1
  }
}

오버샤딩의 문제:

  • Master 노드 부하 증가 (클러스터 상태 관리)
  • 메모리 사용량 증가 (샤드당 오버헤드)
  • 검색 성능 저하 (너무 많은 샤드 조회)

샤드 할당 전략

특정 노드에 샤드 할당:

curl -X PUT "localhost:9200/logs-2024" -H "Content-Type: application/json" -d'
{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 1,
    "index.routing.allocation.require.data": "hot"
  }
}'

샤드 재배치 확인:

curl -X GET "localhost:9200/_cat/shards/products?v"
index    shard prirep state   docs  store ip         node
products 0     p      STARTED 1000  5mb   10.0.0.1   node-1
products 0     r      STARTED 1000  5mb   10.0.0.3   node-3
products 1     p      STARTED 1200  6mb   10.0.0.2   node-2
products 1     r      STARTED 1200  6mb   10.0.0.1   node-1
products 2     p      STARTED 800   4mb   10.0.0.3   node-3
products 2     r      STARTED 800   4mb   10.0.0.2   node-2

문서 라우팅

문서가 어떤 샤드에 저장될지는 라우팅 공식으로 결정된다:

shard_num = hash(_routing) % num_primary_shards

기본 라우팅 (문서 ID 사용):

# 문서 ID "123"을 기반으로 샤드 결정
curl -X PUT "localhost:9200/products/_doc/123" -H "Content-Type: application/json" -d'
{
  "name": "블루투스 이어폰"
}'

커스텀 라우팅 (특정 필드 기준):

# user_id 기준으로 라우팅 (같은 사용자의 문서는 같은 샤드에)
curl -X PUT "localhost:9200/orders/_doc/1?routing=user_123" -H "Content-Type: application/json" -d'
{
  "user_id": "user_123",
  "product": "블루투스 이어폰",
  "amount": 50000
}'

커스텀 라우팅의 장점:

  • 관련 문서가 같은 샤드에 위치
  • 특정 쿼리 시 단일 샤드만 조회 (성능 향상)

커스텀 라우팅의 주의점:

  • Hot Spot 발생 가능 (특정 샤드에 데이터 집중)
  • 검색 시 라우팅 값 필요

장애 복구 시나리오

시나리오 1: Data 노드 1개 장애

Before (Green):
Node 1: [P0] [R1]
Node 2: [P1] [R2]
Node 3: [P2] [R0]

Node 2 Down (Yellow):
Node 1: [P0] [R1] [P1*]  ← R1이 P1로 승격
Node 3: [P2] [R0]
* 5개 샤드 중 R1, R2 미할당

자동 복구 과정:

  1. Master가 Node 2 장애 감지
  2. Node 2의 Replica(R1)를 Primary로 승격
  3. 새로운 Replica 생성 시도
  4. 노드 부족으로 일부 Replica 미할당 → Yellow

시나리오 2: Master 노드 장애

Before:
Node 1 (Master): [P0]
Node 2: [P1]
Node 3: [P2]

Node 1 Down:
Node 2 (New Master): [P1]  ← 새로운 Master 선출
Node 3: [P2]
* P0 미할당 → Red

복구 절차:

  1. 나머지 Master-eligible 노드가 재선출
  2. 새 Master가 클러스터 상태 복구
  3. P0이 있던 노드의 데이터 복구 필요

시나리오 3: 네트워크 파티션 (Split-Brain)

정상 상태:
[Master] ─── [Node 2] ─── [Node 3]

네트워크 분리:
[Master]    │    [Node 2] ─── [Node 3]
            │         └── 새로운 Master 선출 시도?

Split-Brain 방지 메커니즘:

  • 최소 과반수(quorum) 없이는 Master 선출 불가
  • 3노드 클러스터: 최소 2노드 필요
  • 5노드 클러스터: 최소 3노드 필요

프로덕션 클러스터 구성 예시

소규모 (개발/테스트)

# 단일 노드 (개발용)
node.roles: [ master, data ]
discovery.type: single-node

중규모 (일반 프로덕션)

3-Node Cluster:
┌─────────────────────────────────────────────┐
│  Node 1          Node 2          Node 3    │
│  [M, D]          [M, D]          [M, D]    │
│                                             │
│  M = Master-eligible                        │
│  D = Data                                   │
└─────────────────────────────────────────────┘
# 모든 노드에 동일 설정
node.roles: [ master, data ]
cluster.initial_master_nodes: ["node-1", "node-2", "node-3"]
discovery.seed_hosts: ["10.0.0.1", "10.0.0.2", "10.0.0.3"]

대규모 (엔터프라이즈)

10+ Node Cluster:
┌───────────────────────────────────────────────────────┐
│  Dedicated Masters (3)                                │
│  [M1] [M2] [M3]                                       │
│                                                       │
│  Coordinating Nodes (2)  ← 로드밸런서 연결            │
│  [C1] [C2]                                            │
│                                                       │
│  Data Hot Nodes (3)      ← 최신 데이터                │
│  [D-H1] [D-H2] [D-H3]                                 │
│                                                       │
│  Data Warm Nodes (2)     ← 오래된 데이터              │
│  [D-W1] [D-W2]                                        │
│                                                       │
│  Ingest Nodes (2)        ← 파이프라인 처리            │
│  [I1] [I2]                                            │
└───────────────────────────────────────────────────────┘

클러스터 모니터링 필수 API

클러스터 상태 확인

# 전체 상태
curl -X GET "localhost:9200/_cluster/health?pretty"

# 상세 상태
curl -X GET "localhost:9200/_cluster/health?level=shards&pretty"

노드 정보 확인

# 노드 목록
curl -X GET "localhost:9200/_cat/nodes?v"

# 노드 상세 정보
curl -X GET "localhost:9200/_nodes/stats?pretty"

샤드 할당 확인

# 샤드 목록
curl -X GET "localhost:9200/_cat/shards?v&s=index"

# 미할당 샤드 원인
curl -X GET "localhost:9200/_cluster/allocation/explain?pretty"

디스크 사용량 확인

curl -X GET "localhost:9200/_cat/allocation?v"
shards disk.indices disk.used disk.avail disk.total disk.percent host       node
    10       50gb     60gb      40gb       100gb          60    10.0.0.1   node-1
    10       45gb     55gb      45gb       100gb          55    10.0.0.2   node-2
    10       48gb     58gb      42gb       100gb          58    10.0.0.3   node-3

OpenSearch 차이점

OpenSearch는 Elasticsearch 7.10 포크로 대부분의 아키텍처가 동일하다.

주요 차이점

구분ElasticsearchOpenSearch
노드 역할 설정node.roles동일 (1.x부터 지원)
클러스터 설정cluster.initial_master_nodes동일
보안 기본값8.x부터 기본 활성화기본 활성화 (Security Plugin)

OpenSearch 클러스터 설정 예시

# opensearch.yml
cluster.name: production-search
node.name: os-node-1
node.roles: [ cluster_manager, data ]  # master → cluster_manager
discovery.seed_hosts: ["10.0.0.1", "10.0.0.2", "10.0.0.3"]
cluster.initial_cluster_manager_nodes: ["os-node-1", "os-node-2", "os-node-3"]

용어 변경:

  • mastercluster_manager
  • master_eligiblecluster_manager_eligible

마무리

이 글에서 다룬 핵심 내용:

  1. 클러스터: 노드의 논리적 그룹, Green/Yellow/Red 상태
  2. 노드 역할: Master, Data, Coordinating, Ingest 등
  3. 샤드: Primary/Replica, 데이터 분산의 기본 단위
  4. 문서 라우팅: 해시 기반 샤드 결정
  5. 장애 복구: 자동 Failover, Split-Brain 방지

다음 글에서는 Docker와 Kubernetes에서 Elasticsearch를 설치하고 클러스터를 구성하는 방법을 실습한다. 로컬 개발 환경부터 프로덕션 배포까지 단계별로 알아본다.

참고 자료

다음 글: Elasticsearch CRUD: 문서 색인과 Bulk API 최적화
My avatar

글을 마치며

이 글이 도움이 되었기를 바랍니다. 궁금한 점이나 의견이 있다면 댓글로 남겨주세요.

더 많은 기술 인사이트와 개발 경험을 공유하고 있으니, 다른 포스트도 확인해보세요.

유럽살며 여행하며 코딩하는 노마드의 여정을 함께 나누며, 함께 성장하는 개발자 커뮤니티를 만들어가요! 🚀


Elasticsearch 검색 엔진 마스터 시리즈
Elasticsearch 검색 엔진 입문 가이드

Elasticsearch 입문: 검색 엔진이 필요한 이유

RDBMS의 LIKE 검색이 왜 프로덕션에서 문제가 되는지, 역인덱스가 무엇인지, 그리고 Elasticsearch가 어떻게 이 문제를 해결하는지 실제 장애 사례와 함께 알아봅니다.

백엔드 데이터베이스 프로덕션 +3
Elasticsearch 매핑과 필드 타입 가이드

Elasticsearch 매핑: 필드 타입과 스키마 설계

Elasticsearch의 매핑을 이해하고 올바른 필드 타입을 선택하는 방법을 알아봅니다. text vs keyword, 동적 매핑의 함정, 매핑 폭발 방지, 중첩 객체 처리까지 실무 설계 패턴을 다룹니다.

Elasticsearch OpenSearch 검색엔진 +3
Elasticsearch CRUD와 Bulk API 가이드

Elasticsearch CRUD: 문서 색인과 Bulk API 최적화

Elasticsearch에서 문서를 생성, 조회, 수정, 삭제하는 방법과 대량 데이터 처리를 위한 Bulk API 최적화 전략을 알아봅니다. refresh 동작, 라우팅, 버전 관리까지 실무 팁을 다룹니다.

백엔드 Elasticsearch OpenSearch +3