Skip to content

필사 모드: OpenSearch 운영·관리·인덱스 설계 실전 가이드 2026

한국어
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.
원문 렌더가 준비되기 전까지 텍스트 가이드로 표시합니다.

왜 OpenSearch 운영 역량이 중요한가

OpenSearch는 로그 분석, 전문 검색, 관측성(Observability) 파이프라인의 핵심 엔진이다. 클러스터를 띄우는 것은 쉽지만, **안정적으로 운영하면서 비용을 제어**하는 것은 전혀 다른 문제다. 인덱스 하나의 샤드 수를 잘못 잡으면 클러스터 전체의 JVM 힙이 흔들리고, ISM 정책 하나가 빠지면 디스크가 차서 쓰기가 멈춘다.

이 글에서는 **클러스터 아키텍처 → 인덱스 설계 → ISM 라이프사이클 → 모니터링 → 장애 대응 → 보안 → 마이그레이션** 순서로, 운영자가 실제 프로덕션에서 마주치는 의사결정 포인트를 다룬다. 모든 API 예제는 OpenSearch 2.x 기준이며, AWS OpenSearch Service에서도 동일하게 사용할 수 있다.

1. 프로덕션 클러스터 아키텍처

1.1 노드 역할 분리

프로덕션 클러스터에서 각 노드 타입의 역할을 명확히 분리해야 안정성과 성능을 동시에 확보할 수 있다.

┌─────────────────────────────────────────────────────────────────┐

│ 클라이언트 / Data Prepper │

│ (Ingest Pipeline) │

└──────────────────────────┬──────────────────────────────────────┘

┌────────────────┼────────────────┐

▼ ▼ ▼

┌──────────┐ ┌──────────┐ ┌──────────┐

│ Master-1 │ │ Master-2 │ │ Master-3 │

│ (AZ-a) │ │ (AZ-b) │ │ (AZ-c) │

│ 전용 마스터│ │ 전용 마스터│ │ 전용 마스터│

└──────────┘ └──────────┘ └──────────┘

│ │ │

▼ ▼ ▼

┌──────────┐ ┌──────────┐ ┌──────────┐

│ Hot-1 │ │ Hot-2 │ │ Hot-3 │

│ (AZ-a) │ │ (AZ-b) │ │ (AZ-c) │

│ EBS gp3 │ │ EBS gp3 │ │ EBS gp3 │

└──────────┘ └──────────┘ └──────────┘

│ │ │

▼ ▼ ▼

┌──────────┐ ┌──────────┐ ┌──────────┐

│ Warm-1 │ │ Warm-2 │ │ Warm-3 │

│ UltraWarm│ │ UltraWarm│ │ UltraWarm│

│ (S3+캐시) │ │ (S3+캐시) │ │ (S3+캐시) │

└──────────┘ └──────────┘ └──────────┘

| 노드 타입 | 역할 | 권장 사양 | 배치 규칙 |

| --------------------- | ----------------------------------- | ----------------------------------- | ------------------------ |

| **전용 마스터** | 클러스터 메타데이터 관리, 샤드 할당 | 최소 3대 (홀수), 최신 세대 인스턴스 | 반드시 3 AZ 분산 |

| **Hot 데이터 노드** | 활성 읽기/쓰기 처리 | 높은 CPU + 빠른 스토리지 (gp3) | AZ 균등 분배 |

| **Warm 데이터 노드** | 읽기 위주, 빈도 낮은 조회 | 높은 스토리지 밀도, 낮은 CPU | UltraWarm (S3 기반) |

| **Coordinating 노드** | 요청 라우팅, 결과 집계 | 높은 CPU + 메모리, 스토리지 최소 | 검색 집약 워크로드에서만 |

1.2 용량 산정 공식

프로덕션 클러스터 사이징의 핵심 공식이다.

필요 스토리지 = 일일 원본 데이터

× (1 + 레플리카 수)

× 1.1 (인덱싱 오버헤드)

× 1.15 (OS 예약 + 내부 작업 여유)

× 보존 기간(일)

**예시:** 일별 로그 100 GiB, 레플리카 1, 30일 보존 → `100 × 2 × 1.1 × 1.15 × 30 = 7,590 GiB`

디스크 사용률은 **최대 75%** 이내로 유지한다. 75%를 초과하면 OpenSearch가 새 샤드 할당을 거부하기 시작하고, 85%에서 read-only로 전환된다.

실제 프로비저닝 = 필요 스토리지 / 0.75 = 10,120 GiB

2. 인덱스 설계와 샤드 전략

2.1 샤드 사이징 기준

샤드 크기는 워크로드 유형에 따라 달라진다. OpenSearch 공식 문서와 AWS 가이드에서 권장하는 범위는 다음과 같다.

| 워크로드 | 권장 샤드 크기 | 근거 |

