Skip to content

Split View: OpenSearch 운영·관리·인덱스 설계 실전 가이드 2026

|

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

왜 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-3UltraWarm│UltraWarm│UltraWarm│
    (S3+캐시) (S3+캐시) (S3+캐시)   └──────────┘     └──────────┘     └──────────┘
노드 타입역할권장 사양배치 규칙
전용 마스터클러스터 메타데이터 관리, 샤드 할당최소 3대 (홀수), 최신 세대 인스턴스반드시 3 AZ 분산
Hot 데이터 노드활성 읽기/쓰기 처리높은 CPU + 빠른 스토리지 (gp3)AZ 균등 분배
Warm 데이터 노드읽기 위주, 빈도 낮은 조회높은 스토리지 밀도, 낮은 CPUUltraWarm (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 / 3010 프라이머리 샤드

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 레플리카 전략

구성권장 레플리카 수이유
단일 AZ1노드 장애 시 데이터 보호
Multi-AZ (2 AZ)1AZ 장애 시 다른 AZ에서 서비스
Multi-AZ with Standby (3 AZ)2AZ 장애 시에도 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 + AliasData Stream
초기 설정초기 인덱스 + alias 수동 생성인덱스 템플릿만 등록
쓰기 대상write aliasdata 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 JVMOldGenJVMMemoryPressure≥ 80% (1분, 3연속)Warning
CPU 사용률CPUUtilization≥ 80% (15분, 3연속)Warning
마스터 CPUMasterCPUUtilization≥ 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 전략 비교

전략RTORPO비용복잡도
스냅샷/복원수 시간마지막 스냅샷 이후 데이터낮음낮음
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 운영 관점 비교

항목OpenSearchElasticsearch
라이선스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 ServiceElastic 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-pyopensearch-py, elasticsearch-jsopensearch-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 DistroOpenSearch
_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_interval1s30s 이상 (로그)CPU/IO 절감, 검색 가시성 지연
index.translog.flush_threshold_size512 MiBJVM 힙의 25%flush 빈도 감소
index.merge.policytieredlog_byte_size (시계열)@timestamp 범위 쿼리 성능 개선
index.codecLZ4zstd_no_dict디스크 25–30% 절감
벌크 요청 크기5–15 MiB5 MiB부터 시작, 성능 정체 시점까지 증가
벌크 배치 수5,000–10,000 docs/_bulk단건 호출 대비 극적인 처리량 향상
indices.memory.index_buffer_size10%10–15%인덱싱 집약 시 버퍼 확대
search.max_buckets65535워크로드에 따라집계 쿼리 안전 제한

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

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 액션으로 변환"""
    import json
    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 DBAWS는 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 Operations, Management, and Index Design Practical Guide 2026

Why OpenSearch Operations Expertise Matters

OpenSearch is a core engine for log analytics, full-text search, and observability pipelines. Spinning up a cluster is easy, but operating it stably while controlling costs is an entirely different challenge. If the shard count for a single index is set incorrectly, the JVM heap of the entire cluster can become unstable. If a single ISM policy is missing, the disk fills up and writes stop.

This article covers the decision points that operators encounter in actual production, in the following order: cluster architecture, index design, ISM lifecycle, monitoring, incident response, security, and migration. All API examples are based on OpenSearch 2.x and can also be used on AWS OpenSearch Service.


1. Production Cluster Architecture

1.1 Node Role Separation

In production clusters, clearly separating each node type's role is essential for achieving both stability and performance.

+------------------------------------------------------------------+
|                      Client / Data Prepper                        |
|                        (Ingest Pipeline)                          |
+------------------------------+-----------------------------------+
                               |
          +--------------------+--------------------+
          v                    v                    v
   +------------+       +------------+       +------------+
   | Master-1   |       | Master-2   |       | Master-3   |
   | (AZ-a)     |       | (AZ-b)     |       | (AZ-c)     |
   | Dedicated  |       | Dedicated  |       | Dedicated  |
   +------------+       +------------+       +------------+
          |                    |                    |
          v                    v                    v
   +------------+       +------------+       +------------+
   | Hot-1      |       | Hot-2      |       | Hot-3      |
   | (AZ-a)     |       | (AZ-b)     |       | (AZ-c)     |
   | EBS gp3    |       | EBS gp3    |       | EBS gp3    |
   +------------+       +------------+       +------------+
          |                    |                    |
          v                    v                    v
   +------------+       +------------+       +------------+
   | Warm-1     |       | Warm-2     |       | Warm-3     |
   | UltraWarm  |       | UltraWarm  |       | UltraWarm  |
   | (S3+cache) |       | (S3+cache) |       | (S3+cache) |
   +------------+       +------------+       +------------+
Node TypeRoleRecommended SpecsPlacement Rule
Dedicated MasterCluster metadata management, shard allocationMinimum 3 (odd number), latest gen instancesMust be distributed across 3 AZs
Hot Data NodeActive read/write processingHigh CPU + fast storage (gp3)Even distribution across AZs
Warm Data NodeRead-heavy, infrequent queriesHigh storage density, low CPUUltraWarm (S3-based)
Coordinating NodeRequest routing, result aggregationHigh CPU + memory, minimal storageOnly for search-intensive workloads

1.2 Capacity Sizing Formula

The core formula for production cluster sizing.

Required Storage = Daily Raw Data
                 x (1 + Number of Replicas)
                 x 1.1 (Indexing Overhead)
                 x 1.15 (OS Reserved + Internal Operations Margin)
                 x Retention Period (days)

Example: 100 GiB daily logs, 1 replica, 30-day retention: 100 x 2 x 1.1 x 1.15 x 30 = 7,590 GiB

Disk usage should be maintained at 75% maximum. Above 75%, OpenSearch begins rejecting new shard allocations, and at 85% it switches to read-only.

Actual Provisioning = Required Storage / 0.75 = 10,120 GiB

2. Index Design and Shard Strategy

2.1 Shard Sizing Standards

Shard size varies by workload type. The recommended ranges from OpenSearch official documentation and AWS guides are as follows.

WorkloadRecommended Shard SizeRationale
Latency-sensitive search (e-commerce, autocomplete)10-30 GiBMinimize latency, fast recovery
Write-heavy / log analytics30-50 GiBMaximize write throughput
Maximum recommended limit50 GiBRecovery/reallocation time spikes above this

Primary shard count formula:

Primary Shard Count = (Raw Data Size + Growth Margin) x 1.1 / Target Shard Size

Example: 66 GiB daily logs, 4x growth expected, target shard size 30 GiB

(66 + 198) x 1.1 / 30 = approx. 10 primary shards

2.2 Per-Node Shard Limits

ConstraintLimit
Per JVM heap25 shards per 1 GiB of heap
Per CPU1.5 vCPUs per shard (initial sizing)
OpenSearch 2.15 or earlierMaximum 1,000 shards per node
OpenSearch 2.17+1,000 shards per 16 GiB JVM heap, max 4,000 per node

Key principle: Set the shard count as a multiple of data node count for even distribution. For example, with 3 data nodes, set primary shards to 3, 6, 9, 12, etc.

2.3 Index Templates and Mapping Design

Example 1: Composable Index Template

Index templates ensure consistent settings and mappings for all new indexes. Using dynamic: strict to prevent unexpected field additions (mapping explosion) is critical.

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
}

Setting points explained:

  • refresh_interval: 30s -- Increased from the default 1 second to reduce CPU/IO load. Suitable for log workloads where real-time search is not required.
  • translog.flush_threshold_size: 1024mb -- Set to 25% of JVM heap to reduce flush frequency.
  • dynamic: strict -- Rejects indexing of undefined fields. This is the key to preventing mapping explosion.
  • index.codec: zstd_no_dict -- zstd compression available in OpenSearch 2.9+. Achieves 25-30% storage savings compared to default LZ4.
  • metadata.enabled: false -- Disables indexing for nested objects that are not searched, saving resources.

2.4 Replica Strategy

ConfigurationRecommended ReplicasReason
Single AZ1Data protection against node failure
Multi-AZ (2 AZ)1Service from other AZ during AZ failure
Multi-AZ with Standby (3 AZ)2100% data availability during AZ failure
During bulk indexing0 (temporary)Maximize indexing speed, then restore

Replica 0 operation pattern: Setting replicas to 0 during initial bulk loading and restoring afterward can improve indexing speed by up to 2x.

// Before indexing: disable replicas
PUT my-bulk-index/_settings
{ "index.number_of_replicas": 0 }

// After indexing complete: restore replicas
PUT my-bulk-index/_settings
{ "index.number_of_replicas": 1 }

2.5 Data Stream vs Traditional Index Pattern

OpenSearch 2.6+ supports Data Streams. Compared to the traditional Rollover + Alias pattern:

ItemRollover + AliasData Stream
Initial SetupManual initial index + alias creationOnly register index template
Write TargetWrite aliasUse data stream name directly
Backing Index Nameapp-logs-000001.ds-app-logs-000001
DeletionPer indexDELETE _data_stream/app-logs
Recommended WorkloadGeneral purposeTime-series (append-only) only

3. ISM (Index State Management) Lifecycle Management

ISM is OpenSearch's index lifecycle automation engine. It corresponds to Elasticsearch's ILM, automatically handling the Hot, Warm, Cold, Delete flow.

3.1 Hot-Warm-Cold-Delete Full Policy

Example 2: ISM Full Lifecycle Policy

PUT _plugins/_ism/policies/app-log-lifecycle
{
  "policy": {
    "description": "App log index lifecycle: 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": "Index {{ctx.index}} will be deleted according to retention policy (90 days)."
              }
            }
          },
          { "delete": {} }
        ],
        "transitions": []
      }
    ],
    "ism_template": [
      { "index_patterns": ["app-logs-*"], "priority": 100 }
    ]
  }
}

Operational notes:

  • The default ISM check interval is 5 minutes. It can be adjusted via plugins.index_state_management.job_interval.
  • Setting the ism_template field automatically attaches the policy to new indexes matching the pattern.
  • Rollover conditions (min_size, min_index_age, min_doc_count) are OR conditions -- rollover executes when any one is met.
  • allocation.require.temp: warm -- Moves shards to nodes with the node.attr.temp=warm attribute.
  • After force_merge, no further writes should be made to that index.

3.2 ISM Operation Commands

# Check policy execution status (specific index)
GET _plugins/_ism/explain/app-logs-000001

# Attach policy to existing indexes
POST _plugins/_ism/add/old-logs-2025-*
{ "policy_id": "app-log-lifecycle" }

# Update policy version (apply new policy to managed indexes)
POST _plugins/_ism/change_policy/app-logs-*
{
  "policy_id": "app-log-lifecycle-v2",
  "state": "warm"
}

# Retry failed ISM operations
POST _plugins/_ism/retry/app-logs-000003

# Remove ISM policy from a specific index
POST _plugins/_ism/remove/app-logs-000005

3.3 Rollover + Alias Pattern Practical Setup

Example 3: Rollover Alias Initial Setup and Operation Flow

This is the standard pattern for time-series index operations. The write alias always points to the latest index, and ISM rollover creates and switches to new indexes.

// Step 1: Create initial index + alias setup
PUT app-logs-000001
{
  "aliases": {
    "app-logs-write": { "is_write_index": true },
    "app-logs-read": {}
  }
}

// Step 2: ISM policy's rollover action handles automatically
//   -> Creates app-logs-000002
//   -> Moves app-logs-write alias to 000002
//   -> Sets 000001's is_write_index to false

// Reads always use the read alias (targets all history)
GET app-logs-read/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "level": "ERROR" } },
        { "range": { "@timestamp": { "gte": "now-1h" } } }
      ]
    }
  },
  "sort": [{ "@timestamp": "desc" }],
  "size": 100
}

// Writes always use the write alias
POST app-logs-write/_doc
{
  "@timestamp": "2026-03-04T10:00:00Z",
  "level": "ERROR",
  "service": "payment-api",
  "message": "Payment gateway timeout occurred",
  "trace_id": "abc123",
  "duration_ms": 30500,
  "http_status": 504
}

Alias status check:

# Check current write index
GET _alias/app-logs-write

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

4. Monitoring and Alert Configuration

4.1 Essential Monitoring Metrics and Thresholds

CategoryMetricThresholdSeverity
Cluster StatusClusterStatus.red1 or more (1 min, 1 occurrence)Critical
Cluster StatusClusterStatus.yellow1 or more (1 min, 5 consecutive)Warning
Write BlockingClusterIndexWritesBlocked1 or more (5 min, 1 occurrence)Critical
Node CountNodesunder expected count (1 day, 1 occurrence)Critical
Free DiskFreeStorageSpace25% or less of node storageWarning
JVM HeapJVMMemoryPressure95% or more (1 min, 3 consecutive)Critical
Old Gen JVMOldGenJVMMemoryPressure80% or more (1 min, 3 consecutive)Warning
CPU UtilizationCPUUtilization80% or more (15 min, 3 consecutive)Warning
Master CPUMasterCPUUtilization50% or more (15 min, 3 consecutive)Warning
Write ThreadpoolThreadpoolWriteRejected1 or more (SUM, delta)Warning
Search ThreadpoolThreadpoolSearchRejected1 or more (SUM, delta)Warning
Snapshot FailureAutomatedSnapshotFailure1 or more (1 min, 1 occurrence)Critical

4.2 Cluster Status Check APIs

Example 4: Comprehensive Cluster Health Check Command Collection

# Overall cluster status (green/yellow/red)
GET _cluster/health

# Per-index status check
GET _cluster/health?level=indices

# Per-node JVM, CPU, disk details
GET _nodes/stats/jvm,os,fs

# Hot threads check (for high CPU root cause analysis)
GET _nodes/hot_threads

# Thread pool status (check pending and rejected counts)
GET _cat/thread_pool?v&h=node_name,name,active,queue,rejected

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

# Unassigned shard root cause analysis
GET _cluster/allocation/explain
{
  "index": "app-logs-000003",
  "shard": 0,
  "primary": true
}

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

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

# Check pending tasks
GET _cat/pending_tasks?v

4.3 Catching Bottleneck Queries with 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"
}

Setting the warn level to 10 seconds and info level to 5 seconds makes queries taking over 10 seconds immediate alerting targets. Analyze these logs in OpenSearch Dashboards' Discover or collect them into a separate index to track trends.

4.4 OpenSearch Alerting Configuration

Example 5: Cluster Status Monitor + Slack Alert

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": "Cluster status is RED.\nCluster: {{ctx.monitor.name}}\nTime: {{ctx.periodEnd}}\nImmediate investigation required."
            },
            "throttle_enabled": true,
            "throttle": { "value": 10, "unit": "MINUTES" }
          }
        ]
      }
    }
  ]
}

Practical tip: In actual operations, external monitoring that periodically calls the _cluster/health API (Prometheus + Grafana, Datadog, etc.) is more reliable. OpenSearch's built-in alerting may not function when the cluster itself is unstable.


5. Incident Scenarios and Recovery Strategies

5.1 Response by Major Incident Type

IncidentCauseImpactImmediate Response
Red ClusterUnassigned primary shardsData loss risk, write failure on affected indexAnalyze cause with _cluster/allocation/explain
Yellow ClusterUnassigned replicasRedundancy broken, data loss risk on node failureAdd nodes or adjust replica count
Disk FullInsufficient storageClusterBlockException, all writes blockedDelete old indexes, review ISM policies
JVM OOMHeap pressure over 95%Node crash, cascading failuresKill heavy queries, clear caches
Split BrainFewer than 3 master nodes + network partitionData inconsistencyDedicated master nodes (3 across 3 AZs) is mandatory
AZ FailureAWS infrastructure issueData loss if replicas insufficientMulti-AZ + 2 replicas configuration
Mapping Explosiondynamic mapping + unstructured fieldsHeap spike, indexing delaysSwitch to dynamic: strict or dynamic: false

5.2 Disk Full Emergency Recovery

When the cluster has switched to read-only, recover in the following order.

# 1. Identify the largest indexes
GET _cat/indices?v&h=index,store.size&s=store.size:desc&format=json

# 2. Delete old unnecessary indexes
DELETE old-logs-2024-*

# 3. Remove read-only block (all indexes)
PUT _all/_settings
{
  "index.blocks.read_only_allow_delete": null
}

# 4. Remove cluster-level block
PUT _cluster/settings
{
  "persistent": {
    "cluster.blocks.read_only": false
  }
}

# 5. Check cluster status
GET _cluster/health

When disk usage exceeds 85%, OpenSearch automatically switches indexes to read-only. This threshold can be adjusted via the cluster.routing.allocation.disk.watermark.flood_stage setting, but fundamentally freeing up disk space is the answer.

5.3 Snapshots and Recovery

Example 6: S3 Snapshot Repository Registration and Restoration

// Register S3 repository
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"
  }
}

// Create manual snapshot
PUT _snapshot/s3-backup/snapshot-2026-03-04
{
  "indices": "app-logs-*",
  "ignore_unavailable": true,
  "include_global_state": false
}

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

// Restore specific indexes only (rename to avoid conflicts with existing indexes)
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
  }
}

// Restore replicas after restoration is complete
PUT app-logs-000001_restored/_settings
{ "index.number_of_replicas": 1 }

Set include_global_state: false to avoid conflicts with security indexes (.opendistro_security). Always use this option.

5.4 DR Strategy Comparison

StrategyRTORPOCostComplexity
Snapshot/RestoreHoursData since last snapshotLowLow
Cross-Cluster ReplicationMinutesUnder 1 min lagHigh (2x cluster)Medium
Dual Ingestion (Active-Active)Near zeroNear zeroHigh (2x cluster + routing)High

CCR caveat: If replication is paused for more than 12 hours, it cannot be resumed. It must be restarted from scratch.


6. AWS OpenSearch Service Operations Checklist

6.1 Instance Type Selection

Use CaseRecommended InstanceNotes
Small productionr6g.largeCan serve as combined data + master
General productionr6g.xlarge to r6g.2xlargePrice-performance balance
Log analytics (large)im4gn.*High storage density per vCPU
Indexing-intensiveOR1 instances30% better price-performance (2024+)
Dedicated Master3 instances across 3 AZsLatest generation, always odd count
Not for productiont2.*, t3.smallCPU throttling when burst credits deplete

6.2 Storage Tier Utilization

TierUse CaseBackendCost (Relative)
HotActive read/writeEBS (gp3 recommended)100%
UltraWarmRead-only, infrequent queriesS3 + cache~40%
ColdArchive, on-demand analyticsS3$0.024/GiB/month
  • UltraWarm becomes cost-effective above approximately 2.5 TiB.
  • Use gp3 for EBS -- 9.6% cheaper than gp2 with no burst credit concerns.

6.3 Cost Optimization Strategies

  1. Automatic deletion with ISM -- Automatically clean up indexes past their retention period.
  2. Index rollups -- Aggregate granular data then move to UltraWarm/Cold.
  3. Reserved Instances -- Purchase after 14+ days of stable operation. 1-year no upfront saves ~30%, 3-year all upfront saves ~50%.
  4. Delete unused indexes -- They consume resources even during maintenance tasks (snapshots, etc.).
  5. Enable Auto-Tune -- Automatically optimizes JVM, queue sizes, and caches.

7. OpenSearch vs Elasticsearch Operations Perspective Comparison

CategoryOpenSearchElasticsearch
LicenseApache 2.0 (fully open source)SSPL + Elastic License (from 7.11)
SecurityFree built-in (RBAC, FGAC, audit log)Paid (Platinum+)
AlertingFree plugin (Alerting)Watcher (paid)
Anomaly DetectionFree pluginML (paid Platinum+)
Lifecycle MgmtISM (Index State Management)ILM (Index Lifecycle Management)
SQL SupportFree plugin + PPLPaid feature
Managed ServiceAWS OpenSearch ServiceElastic Cloud (multi-cloud)
Vector Searchk-NN plugin (FAISS, nmslib, Lucene)Native integration (8.x HNSW)
CloudAWS-centricAWS, GCP, Azure support
Release Cycle~3 month minor releases~2 week patches, ~4 month minor

Selection criteria summary:

  • OpenSearch: AWS infrastructure-centric, cost-sensitive, open-source license required, log/observability workloads
  • Elasticsearch: Elastic APM/SIEM needed, multi-cloud deployment, vector search/RAG focus, Elastic ecosystem (Kibana, Beats, Logstash) utilization

8. Elasticsearch to OpenSearch Migration Checklist

8.1 Migration Method Comparison

MethodDowntimeBest ForComplexity
Snapshot/RestoreProportional to dataSimple migration, small scaleLow
Rolling UpgradeMinimalES 6.8-7.10.2 to OS 1.xMedium
Remote ReindexNone (live)Cross-version moves, mapping changesMedium
Migration AssistantNear zeroLarge clusters, multi-step version movesLow (automated)

8.2 Pre-Migration Checklist

  • Version compatibility check -- OpenSearch 1.x is based on ES 7.10.2. ES 7.11+ (SSPL) requires snapshot/reindex
  • Plugin compatibility audit -- Verify third-party plugins are supported in OpenSearch
  • API path changes -- _opendistro/ to _plugins/ (security, alerting, ISM, and all plugin APIs)
  • Client library replacement -- elasticsearch-py to opensearch-py, elasticsearch-js to opensearch-js
  • Kibana to OpenSearch Dashboards -- Complete all reference name changes
  • Full snapshot backup -- Required before starting migration
  • Feature parity verification -- Confirm features in use exist in OpenSearch (refer to official compatibility matrix)
  • Performance benchmark -- Measure with identical query sets on old and new environments
  • Rollback plan -- Document the procedure for reverting to the original cluster if migration fails
  • Security configuration migration -- Recreate roles/users/tenants via securityadmin.sh or API

8.3 Remote Reindex Practical Example

Example 7: Live Reindex from Remote Cluster

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"
}

Caution:

  • The source host must be registered in the reindex.remote.allowlist setting.
  • For large-scale migrations, run asynchronously with wait_for_completion=false and monitor progress with GET _tasks/{task_id}.
  • size: 5000 is the scroll size per fetch. Adjust based on network bandwidth.

8.4 API Path Change Reference

Elasticsearch / Open DistroOpenSearch
_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. Performance Tuning Quick Reference

SettingDefaultRecommendedEffect
index.refresh_interval1s30s or more (for logs)CPU/IO savings, delayed search visibility
index.translog.flush_threshold_size512 MiB25% of JVM heapReduced flush frequency
index.merge.policytieredlog_byte_size (time-series)Improved @timestamp range query performance
index.codecLZ4zstd_no_dict25-30% disk savings
Bulk request size--5-15 MiBStart at 5 MiB, increase until performance plateaus
Bulk batch count--5,000-10,000 docs/_bulkDramatic throughput improvement over single calls
indices.memory.index_buffer_size10%10-15%Expanded buffer for indexing-intensive workloads
search.max_buckets65535Depends on workloadSafety limit for aggregation queries

Bulk indexing optimization script:

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):
    """Reads a log file and converts to _bulk actions"""
    import json
    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. Security Hardening Checklist

ItemSettingNotes
Transport EncryptionTLS 1.2+ requiredBoth inter-node and client
AuthenticationSAML / OIDC / Internal DBAWS uses Cognito or SAML
FGACIndex/field/document level access_plugins/_security/api/roles
Audit LogRead/write access recordingRequired for compliance
IP-Based AccessVPC + Security GroupNever use public endpoints
API Key ManagementPeriodic rotation90-day cycle recommended

Conclusion: 7 Things Every Operator Must Remember

  1. Target 30-50 GiB shard size -- Too small means metadata overhead, too large means recovery delays.
  2. Set up ISM proactively -- Apply it when indexes are created, not after the disk fills up.
  3. Monitor in layers -- Cluster status, then JVM/CPU, then thread pool rejections, then slow logs.
  4. 3 dedicated master nodes are non-negotiable -- The only way to prevent split brain at its source.
  5. Snapshot daily, test restoration quarterly -- A backup that has never been restored is not a backup.
  6. dynamic: strict should be the default -- Prevention is the only answer for mapping explosion. The only remediation is reindex.
  7. Always watch disk watermarks -- Warning at 75%, read-only at 85%. ISM auto-deletion is the only safety net.

References

Quiz

Q1: What is the main topic covered in "OpenSearch Operations, Management, and Index Design Practical Guide 2026"?

A comprehensive guide for OpenSearch cluster operators covering index design, shard/replica strategies, ISM rollover lifecycle, monitoring and alerting, incident response, security hardening, and migration with practical API examples.

Q2: Why OpenSearch Operations Expertise Matters? OpenSearch is a core engine for log analytics, full-text search, and observability pipelines. Spinning up a cluster is easy, but operating it stably while controlling costs is an entirely different challenge.

Q3: Describe the Production Cluster Architecture. 1.1 Node Role Separation In production clusters, clearly separating each node type's role is essential for achieving both stability and performance. 1.2 Capacity Sizing Formula The core formula for production cluster sizing.

Q4: Describe the Index Design and Shard Strategy. 2.1 Shard Sizing Standards Shard size varies by workload type. The recommended ranges from OpenSearch official documentation and AWS guides are as follows.

Q5: How does ISM (Index State Management) Lifecycle Management work? ISM is OpenSearch's index lifecycle automation engine. It corresponds to Elasticsearch's ILM, automatically handling the Hot, Warm, Cold, Delete flow. 3.1 Hot-Warm-Cold-Delete Full Policy Operational notes: The default ISM check interval is 5 minutes.