Skip to content
Published on

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

Authors
  • Name
    Twitter

왜 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