| ------------------------------------- | -------------- | ------------------------------- |

| 검색 지연 민감 (e-commerce, 자동완성) | 10–30 GiB | 지연 시간 최소화, 빠른 복구 |

| 쓰기 중심 / 로그 분석 | 30–50 GiB | 쓰기 처리량 극대화 |

| 최대 권장 한도 | 50 GiB | 이 이상은 복구·재배치 시간 급증 |

프라이머리 샤드 수 계산 공식:

프라이머리 샤드 수 ≈ (원본 데이터 크기 + 성장 여유) × 1.1 / 목표 샤드 크기

**예시:** 일별 로그 66 GiB, 향후 4배 성장 예상, 목표 샤드 크기 30 GiB

(66 + 198) × 1.1 / 30 ≈ 10 프라이머리 샤드

2.2 노드당 샤드 한도

| 제약 조건 | 한도 |

| ----------------- | --------------------------------------------- |

| JVM 힙 대비 | 힙 1 GiB당 25 샤드 |

| CPU 대비 | 샤드 1개당 vCPU 1.5개 (초기 규모) |

| OpenSearch ≤ 2.15 | 노드당 최대 1,000 샤드 |

| OpenSearch 2.17+ | JVM 힙 16 GiB당 1,000 샤드, 노드당 최대 4,000 |

**핵심 원칙:** 샤드 수를 데이터 노드 수의 배수로 설정해 균등 분배한다. 예를 들어 데이터 노드 3대라면 프라이머리 샤드 3, 6, 9, 12개 등으로 설정한다.

2.3 인덱스 템플릿과 매핑 설계

> **예제 1: Composable Index Template**

인덱스 템플릿으로 모든 새 인덱스의 설정과 매핑을 일관되게 관리한다. `dynamic: strict`로 예상치 못한 필드 추가(매핑 폭발)를 방지하는 것이 핵심이다.

PUT _index_template/app-logs-template

{

"index_patterns": ["app-logs-*"],

"template": {

"settings": {

"number_of_shards": 3,

"number_of_replicas": 1,

"refresh_interval": "30s",

"index.translog.flush_threshold_size": "1024mb",

"index.codec": "zstd_no_dict",

"plugins.index_state_management.rollover_alias": "app-logs-write"

},

"mappings": {

"dynamic": "strict",

"properties": {

"@timestamp": { "type": "date" },

"level": { "type": "keyword" },

"message": { "type": "text", "analyzer": "standard" },

"service": { "type": "keyword" },

"trace_id": { "type": "keyword" },

"span_id": { "type": "keyword" },

"host": { "type": "keyword" },

"duration_ms": { "type": "float" },

"http_status": { "type": "short" },

"request_path":{ "type": "keyword" },

"user_id": { "type": "keyword" },

"metadata": {

"type": "object",

"enabled": false

}

}

}

},

"composed_of": ["common-settings"],

"priority": 200

}

**설정 포인트 해설:**

- `refresh_interval: 30s` — 기본 1초에서 늘려 CPU/IO 부하를 줄인다. 실시간 검색이 불필요한 로그 워크로드에 적합하다.

- `translog.flush_threshold_size: 1024mb` — JVM 힙의 25% 수준으로 설정해 flush 빈도를 낮춘다.

- `dynamic: strict` — 정의하지 않은 필드가 들어오면 인덱싱을 거부한다. 매핑 폭발(mapping explosion) 방지의 핵심이다.

- `index.codec: zstd_no_dict` — OpenSearch 2.9+에서 사용 가능한 zstd 압축. 기본 LZ4 대비 25–30% 스토리지 절감.

- `metadata.enabled: false` — 검색하지 않는 중첩 객체는 인덱싱을 비활성화해 리소스를 절약한다.

2.4 레플리카 전략

| 구성 | 권장 레플리카 수 | 이유 |

| ---------------------------- | ---------------- | ------------------------------- |

| 단일 AZ | 1 | 노드 장애 시 데이터 보호 |

| Multi-AZ (2 AZ) | 1 | AZ 장애 시 다른 AZ에서 서비스 |

| Multi-AZ with Standby (3 AZ) | 2 | AZ 장애 시에도 100% 데이터 가용 |

| 대량 벌크 인덱싱 중 | 0 (임시) | 인덱싱 속도 극대화 후 복원 |

**레플리카 0 작전 패턴:** 초기 대량 로딩 시 레플리카를 0으로 설정하고, 완료 후 다시 올리면 인덱싱 속도가 최대 2배까지 향상된다.

// 인덱싱 전: 레플리카 비활성화

PUT my-bulk-index/_settings

{ "index.number_of_replicas": 0 }

// 인덱싱 완료 후: 레플리카 복원

PUT my-bulk-index/_settings

{ "index.number_of_replicas": 1 }

2.5 Data Stream vs 기존 인덱스 패턴

