
이번에는 OpenSearch 내부 구조를 “로그 한 건이 들어와서 실제로 어디에 저장되는지” 기준으로 아주 쉽게 연결해서 설명해볼게요. 😊
🔍 OpenSearch 내부 구조 쉽게 이해하기
Index → Document → Shard → Replica → Node
먼저 전체 관계부터 보면 이렇습니다.
OpenSearch Cluster
│
├─ Node 1
│ ├─ Primary Shard 0
│ └─ Replica Shard 1
│
├─ Node 2
│ ├─ Primary Shard 1
│ └─ Replica Shard 2
│
└─ Node 3
├─ Primary Shard 2
└─ Replica Shard 0
그리고 그 Shard 내부에 실제 Document들이 저장됩니다.
즉 핵심 관계는:
Index
↓
Shard로 분할
↓
각 Shard 안에 Document 저장
↓
Shard는 여러 Node에 분산
↓
Replica가 Shard 복사본 보관
한 문장으로 줄이면:
Index라는 논리적인 데이터 공간을 Shard로 쪼개고, 그 Shard를 여러 OpenSearch Node에 분산해서 저장하며 Replica가 복사본을 보관합니다.
1️⃣ Document부터 이해하자 📄
OpenSearch에서 가장 작은 데이터 단위가 Document입니다.
예를 들어 Kubernetes GPU 서버에서 이런 로그가 발생했다고 해보겠습니다.
2026-08-09 14:30:10 ERROR gpu-node01 GPU3 Xid 43
Fluent Bit이 Parsing하면:
{
"@timestamp": "2026-08-09T14:30:10",
"level": "ERROR",
"hostname": "gpu-node01",
"gpu": "GPU3",
"xid": 43
}
이 데이터 한 건이 바로:
📄 Document 1개
입니다.
로그가 하나 더 발생하면:
{
"@timestamp": "2026-08-09T14:31:22",
"level": "WARN",
"hostname": "gpu-node02",
"gpu": "GPU5",
"message": "ECC Correctable Error"
}
또 하나의 Document가 됩니다.
따라서:
로그 한 건
↓
Parsing
↓
JSON 데이터
↓
Document 1개
라고 보면 됩니다.
2️⃣ Index는 Document를 모아놓는 공간 📚
Document가 수백만 개 발생하면 어딘가에 모아야겠죠.
그 논리적인 저장공간이 Index입니다.
예를 들어 Index 이름이:
gpu-logs-2026.08.09
라면:
Index
gpu-logs-2026.08.09
├─ Document 1
│ └─ GPU3 Xid 43
│
├─ Document 2
│ └─ GPU5 ECC Error
│
├─ Document 3
│ └─ GPU1 Temperature Warning
│
├─ Document 4
│ └─ GPU7 NVLink Error
│
└─ ...
처럼 생각할 수 있습니다.
쉽게 비유하면:
Index = 책장 📚
Document = 책 한 권 📄
입니다.
3️⃣ 그런데 Index가 너무 커지면? 🤔
예를 들어 하루에 로그가 10억 건 발생한다고 생각해보겠습니다.
gpu-logs-2026.08.09
Document
Document
Document
Document
...
10억 건
이걸 서버 한 대가 모두 처리한다면 성능과 장애 측면에서 문제가 생깁니다.
그래서 OpenSearch는 Index를 여러 조각으로 나눕니다.
이 조각이 바로:
🧩 Shard
입니다.
4️⃣ Shard가 뭐야?
예를 들어 gpu-logs Index를 3개의 Primary Shard로 구성했다고 해보겠습니다.
Index : gpu-logs
↓ 분할
┌────────────┐
│ Shard 0 │
└────────────┘
┌────────────┐
│ Shard 1 │
└────────────┘
┌────────────┐
│ Shard 2 │
└────────────┘
Document들이 이 Shard들에 분산됩니다.
예를 들면:
gpu-logs Index
│
├─ Shard 0
│ ├─ Document 1
│ ├─ Document 4
│ └─ Document 7
│
├─ Shard 1
│ ├─ Document 2
│ ├─ Document 5
│ └─ Document 8
│
└─ Shard 2
├─ Document 3
├─ Document 6
└─ Document 9
중요한 점은:
관리자가 Document를 어느 Shard에 넣을지 직접 선택하는 것이 아닙니다.
OpenSearch가 Routing 계산을 통해 자동으로 결정합니다.
5️⃣ 왜 Shard로 나눌까? 🚀
가장 큰 이유는 분산 처리입니다.
Shard가 하나뿐이라면:
검색 요청
↓
Shard 0
↓
한 곳에서 전부 처리
하지만 Shard가 3개라면:
검색 요청
↓
┌─────────┼─────────┐
↓ ↓ ↓
Shard 0 Shard 1 Shard 2
│ │ │
└─────────┼─────────┘
↓
결과 병합
여러 Shard가 동시에 검색을 수행할 수 있습니다.
그래서 대량 데이터 환경에서 확장성이 좋아집니다.
6️⃣ OpenSearch Node는 뭐야? 🖥️
Node는 OpenSearch 프로그램이 실행되고 있는 서버 또는 인스턴스입니다.
예를 들어 OpenSearch 서버가 3대 있다면:
OpenSearch Cluster
Node 1
Node 2
Node 3
이 3대가 하나의 Cluster를 구성할 수 있습니다.
그리고 앞에서 만든 Shard를 여러 Node에 나눠 저장합니다.
Index : gpu-logs
Shard 0 → Node 1
Shard 1 → Node 2
Shard 2 → Node 3
즉:
Index
↓
Shard
↓
Node에 실제 저장
입니다.
7️⃣ 이제 Replica가 등장합니다 🛡️
여기서 문제가 하나 있습니다.
현재:
Node 1 → Shard 0
Node 2 → Shard 1
Node 3 → Shard 2
라고 되어 있는데,
만약 Node 1이 장애 나면 어떻게 될까요?
Node 1 ❌
Shard 0
❌
Shard 0에 저장된 데이터를 사용할 수 없게 됩니다.
그래서 OpenSearch는 Replica라는 복사본을 만듭니다.
8️⃣ Replica = Shard의 복사본
Shard 0의 원본을:
Primary Shard 0
라고 한다면 복사본은:
Replica Shard 0
입니다.
예를 들어:
Node 1
└─ Primary Shard 0
Node 2
└─ Replica Shard 0
와 같이 구성할 수 있습니다.
중요한 원칙이 있습니다.
⚠️ Primary와 해당 Replica는 같은 Node에 저장하지 않습니다.
왜냐하면:
Node 1
├─ Primary Shard 0
└─ Replica Shard 0
이렇게 만들면 Node 1이 장애 났을 때:
Primary ❌
Replica ❌
둘 다 사라지기 때문입니다.
그래서:
Node 1 → Primary Shard 0
Node 2 → Replica Shard 0
처럼 분산합니다.
9️⃣ 실제 3 Node 구성을 그림으로 보면 🎯
이제 Primary Shard 3개 + Replica 1개씩 있다고 해보겠습니다.
Index : gpu-logs
Primary Shard
P0
P1
P2
Replica Shard
R0
R1
R2
OpenSearch는 예를 들어 이렇게 배치할 수 있습니다.
┌─────────────────┐
│ OpenSearch Node1│
│ │
│ P0 │
│ R2 │
└─────────────────┘
┌─────────────────┐
│ OpenSearch Node2│
│ │
│ P1 │
│ R0 │
└─────────────────┘
┌─────────────────┐
│ OpenSearch Node3│
│ │
│ P2 │
│ R1 │
└─────────────────┘
여기서:
P = Primary
R = Replica
입니다.
관계를 연결하면:
P0 → R0
P1 → R1
P2 → R2
이고 각각 다른 Node에 존재합니다.
🔟 Node 1이 장애 나면 어떻게 될까? 🚨
Node 1에는:
P0
R2
가 있었습니다.
Node 1 장애:
Node 1 ❌
├─ P0 ❌
└─ R2 ❌
하지만 다른 Node에:
Node 2
└─ R0
Node 3
└─ P2
가 존재합니다.
특히 P0이 사라졌더라도 Replica 0이 있습니다.
R0
↓
Primary로 승격
↓
P0
OpenSearch가 Replica를 Primary 역할로 승격시킬 수 있습니다.
그래서 서비스를 계속 유지할 수 있습니다.
이게 바로 Replica의 핵심 목적입니다.
🛡️ Node 장애가 나더라도 데이터를 유지하고 서비스를 계속하기 위해
1️⃣1️⃣ 실제 로그 한 건은 어떻게 저장될까?
이번에는 처음부터 끝까지 연결해보겠습니다.
GPU 서버에서 로그가 발생합니다.
2026-08-09 14:30:10 ERROR GPU3 Xid 43
① Fluent Bit 수집
GPU Server
↓
Fluent Bit
② Parsing
{
"time": "2026-08-09 14:30:10",
"level": "ERROR",
"gpu": "GPU3",
"xid": 43
}
③ OpenSearch로 전송
Fluent Bit
↓
OpenSearch
④ Index 결정
예를 들어:
gpu-logs-2026.08.09
⑤ Document 생성
Document #A123
⑥ Routing 계산
OpenSearch가:
Document A123
↓
Primary Shard 1
에 저장하기로 결정했다고 가정합니다.
⑦ Primary에 저장
Node 2
Primary Shard 1
↓
Document A123
⑧ Replica에 복제
Primary Shard 1의 Replica가 Node 3에 있다면:
Node 2
P1
└─ Document A123
↓ 복제
Node 3
R1
└─ Document A123
결과적으로 같은 Document의 데이터가 Primary와 Replica 양쪽에 존재하게 됩니다.
1️⃣2️⃣ 전체 그림으로 한 번에 보기 🧠
Kubernetes Pod / GPU Server
│
│ 로그 발생
↓
Fluent Bit
│
│ Parsing
↓
┌─────────────────────────┐
│ Index │
│ gpu-logs-2026.08.09 │
└───────────┬─────────────┘
│
│ Sharding
↓
┌──────────┼──────────┐
↓ ↓ ↓
P0 P1 P2
│ │ │
Document Document Document
│ │ │
↓ ↓ ↓
Node1 Node2 Node3
P0 P1 P2
R2 R0 R1
조금 더 정확하게 그리면:
OpenSearch Cluster
INDEX
gpu-logs-2026.08.09
│
┌────────────┼────────────┐
↓ ↓ ↓
Primary 0 Primary 1 Primary 2
│ │ │
Documents Documents Documents
┌────────────────┐
│ Node 1 │
│ │
│ Primary 0 │
│ Replica 2 │
└────────────────┘
┌────────────────┐
│ Node 2 │
│ │
│ Primary 1 │
│ Replica 0 │
└────────────────┘
┌────────────────┐
│ Node 3 │
│ │
│ Primary 2 │
│ Replica 1 │
└────────────────┘
1️⃣3️⃣ 중요한 포인트: Index 자체가 한 서버에 있는 게 아니다 ⭐
초보자 때 가장 많이 오해하는 부분입니다.
예를 들어:
gpu-logs Index → Node 1
처럼 Index 전체가 특정 서버 한 대에 저장된다고 생각하기 쉽습니다.
실제로는:
gpu-logs Index
↓
Shard 0 → Node 1
Shard 1 → Node 2
Shard 2 → Node 3
처럼 Index가 Shard 단위로 여러 Node에 분산됩니다.
따라서:
Index는 논리적인 개념이고, 실제 데이터 저장과 분산의 기본 단위는 Shard입니다.
이 부분이 매우 중요합니다.
1️⃣4️⃣ Primary Shard와 Replica Shard 차이
구분Primary ShardReplica Shard
| 의미 | 원본 Shard | Primary 복사본 |
| 데이터 저장 | ✅ | ✅ |
| 검색 처리 | ✅ | ✅ |
| 장애 대응 | 기준 데이터 | Primary 장애 시 승격 가능 |
| 같은 Node 배치 | - | 해당 Primary와 같이 배치하지 않음 |
| 목적 | 데이터 저장 | 고가용성 + 검색 처리량 |
특히 Replica는 백업만을 위한 존재가 아닙니다.
Replica도 검색 요청을 처리할 수 있습니다.
즉:
Primary
+ Replica
가 함께 검색 처리에 참여할 수 있습니다.
따라서 Replica는:
🛡️ 장애 대응 + 🚀 검색 성능 확장
두 가지 역할을 합니다.
1️⃣5️⃣ Replica는 백업인가? ❌
이것도 중요합니다.
Replica = Backup
은 아닙니다.
Replica는 Cluster 내부의 실시간 복제본입니다.
실수로 Document를 삭제하면:
Primary에서 삭제
↓
Replica에서도 삭제
됩니다.
따라서 진짜 백업은 별도로:
Snapshot
↓
S3 / Object Storage
같은 방식으로 구성해야 합니다.
즉:
Replica
= 장애 대응용
Snapshot
= 백업/복구용
이라고 구분하면 됩니다.
1️⃣6️⃣ 아주 쉬운 회사 비유 🏢
OpenSearch 구조를 회사 문서 보관으로 비유해보겠습니다.
Index
GPU 장애 보고서 전체
📚 큰 문서함
Document
GPU3 Xid43 장애 보고서 1건
📄 개별 문서
Shard
문서가 너무 많아서:
문서함 A
문서함 B
문서함 C
로 분산
🧩 문서함 조각
Node
문서함을 실제로 보관하는:
창고 1
창고 2
창고 3
🖥️ 실제 서버
Replica
문서함이 없어질 경우에 대비해서:
원본 문서함 → 다른 창고에 복사
🛡️ 복제본
결과적으로:
GPU 장애 로그
↓
Index 📚
↓
Shard 🧩🧩🧩
↓
Node 🖥️ 🖥️ 🖥️
↓
Replica 🛡️
가 됩니다.
🎯 가장 중요한 관계
이 다섯 개를 이렇게 기억하면 됩니다.
Document
= 실제 데이터 한 건
↓ 모아서
Index
= 데이터의 논리적인 묶음
↓ 나눠서
Shard
= 실제 분산 저장 단위
↓ 저장되는 곳
OpenSearch Node
= 실제 서버
+
Replica
= 다른 Node에 만들어놓은 Shard 복사본
💡 한 문장으로 정리
OpenSearch는 수많은 Document를 Index로 관리하고, Index를 여러 Shard로 나눈 뒤 여러 Node에 분산 저장하며, 각 Shard의 Replica를 다른 Node에 배치해 장애에 대비합니다.
실제 로그 시스템으로 연결하면
Kubernetes Pod
↓
Fluent Bit
↓
Parsing
↓
Document
↓
Index
↓
Primary Shard
↓
OpenSearch Node
↓
Replica Shard
↓
다른 OpenSearch Node
↓
OpenSearch Dashboards에서 검색 🔍
이 구조까지 이해하면 다음 단계에서는 “Primary Shard를 몇 개 잡아야 하는지, Replica를 0/1/2개로 하면 무엇이 달라지는지, Node가 3대인데 Shard를 10개 잡으면 실제로 어떻게 배치되는지”를 배우면 실무적인 OpenSearch 용량 설계로 연결됩니다.
'J-H-T > OpenSearch' 카테고리의 다른 글
| 🚀 OpenSearch 실무 용량 설계 입문 (0) | 2026.08.09 |
|---|---|
| [🔍 OpenSearch란 무엇일까?] Fluent Bit · Logstash까지 이해하기! (0) | 2026.08.09 |
댓글