OpenSearch 2.6+에서는 Data Stream을 지원한다. 기존 Rollover + Alias 패턴과 비교하면 다음과 같다.

| 항목 | Rollover + Alias | Data Stream |

| ---------------- | ----------------------------- | ------------------------------ |

| 초기 설정 | 초기 인덱스 + alias 수동 생성 | 인덱스 템플릿만 등록 |

| 쓰기 대상 | write alias | data stream 이름 직접 사용 |

| 백킹 인덱스 이름 | `app-logs-000001` | `.ds-app-logs-000001` |

| 삭제 | 인덱스 단위 | `DELETE _data_stream/app-logs` |

| 추천 워크로드 | 범용 | 시계열(append-only) 전용 |

3. ISM(Index State Management) 라이프사이클 관리

ISM은 OpenSearch의 인덱스 수명주기 자동화 엔진이다. Elasticsearch의 ILM에 해당하며, **Hot → Warm → Cold → Delete** 흐름을 자동으로 처리한다.

3.1 Hot-Warm-Cold-Delete 전체 정책

> **예제 2: ISM 전체 라이프사이클 정책**

PUT _plugins/_ism/policies/app-log-lifecycle

{

"policy": {

"description": "앱 로그 인덱스 라이프사이클: hot → warm → cold → delete",

"default_state": "hot",

"states": [

{

"name": "hot",

"actions": [

{

"rollover": {

"min_size": "30gb",

"min_index_age": "1d",

"min_doc_count": 10000000

}

}

],

"transitions": [

{ "state_name": "warm", "conditions": { "min_index_age": "3d" } }

]

},

{

"name": "warm",

"actions": [

{ "replica_count": { "number_of_replicas": 1 } },

{ "force_merge": { "max_num_segments": 1 } },

{ "allocation": {

"require": { "temp": "warm" },

"wait_for": true

}

}

],

"transitions": [

{ "state_name": "cold", "conditions": { "min_index_age": "30d" } }

]

},

{

"name": "cold",

"actions": [

{ "replica_count": { "number_of_replicas": 0 } },

{ "read_only": {} }

],

"transitions": [

{ "state_name": "delete", "conditions": { "min_index_age": "90d" } }

]

},

{

"name": "delete",

"actions": [

{

"notification": {

"destination": {

"slack": { "url": "https://hooks.slack.com/services/T.../B.../xxx" }

},

"message_template": {

"source": "인덱스 {{ctx.index}}가 보존 정책(90일)에 따라 삭제됩니다."

}

}

},

{ "delete": {} }

],

"transitions": []

}

],

"ism_template": [

{ "index_patterns": ["app-logs-*"], "priority": 100 }

]

}

}

**운영 포인트:**

- ISM 점검 주기 기본값은 **5분**이다. `plugins.index_state_management.job_interval`로 조정할 수 있다.

- `ism_template` 필드를 설정하면 패턴에 맞는 새 인덱스에 자동으로 정책이 붙는다.

- rollover 조건(`min_size`, `min_index_age`, `min_doc_count`)은 **OR** 조건이다 — 하나라도 충족하면 롤오버가 실행된다.

- `allocation.require.temp: warm` — 샤드를 `node.attr.temp=warm` 속성의 노드로 이동시킨다.

- force_merge 후에는 해당 인덱스에 더 이상 쓰기를 하지 않아야 한다.

3.2 ISM 운영 명령어

정책 실행 상태 확인 (특정 인덱스)

GET _plugins/_ism/explain/app-logs-000001

기존 인덱스에 정책 부착

POST _plugins/_ism/add/old-logs-2025-*

{ "policy_id": "app-log-lifecycle" }

정책 버전 업데이트 (관리 중인 인덱스에 새 정책 적용)

POST _plugins/_ism/change_policy/app-logs-*

{

"policy_id": "app-log-lifecycle-v2",

"state": "warm"

}

실패한 ISM 작업 재시도

POST _plugins/_ism/retry/app-logs-000003

특정 인덱스에서 ISM 정책 제거

POST _plugins/_ism/remove/app-logs-000005

3.3 Rollover + Alias 패턴 실전 구성

> **예제 3: Rollover Alias 초기 설정과 운영 흐름**

시계열 인덱스 운영의 표준 패턴이다. 쓰기 alias가 항상 최신 인덱스를 가리키고, ISM rollover가 새 인덱스를 생성·전환한다.

// 1단계: 초기 인덱스 생성 + alias 설정

PUT app-logs-000001

{

"aliases": {

"app-logs-write": { "is_write_index": true },

"app-logs-read": {}

}

}

// 2단계: ISM 정책의 rollover 액션이 자동으로 처리

// → app-logs-000002 생성

// → app-logs-write alias를 000002로 이동

// → 000001의 is_write_index를 false로 변경

// 읽기는 항상 read alias로 (모든 히스토리 대상)

GET app-logs-read/_search

{

"query": {

"bool": {

"must": [

{ "match": { "level": "ERROR" } },

{ "range": { "@timestamp": { "gte": "now-1h" } } }

]

}

},

"sort": [{ "@timestamp": "desc" }],

"size": 100

}

// 쓰기는 항상 write alias로

POST app-logs-write/_doc

{

"@timestamp": "2026-03-04T10:00:00Z",

"level": "ERROR",

"service": "payment-api",

"message": "결제 게이트웨이 타임아웃 발생",

"trace_id": "abc123",

"duration_ms": 30500,

"http_status": 504

}

**alias 상태 확인:**

현재 write index 확인

GET _alias/app-logs-write

전체 alias 구조 확인

GET _cat/aliases/app-logs-*?v&h=alias,index,is_write_index

4. 모니터링과 알림 설정

4.1 필수 모니터링 지표와 임계치

| 카테고리 | 지표 | 임계치 | 심각도 |

| ------------- | --------------------------- | ------------------------- | -------- |

| 클러스터 상태 | `ClusterStatus.red` | ≥ 1 (1분, 1회) | Critical |

| 클러스터 상태 | `ClusterStatus.yellow` | ≥ 1 (1분, 5연속) | Warning |

| 쓰기 차단 | `ClusterIndexWritesBlocked` | ≥ 1 (5분, 1회) | Critical |

| 노드 수 | `Nodes` | < 예상 노드 수 (1일, 1회) | Critical |

| 디스크 여유 | `FreeStorageSpace` | ≤ 노드 스토리지의 25% | Warning |

| JVM 힙 | `JVMMemoryPressure` | ≥ 95% (1분, 3연속) | Critical |

| Old Gen JVM | `OldGenJVMMemoryPressure` | ≥ 80% (1분, 3연속) | Warning |

| CPU 사용률 | `CPUUtilization` | ≥ 80% (15분, 3연속) | Warning |

| 마스터 CPU | `MasterCPUUtilization` | ≥ 50% (15분, 3연속) | Warning |

| 쓰기 스레드풀 | `ThreadpoolWriteRejected` | ≥ 1 (SUM, 차분) | Warning |

| 검색 스레드풀 | `ThreadpoolSearchRejected` | ≥ 1 (SUM, 차분) | Warning |

| 스냅샷 실패 | `AutomatedSnapshotFailure` | ≥ 1 (1분, 1회) | Critical |

4.2 클러스터 상태 점검 API

> **예제 4: 종합 클러스터 헬스체크 명령어 모음**

클러스터 전체 상태 (green/yellow/red)

GET _cluster/health

인덱스별 상태 확인

GET _cluster/health?level=indices

노드별 JVM, CPU, 디스크 상세

GET _nodes/stats/jvm,os,fs

핫 스레드 확인 (높은 CPU 원인 분석)

GET _nodes/hot_threads

스레드풀 상태 (대기, 거절 수 확인)

GET _cat/thread_pool?v&h=node_name,name,active,queue,rejected

샤드 배치 현황

GET _cat/shards?v&h=index,shard,prirep,state,docs,store,node&s=state

미할당 샤드 원인 분석

GET _cluster/allocation/explain

{

"index": "app-logs-000003",

"shard": 0,

"primary": true

}

인덱스별 크기 및 문서 수

GET _cat/indices?v&h=index,health,pri,rep,docs.count,store.size&s=store.size:desc

노드별 디스크 사용량

GET _cat/allocation?v&h=node,shards,disk.used,disk.avail,disk.percent

대기 중인 태스크 확인

GET _cat/pending_tasks?v

4.3 Slow Log로 병목 쿼리 잡기

PUT app-logs-*/_settings

{

"index.search.slowlog.threshold.query.warn": "10s",

"index.search.slowlog.threshold.query.info": "5s",

"index.search.slowlog.threshold.fetch.warn": "5s",

"index.indexing.slowlog.threshold.index.warn": "10s",

"index.indexing.slowlog.threshold.index.info": "5s"

}

`warn` 레벨을 10초, `info` 레벨을 5초로 설정하면, 10초 이상 걸리는 쿼리는 즉각 알림 대상이 된다. 이 로그를 OpenSearch Dashboards의 Discover에서 분석하거나, 별도 인덱스로 수집해 트렌드를 추적한다.

4.4 OpenSearch 알림(Alerting) 설정

> **예제 5: 클러스터 상태 모니터 + Slack 알림**

POST _plugins/_alerting/monitors

{

"type": "monitor",

"name": "Cluster Red Status Alert",

"monitor_type": "query_level_monitor",

"enabled": true,

"schedule": {

"period": { "interval": 1, "unit": "MINUTES" }

},

"inputs": [

{

"search": {

"indices": [".opensearch-sap-log-types-config"],

"query": {

"size": 0,

"query": {

"match_all": {}

}

}

}

}

],

"triggers": [

{

"query_level_trigger": {

"name": "Cluster Status Red",

"severity": "1",

"condition": {

"script": {

"source": "ctx.results[0].hits.total.value >= 0",

"lang": "painless"

}

},

"actions": [

{

"name": "Notify Slack",

"destination_id": "slack-destination-id",

"message_template": {

"source": "클러스터 상태가 RED입니다.\n클러스터: {{ctx.monitor.name}}\n시각: {{ctx.periodEnd}}\n즉시 확인이 필요합니다."

},

"throttle_enabled": true,

"throttle": { "value": 10, "unit": "MINUTES" }

}

]

}

}

]

}

**실전 팁:** 실제 운영에서는 `_cluster/health` API를 주기적으로 호출하는 외부 모니터링(Prometheus + Grafana, Datadog 등)이 더 안정적이다. OpenSearch 자체 알림은 클러스터가 불안정할 때 함께 동작하지 않을 수 있다.

5. 장애 시나리오와 복구 전략

5.1 주요 장애 유형별 대응

| 장애 | 원인 | 영향 | 즉시 대응 |

| ------------------- | --------------------------------- | --------------------------------------- | -------------------------------------------- |

| **Red 클러스터** | 프라이머리 샤드 미할당 | 데이터 유실 위험, 해당 인덱스 쓰기 실패 | `_cluster/allocation/explain`으로 원인 분석 |

| **Yellow 클러스터** | 레플리카 미할당 | 이중화 깨짐, 노드 장애 시 데이터 유실 | 노드 추가 또는 레플리카 수 조정 |

| **디스크 풀** | 스토리지 부족 | `ClusterBlockException`, 모든 쓰기 차단 | 오래된 인덱스 삭제, ISM 정책 점검 |

| **JVM OOM** | 힙 압력 > 95% | 노드 크래시, 연쇄 장애 | 무거운 쿼리 kill, 캐시 정리 |

| **Split Brain** | 마스터 노드 < 3대 + 네트워크 분리 | 데이터 불일치 | 전용 마스터 노드 3대 (3 AZ) 구성 필수 |

| **AZ 장애** | AWS 인프라 문제 | 레플리카 부족 시 데이터 유실 | Multi-AZ + 레플리카 2 구성 |

| **매핑 폭발** | dynamic mapping + 비정형 필드 | 힙 급증, 인덱싱 지연 | `dynamic: strict` 또는 `dynamic: false` 전환 |

5.2 디스크 풀 긴급 복구

클러스터가 read-only로 전환된 경우 다음 순서로 복구한다.

1. 가장 큰 인덱스 확인

GET _cat/indices?v&h=index,store.size&s=store.size:desc&format=json

2. 오래된 불필요 인덱스 삭제

DELETE old-logs-2024-*

3. read-only 차단 해제 (모든 인덱스)

PUT _all/_settings

{

"index.blocks.read_only_allow_delete": null

}

4. 클러스터 레벨 차단 해제

PUT _cluster/settings

{

"persistent": {

"cluster.blocks.read_only": false

}

}

5. 클러스터 상태 확인

GET _cluster/health

> 디스크 사용률이 85%를 넘으면 OpenSearch는 자동으로 인덱스를 read-only로 전환한다. 이 임계치는 `cluster.routing.allocation.disk.watermark.flood_stage` 설정으로 조정할 수 있지만, 근본적으로 디스크를 확보하는 것이 답이다.

5.3 스냅샷과 복구

> **예제 6: S3 스냅샷 리포지토리 등록 및 복원**

// S3 리포지토리 등록

PUT _snapshot/s3-backup

{

"type": "s3",

"settings": {

"bucket": "my-opensearch-snapshots",

"base_path": "daily",

"region": "ap-northeast-2",

"server_side_encryption": true,

"role_arn": "arn:aws:iam::123456789012:role/OpenSearchSnapshotRole"

}

}

// 수동 스냅샷 생성

PUT _snapshot/s3-backup/snapshot-2026-03-04

{

"indices": "app-logs-*",

"ignore_unavailable": true,

"include_global_state": false

}

// 스냅샷 상태 확인

GET _snapshot/s3-backup/snapshot-2026-03-04/_status

// 특정 인덱스만 복원 (이름 변경하여 기존 인덱스와 충돌 방지)

POST _snapshot/s3-backup/snapshot-2026-03-04/_restore

{

"indices": "app-logs-000001,app-logs-000002",

"ignore_unavailable": true,

"include_global_state": false,

"rename_pattern": "(.+)",

"rename_replacement": "$1_restored",

"index_settings": {

"index.number_of_replicas": 0

}

}

// 복원 완료 후 레플리카 복원

PUT app-logs-000001_restored/_settings

{ "index.number_of_replicas": 1 }

> `include_global_state: false`로 설정해야 보안 인덱스(`.opendistro_security`)와 충돌하지 않는다. 항상 이 옵션을 사용하라.

5.4 DR 전략 비교

| 전략 | RTO | RPO | 비용 | 복잡도 |

| ------------------------- | ------- | ------------------------- | --------------------------- | ------ |

| 스냅샷/복원 | 수 시간 | 마지막 스냅샷 이후 데이터 | 낮음 | 낮음 |

| Cross-Cluster Replication | 수 분 | < 1분 지연 | 높음 (2× 클러스터) | 중간 |

| 이중 전송 (Active-Active) | 거의 0 | 거의 0 | 높음 (2× 클러스터 + 라우팅) | 높음 |

**CCR 주의사항:** 복제가 12시간 이상 일시 중지되면 resume이 불가능하다. 중지 후 처음부터 다시 시작해야 한다.

6. AWS OpenSearch Service 운영 체크리스트

6.1 인스턴스 타입 선택

| 용도 | 권장 인스턴스 | 비고 |

| ------------------ | ---------------------------- | ---------------------------------- |

| 소규모 프로덕션 | `r6g.large` | 데이터 + 마스터 겸용 가능 |

| 범용 프로덕션 | `r6g.xlarge` ~ `r6g.2xlarge` | 가격 대비 성능 균형 |

| 로그 분석 (대용량) | `im4gn.*` | vCPU 대비 높은 스토리지 밀도 |

| 인덱싱 집약 분석 | OR1 인스턴스 | 가격 대비 성능 30% 향상 (2024+) |

| 전용 마스터 | 3대 × 3 AZ | 최신 세대, 항상 홀수 구성 |

| **프로덕션 금지** | `t2.*`, `t3.small` | 버스트 크레딧 소진 시 CPU 스로틀링 |

6.2 스토리지 티어 활용

| 티어 | 용도 | 백엔드 | 비용 (상대) |

| --------- | ------------------------- | -------------- | -------------- |

| Hot | 활성 읽기/쓰기 | EBS (gp3 권장) | 100% |

| UltraWarm | 읽기 전용, 빈도 낮은 조회 | S3 + 캐시 | ~40% |

| Cold | 아카이브, 온디맨드 분석 | S3 | \$0.024/GiB/월 |

- UltraWarm은 약 **2.5 TiB** 이상에서 비용 효율적이다.

- EBS는 gp3를 사용하라 — gp2 대비 9.6% 저렴하고 버스트 크레딧 걱정이 없다.

6.3 비용 최적화 전략

1. **ISM으로 자동 삭제** — 보존 기간이 지난 인덱스를 자동으로 정리한다.

2. **인덱스 롤업** — 세분화된 데이터를 집계한 뒤 UltraWarm/Cold로 이동한다.

3. **예약 인스턴스** — 14일 이상 안정 운영 후 구매. 1년 선결제 없음 ~30% 절감, 3년 전액 선결제 ~50% 절감.

4. **미사용 인덱스 삭제** — 유지 보수 작업(스냅샷 등) 시에도 리소스를 소비한다.

5. **Auto-Tune 활성화** — JVM, 큐 크기, 캐시를 자동 최적화한다.

7. OpenSearch vs Elasticsearch 운영 관점 비교

| 항목 | OpenSearch | Elasticsearch |

| ----------------- | ------------------------------------- | --------------------------------- |

| **라이선스** | Apache 2.0 (완전 오픈소스) | SSPL + Elastic License (7.11부터) |

| **보안 기능** | 무료 내장 (RBAC, FGAC, 감사 로그) | 유료 (Platinum+) |

| **알림** | 무료 플러그인 (Alerting) | Watcher (유료) |

| **이상 탐지** | 무료 플러그인 | ML (유료 Platinum+) |

| **수명주기 관리** | ISM (Index State Management) | ILM (Index Lifecycle Management) |

| **SQL 지원** | 무료 플러그인 + PPL | 유료 기능 |

| **관리형 서비스** | AWS OpenSearch Service | Elastic Cloud (멀티 클라우드) |

| **벡터 검색** | k-NN 플러그인 (FAISS, nmslib, Lucene) | 네이티브 통합 (8.x HNSW) |

| **클라우드** | AWS 중심 | AWS, GCP, Azure 지원 |

| **릴리스 주기** | ~3개월 마이너 릴리스 | ~2주 패치, ~4개월 마이너 |

**선택 기준 요약:**

- **OpenSearch**: AWS 인프라 중심, 비용 민감, 오픈소스 라이선스 필수, 로그/관측성 워크로드

- **Elasticsearch**: Elastic APM/SIEM 필요, 멀티 클라우드 배포, 벡터 검색·RAG 집중, Elastic 에코시스템(Kibana, Beats, Logstash) 활용

8. Elasticsearch → OpenSearch 마이그레이션 체크리스트

8.1 마이그레이션 방법 비교

| 방법 | 다운타임 | 적합 상황 | 복잡도 |

| ------------------- | ------------------ | --------------------------------- | ------------- |

| 스냅샷/복원 | 데이터 크기에 비례 | 단순 마이그레이션, 소규모 | 낮음 |

| 롤링 업그레이드 | 최소 | ES 6.8–7.10.2 → OS 1.x | 중간 |

| Remote Reindex | 없음 (라이브) | 버전 간 이동, 매핑 변경 필요 시 | 중간 |

| Migration Assistant | 거의 없음 | 대규모 클러스터, 다단계 버전 이동 | 낮음 (자동화) |

8.2 마이그레이션 전 체크리스트

- [ ] **버전 호환성 확인** — OpenSearch 1.x는 ES 7.10.2 기반. ES 7.11+ (SSPL)에서는 스냅샷/reindex 필요

- [ ] **플러그인 호환성 감사** — 서드파티 플러그인이 OpenSearch에서 지원되는지 확인

- [ ] **API 경로 변경** — `_opendistro/` → `_plugins/` (보안, 알림, ISM 등 모든 플러그인 API)

- [ ] **클라이언트 라이브러리 교체** — `elasticsearch-py` → `opensearch-py`, `elasticsearch-js` → `opensearch-js`

- [ ] **Kibana → OpenSearch Dashboards** 참조 이름 전수 변경

- [ ] **전체 스냅샷 백업** — 마이그레이션 시작 전 필수

- [ ] **기능 동등성 검증** — 사용 중인 기능이 OpenSearch에 존재하는지 확인 (공식 호환성 매트릭스 참조)

- [ ] **성능 벤치마크** — 동일 쿼리 세트로 기존 환경과 신규 환경 비교 측정

- [ ] **롤백 계획 수립** — 마이그레이션 실패 시 원래 클러스터로 복귀하는 절차 문서화

- [ ] **보안 구성 마이그레이션** — `securityadmin.sh` 또는 API로 역할/사용자/테넌트 재생성

8.3 Remote Reindex 실전 예제

> **예제 7: 원격 클러스터에서 실시간 Reindex**

POST _reindex

{

"source": {

"remote": {

"host": "https://old-es-cluster:9200",

"username": "admin",

"password": "********",

"socket_timeout": "60m",

"connect_timeout": "30s"

},

"index": "app-logs-2025-*",

"size": 5000,

"query": {

"range": {

"@timestamp": {

"gte": "2025-01-01",

"lt": "2026-01-01"

}

}

}

},

"dest": {

"index": "app-logs-migrated"

},

"conflicts": "proceed"

}

**주의:**

- `reindex.remote.allowlist` 설정에 원본 호스트를 등록해야 한다.

- 대규모 마이그레이션은 `wait_for_completion=false`로 비동기 실행하고 `GET _tasks/{task_id}`로 진행률을 모니터링한다.

- `size: 5000`은 한 번에 가져오는 scroll 크기다. 네트워크 대역폭에 따라 조정한다.

8.4 API 경로 변경표

| Elasticsearch / Open Distro | OpenSearch |

| --------------------------------- | ------------------------------ |

| `_opendistro/_security/` | `_plugins/_security/` |

| `_opendistro/_alerting/` | `_plugins/_alerting/` |

| `_opendistro/_ism/` | `_plugins/_ism/` |

| `_opendistro/_anomaly_detection/` | `_plugins/_anomaly_detection/` |

| `_opendistro/_sql/` | `_plugins/_sql/` |

| `_opendistro/_ppl/` | `_plugins/_ppl/` |

| `_opendistro/_knn/` | `_plugins/_knn/` |

9. 성능 튜닝 빠른 참조

| 설정 | 기본값 | 권장값 | 효과 |

| ------------------------------------- | ------- | ------------------------- | --------------------------------------- |

| `index.refresh_interval` | 1s | 30s 이상 (로그) | CPU/IO 절감, 검색 가시성 지연 |

| `index.translog.flush_threshold_size` | 512 MiB | JVM 힙의 25% | flush 빈도 감소 |

| `index.merge.policy` | tiered | `log_byte_size` (시계열) | `@timestamp` 범위 쿼리 성능 개선 |

| `index.codec` | LZ4 | `zstd_no_dict` | 디스크 25–30% 절감 |

| 벌크 요청 크기 | — | 5–15 MiB | 5 MiB부터 시작, 성능 정체 시점까지 증가 |

| 벌크 배치 수 | — | 5,000–10,000 docs/`_bulk` | 단건 호출 대비 극적인 처리량 향상 |

| `indices.memory.index_buffer_size` | 10% | 10–15% | 인덱싱 집약 시 버퍼 확대 |

| `search.max_buckets` | 65535 | 워크로드에 따라 | 집계 쿼리 안전 제한 |

**벌크 인덱싱 최적화 스크립트:**

from opensearchpy import OpenSearch, helpers

client = OpenSearch(

hosts=[{"host": "opensearch-node", "port": 9200}],

http_auth=("admin", "admin"),

use_ssl=True,

verify_certs=False,

)

def generate_actions(file_path):

"""로그 파일을 읽어 _bulk 액션으로 변환"""

with open(file_path) as f:

for line in f:

doc = json.loads(line)

yield {

"_index": "app-logs-write",

"_source": doc,

}

success, errors = helpers.bulk(

client,

generate_actions("/var/log/app/events.jsonl"),

chunk_size=5000,

max_retries=3,

request_timeout=60,

)

print(f"Indexed: {success}, Errors: {len(errors)}")

10. 보안 강화 체크리스트

| 항목 | 설정 | 비고 |

| ----------------- | ------------------------------- | ------------------------------ |

| 전송 암호화 | TLS 1.2+ 필수 | 노드 간, 클라이언트 간 모두 |

| 인증 | SAML / OIDC / Internal DB | AWS는 Cognito 또는 SAML 연동 |

| FGAC | 인덱스·필드·문서 수준 접근 제어 | `_plugins/_security/api/roles` |

| 감사 로그 | 읽기/쓰기 접근 기록 | 컴플라이언스 요건 시 필수 |

| IP 기반 접근 제어 | VPC + Security Group | 퍼블릭 엔드포인트 사용 금지 |

| API 키 관리 | 주기적 로테이션 | 90일 주기 권장 |

마무리: 운영자가 기억해야 할 7가지

1. **샤드 크기는 30–50 GiB를 기준으로** — 너무 작으면 메타데이터 오버헤드, 너무 크면 복구 지연.

2. **ISM은 선제적으로 설정** — 디스크가 차고 나서 만드는 것이 아니라 인덱스 생성과 동시에 적용한다.

3. **모니터링은 계층적으로** — 클러스터 상태 → JVM/CPU → 스레드풀 거절 → 슬로우 로그 순서로 파악한다.

4. **전용 마스터 노드 3대는 협상 불가** — Split Brain을 원천 차단하는 유일한 방법이다.

5. **스냅샷은 매일, 복원 테스트는 분기별** — 복원을 실제로 해보지 않은 백업은 백업이 아니다.

6. **dynamic: strict는 기본값** — 매핑 폭발은 예방만이 답이다. 사후 대응은 reindex뿐이다.

7. **디스크 워터마크를 항상 주시** — 75%에서 경고, 85%에서 read-only. ISM 자동 삭제가 유일한 안전망이다.

References

- [OpenSearch - Choosing the number of shards (AWS)](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/bp-sharding.html)

- [OpenSearch - Operational best practices (AWS)](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/bp.html)

- [OpenSearch - Index State Management](https://docs.opensearch.org/latest/im-plugin/ism/index/)

- [OpenSearch - ISM Policies](https://docs.opensearch.org/latest/im-plugin/ism/policies/)

- [OpenSearch - Index templates](https://docs.opensearch.org/latest/im-plugin/index-templates/)

- [OpenSearch - Data streams](https://docs.opensearch.org/latest/im-plugin/data-streams/)

- [OpenSearch - Tuning for indexing speed](https://docs.opensearch.org/latest/tuning-your-cluster/performance/)

- [OpenSearch - Alerting](https://docs.opensearch.org/latest/observing-your-data/alerting/index/)

- [OpenSearch - Security configuration](https://docs.opensearch.org/latest/security/configuration/index/)

- [AWS - Recommended CloudWatch alarms for OpenSearch](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/cloudwatch-alarms.html)

- [AWS - Sizing OpenSearch Service domains](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/sizing-domains.html)

- [AWS - Multi-tier storage for OpenSearch](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/multi-tier-storage.html)

- [AWS - Cross-cluster replication](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/replication.html)

- [AWS - Migrating to Amazon OpenSearch Service](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/migration.html)

- [OpenSearch - Migrate or upgrade](https://docs.opensearch.org/latest/upgrade-or-migrate/)

- [OpenSearch - Remote reindex](https://docs.opensearch.org/latest/im-plugin/remote-reindex/)

- [Benchmarking OpenSearch and Elasticsearch - Trail of Bits (2025)](https://blog.trailofbits.com/2025/03/06/benchmarking-opensearch-and-elasticsearch/)

현재 단락 (1/561)

OpenSearch는 로그 분석, 전문 검색, 관측성(Observability) 파이프라인의 핵심 엔진이다. 클러스터를 띄우는 것은 쉽지만, **안정적으로 운영하면서 비용을 제어*...

작성 글자: 0원문 글자: 19,449작성 단락: 0/561