Opensearch를 사용하게 된 이유

Elasticsearch를 기본적으로 사용하는데 문제는 없지만 일부 기능들이 Enterprise license에서만 제공하는 기능들이 있습니다. 그런 이유로 회사에서 Elasticsearch에서 Opensearch로 데이터를 Migration 하는 작업을 진행하였습니다. 저희 같은 경우 비용 절감을 위해 AWS Managed service를 이용하고 있지 않다는 점을 알아주셨으면 좋겠습니다. 이번 문서에서는 데이터를 Migration 하는 방법에 대해서 정리해지만 해당 방법이 최선이라는 생각은 하지 않습니다. 더 좋은 방법을 알고 계신다면 댓글로 남겨주세요.

 

시작하기 전에 간단한 설명

먼저 저의 Elasticsearch의 클러스터 운영방법에 대해서 간단하게 설명 드리겠습니다. 저희 같은 경우 인덱스를 날짜 별로 관리를 하고 있기 때문에 이전의 날짜에는 데이터가 업데이트되지 않는 방식으로 운영하고 있습니다. 그리고 Snapshot의 경우 AWS S3에 저장하고 있습니다.

 

Opensearch 클러스터 구성

달리는 차의 바퀴를 갈이 끼우는 것은 매우 어려운 일이기 때문에 우리는 서비스를 중단하지 않고 데이터를 이주하기 위해서는 Elasticsearch의 동일한 스팩의 Opensearch 클러스터를 미리 구성해야 합니다. 

 

기존에 있던 Elasticsearch와 동일한 스팩의 Opensearch를 클러스터를 구성하고 Elasticsearch와 Opensearch의 스냅샷 Repository를 동일한 S3를 바라보도록 설정하였습니다. 이렇게 설정한 것을 그림으로 표현하면 다음과 같습니다.

Elasticsearch -> Opensearch 데이터 Migration

1. Elasticsearch 에서 Snapshot을 생성합니다.

Elasticsearch와 Opensearch 모두 동일하게 바라보는 Repository로 Snapshot을 생성합니다.

Elasticsearch 스냅샷 생성 방법 (https://stdhsw.tistory.com/entry/Elasticsearch-710-%EB%B2%84%EC%A0%84-AWS-S3-Snapshot-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0Kubernetes)

 

2. Indexing하는 Processor를 Elasticsearch와 Opensearch 둘 다 전송합니다.

데이터를 인덱싱하는 Processor의 타겟을 Elasticsearch, Opensearch 둘 다 지정합니다. 대부분 FluentBit, Vector 등 멀티 타겟을 지정하는 것을 지원하지만 만약 인덱싱하는 Processor에 멀티 타겟을 지정할 수 없다면 envoy proxy에 대해서 알아보는 것을 권장합니다.

이렇게 두 타켓을 지정하였다면 아래 그림과 같은 모습입니다.

 

3. 하루전날짜, 이틀전날짜 인덱스는 스냅샷으로 이주

1번에서 수행한 Elasticsearch의 스냅샷을 이용하여 하루전날짜, 이틀전날짜의 인덱스를 Opensearch에서 복원합니다.

Opensearch 스냅샷 복원방법 (https://stdhsw.tistory.com/entry/Opensearch-Snapshot-AWS-S3%EB%B0%B1%EC%97%85-%EB%B0%8F-%EB%B3%B5%EA%B5%AC)

 

4. Search processor의 타겟을 변경합니다.

Search processor의 타겟을 변경하고 안정적으로 서비스가 동작하는지 전체적으로 검토합니다. 그리고 서비스가 안정적이라고 확신하면 그때 Indexing Processor가 Elasticsearch에 인덱싱 하지 않도록 합니다.

 

5. Elasticsearch의 자원을 반납합니다.

이제 마지막으로 모든 것이 정상적으로 운영이 가능하다면 Elasticsearch의 자원을 반납해줍니다.

 

마지막으로

지금까지 Elasticsearch에서 Opensearch로 Migration하는 방법을 정리해 봤는데요 이 방법은 저의 머릿속에서의 최선의 방법이라 생각해서 수행한 방법입니다. 다행히 서비스 동작에 문제는 없었지만 해당 방법이 최선의 방법이라는 생각은 하지 않습니다. 만약 이 방법보다 더 좋은 방법을 알고 계신다면 댓글로 공유해 주시면 감사하겠습니다.

 

'Opensearch' 카테고리의 다른 글

Opensearch Searchable Snapshot  (0) 2023.07.17
Opensearch Snapshot (AWS S3)백업 및 복구  (0) 2023.07.17
Opensearch SM(Snapshot Management)  (0) 2023.07.17
Opensearch Index Template  (0) 2023.07.14
Opensearch ISM(Index State Management)  (0) 2023.07.13

이 문서는 2023년 7월에 작성되었으며 Opensearch 2.8.0 버전입니다.

Opensearch의 Searchable Snapshot은 클러스터에서 삭제된 Index를 Snapshot으로 복원하여 데이터를 검색할 수 있도록 하는 기능을 수행합니다. Searchable Snapshot 기능을 사용하기 위해서는 Snapshot에 대한 사전 지식이 필요합니다. Snapshot 글을 먼저 참고하시면 진행하는데 많은 도움이 됩니다.

https://stdhsw.tistory.com/entry/Opensearch-Snapshot-AWS-S3%EB%B0%B1%EC%97%85-%EB%B0%8F-%EB%B3%B5%EA%B5%AC

 

클러스터 노드 구성

Opensearch에서 Searchable Snapshot 기능을 사용하기 위해서는 "search" role을 가진 노드가 필요합니다. 저는 한정된 자원으로 Master Node 1대, Data Node 2대, Search Node 1대로 클러스터를 구성하여 테스트하였습니다. (웬만하면 Master Role을 가진 노드를 3대 이상의 홀수 개수만큼 유지하는 것을 추천합니다.)

  Master Node Data Node Search Node
개수 1 2 1
Role master data
ingest
saerch

Master Node는 Opensearch의 클러스터를 관리하는 역할을 수행하며 Data Node는 실제 데이터가 저장되고 연산 기능을 수행합니다. Search Node는 Snapshot으로 부터 인덱스 데이터를 가져와 데이터를 검색할 수 있는 기능을 제공합니다.

Snapshot으로부터 데이터를 가져오는데 인덱스의 크기가 크면 매우 오래 걸릴 수 있으며 데이터를 가져오는 과정에서 네트워크 비용이 발생할 수 있습니다.

 

Snapshot 생성 시 설정

일반적인 스냅샷과 다르게 Searchable Snapshot 기능을 사용하기 위해서는 Snapshot 생성 시 "storage_type": "remote_snapshot"의 설정이 필요합니다.

PUT _snapshot/스냅샷레포지토리/스냅샷명?wait_for_completion=true
{
  "indices": "*",
  "ignore_unavailable": true,
  "include_global_state": false,
  "partial": false,
  "storage_type": "remote_snapshot"
}

storage_type에 대한 설정은 Opensearch 공식 Document에서는 다음과 같이 설명하였습니다.

 

local indicates that all snapshot metadata and index data will be downloaded to local storage.
remote_snapshot indicates that snapshot metadata will be downloaded to the cluster, but the remote repository will remain the authoritative store of the index data. Data will be downloaded and cached as necessary to service queries. At least one node in the cluster must be configured with the search role in order to restore a snapshot using the type remote_snapshot.
Defaults to local.

 

Searchable Snapshot API

평상시에는 인덱스가 존재하지 않고 필요시 Snapshot에서 복원하여 검색할 수 있도록 합니다.

POST _snapshot/스냅샷레포지토리/스냅샷명/_restore
{
  "indices": "복원인덱스",
  "storage_type": "remote_snapshot",  
  "rename_pattern": "(.+)",
  "rename_replacement": "searchable_$1",
  "index_settings": {
      "index.number_of_replicas": 0
  }
}
  • indices : 스냅샷에 포함된 복원할 인덱스
  • storage_type : search node로 복원하기 위한 설정
  • rename_pattern : searchable snapshot으로 복원한 인덱스의 새로운 이름 규칙
  • rename_replacement : 복원한 인덱스의 이름을 재지정합니다.
  • index.number_of_replicas : 복원한 인덱스의 Replica shard 개수를 설정합니다.

인덱스를 복원하면 "saerchable_원래인덱스명" 형식으로 인덱스가 복원됩니다.

 

복원 결과 샤드 구성

일반적인 인덱스의 Shard 개수는 4개로 설정하였고 Replica Shard는 1개씩 구성하도록 설정하였습니다.

Searchable Snapshot으로 복원한 인덱스는 데이터를 넣을 수 없고 검색 기능만 가능하기 때문에 Replica shard가 불필요하다고 판단하여 저는 복원한 인덱스에는 Replica shard를 구성하지 않았습니다. Searchable Snapshot으로 복원하면 Search Node에만 샤드가 구성되며 복원 완료 시 검색이 가능합니다.

 

 

공식 Opensearch Document

https://opensearch.org/docs/latest/tuning-your-cluster/availability-and-recovery/snapshots/snapshot-restore/

 

Take and restore snapshots

Take and restore snapshots

opensearch.org

Opensearch의 Snapshot은 Opensearch Cluster의 데이터를 안정적으로 백업하고 복원하는 기능을 제공합니다. Snapshot은 인덱스 데이터, 설정, 매핑 및 기타 Opensearch 클러스터 상태 정보를 모두 포함할 수 있어 Cluster 운영에 안정성을 높일 수 있습니다.

 

Dockerfile로 이미지 생성

Opensearch에서는 AWS S3에 Snapshot 기능을 기본적으로 제공하고 있지 않습니다. 그렇기 때문에 Snapshot의 기능을 사용하기 전에 먼저 AWS S3 Plugin을 먼저 설치해야 합니다. Kubernets에서는 매번 재시작할 때마다 플러그인을 설치할 수 없으니 Dockerfile로 AWS S3 Plugin이 설치된 이미지를 생성하여 사용해야 합니다. 한글로 입력된 곳에 옳바른 값을 넣어주세요.

FROM opensearchproject/opensearch:2.8.0

ENV AWS_ACCESS_KEY_ID="에세스키"
ENV AWS_SECRET_ACCESS_KEY="시크릿키"

RUN /usr/share/opensearch/bin/opensearch-plugin install --batch repository-s3
RUN /usr/share/opensearch/bin/opensearch-keystore create

RUN echo $AWS_ACCESS_KEY_ID | /usr/share/opensearch/bin/opensearch-keystore add --stdin s3.client.default.access_key
RUN echo $AWS_SECRET_ACCESS_KEY | /usr/share/opensearch/bin/opensearch-keystore add --stdin s3.client.default.secret_key

 

저의 경우 Kubernetes환경에서 Opensearch를 사용하기 위해 Dockerfile을 이용하여 이미지를 만들어서 사용하였지만 만약 호스트 환경의 Opensearch인 경우 굳이 이미지를 만들지 않고 "bin/opensearch-plugin install --batch repository-s3" 명령어로 플러그인 설치를 하고 Opensearch를 재시작하시면 됩니다.

 

Snapshot Repository 생성

Snapshot을 생성하기 전에 먼저 Snapshot을 저장할 수 있는 Repository를 생성해야 합니다. 저는 my_repository라는 이름의 Snapshot Repository를 생성하였습니다. 한글로 입력된 곳에 옳바른 값을 넣어주세요.

PUT _snapshot/my_repository
{
  "type": "s3",
  "settings": {
    "bucket": "버킷",
    "base_path": "저장경로",
    "compress": true
  }
}

 

Snapshot Repository 확인

# Snapshot Repository의 정보
GET _snapshot/my_repository

# Snapshot Repository에 저장된 스냅샷 리스트 확인
GET _snapshot/my_repository/_all

 

Snapshot 백업

Snapshot Repository를 정상적으로 생성하였다면 해당 Repository를 이용하여 Snapshot을 생성할 수 있습니다. 저는 my_snapshot_1이라는 이름으로 스냅샷을 생성하였고 인덱스의 데이터만 스냅샷을 생성하도록 설정하였습니다. 데이터의 양이 많다면 스냅샷을 생성하는데 오랜 시간이 걸립니다. wait_for_completion=true 옵션을 설정하지 않으면 스냅샷이 생성이 완료되었는지 확인할 수 없습니다. 그래서 wait_for_completion=true 옵션을 통하여 스냅샷이 완전히 생성될 때까지 대기하여 정상적으로 생성되었는지 인지할 수 있습니다.

PUT _snapshot/my_repository/my_snapshot_1?wait_for_completion=true
{
  "indices": "*",
  "ignore_unavailable": true,
  "include_global_state": false,
  "partial": false
}
  • indices : 스냅샷 생성할 인덱스 리스트를 입력합니다. default *
  • ignore_unavailable : 유효하지 않는 것은 무시하는지 유무를 정합니다. default false 
  • include_global_state : global 설정 값은 저장하지 않는지 유무를 정합니다. default true
  • partial : 부분 스냅샷 저장을 허용할지 선택합니다. default는 false로 false일 경우 하나의 샤드가 문제가 발생하면 스냅샷이 실패합니다.

 

Snapshot 정보 확인

Snapshot을 생성하였다면 정확하게 생성되었는지 확인합니다. Response 마지막 shards를 보면 total로 총 스냅샷을 찍은 샤드의 개수가 나오고 successful로 스냅샷 생성에 성공한 샤드의 개수가 나옵니다. 그리고 failed를 통해 스냅샷 생성에 실패한 샤드의 개수를 확인할 수 있습니다. 종종 네트워크의 문제로 인해 노드끼리의 통신 문제로 실패한 샤드가 발생할 수도 있습니다.

GET _snapshot/my_repository/my_snapshot_1

## 결과
{
    "snapshots": [
        {
            "snapshot": "my_snapshot_1",
            "uuid": "aMA2T3ZoRliO0my6557-uw",
            "version_id": 136297827,
            "version": "2.8.0",
            "indices": [
                ".opendistro-job-scheduler-lock",
                ".opendistro-ism-config",
                ".opensearch-observability",
                "my-log-2023-07-10"
            ],
            "data_streams": [],
            "include_global_state": false,
            "state": "SUCCESS",
            "start_time": "2023-07-11T12:26:32.958Z",
            "start_time_in_millis": 1689078392958,
            "end_time": "2023-07-11T12:26:35.160Z",
            "end_time_in_millis": 1689078395160,
            "duration_in_millis": 2202,
            "failures": [],
            "shards": {
                "total": 7,
                "failed": 0,
                "successful": 7
            }
        }
    ]
}

 

Snapshot으로 복구

Snapshot으로 인덱스를 복구할 때 중요한 것은 같은 이름의 인덱스가 Cluster이 이미 존재해서는 안된다는 것입니다. 만약 같은 이름의 인덱스가 존재할 경우 인덱스를 삭제하거나 인덱스를 덤프를 생성하고 인덱스를 삭제하도록 합니다. 

# 먼저 실습할 인덱스를 삭제합니다. 예시로 my-log-2023-07-10 인덱스를 삭제합니다.
DELETE my-log-2023-07-10/

# 위에서 삭제한 인덱스를 스냅샷을 통해 복구합니다. 예시로 my-log-2023-07-10 인덱스를 복구합니다.
POST _snapshot/my_repository/my_snapshot_1/_restore
{
  "indices": "my-log-*",
  "ignore_unavailable": true,
  "include_global_state": false,
  "include_aliases": true,
  "partial": false
}

예시로 "my-log-2023-07-10" 인덱스를 삭제하고 위에서 생성한 스냅샷을 이용하여 "my-log-"로 시작하는 인덱스를 복구하였습니다.

 

마지막으로 Snapshot 및 Repository 삭제

이제 더 이상 사용하지 않는 오래된 Snapshot 및 Repository을 삭제합니다.

# 스냅샷을 삭제합니다.
DELETE _snapshot/my_repository/my_snapshot_1

# 스냅샷 Repository를 삭제합니다.
DELETE _snapshot/my_repository

 

Snapshot에 대해 더 자세한 내용은 Opensearch Document를 확인하여 주세요

https://opensearch.org/docs/latest/tuning-your-cluster/availability-and-recovery/snapshots/index/

 

Snapshots

Snapshots

opensearch.org

 

Opensearch SM(Snapshot Management)이란 Snapshot을 생성하고 일정 기간 유지시켜 주며 일정 기간이 지날 경우 자동으로 Snapshot을 삭제하여 Snapshot Repository를 관리해 주는 역할을 수행합니다. Opensearch SM을 통하여 매일 스냅샷을 생성하여 안정적인 클러스터 운영이 가능해집니다.

먼저 시작하기 전에 스냅샷을 생성하기 위해 Snapshot Repository가 생성되어 있어야 합니다. Snapshot Repository 생성에 대해서는 이 전 글을 확인하여 주세요.

https://stdhsw.tistory.com/entry/Opensearch-Snapshot-AWS-S3%EB%B0%B1%EC%97%85-%EB%B0%8F-%EB%B3%B5%EA%B5%AC

 

SM Policy 생성하기

SM Policy 생성을 통하여 매일 자동적으로 스냅샷이 생성되고 생성된지 오래된 스냅샷을 자동적으로 삭제할 수 있습니다. 

POST _plugins/_sm/policies/my-policy
{
  "description": "Daily opensearch snapshot",
  "creation": {
    "schedule": {
      "cron": {
        "expression": "0 1 * * *",
        "timezone": "UTC"
      }
    },
    "time_limit": "1h"
  },
  "deletion": {
    "schedule": {
      "cron": {
        "expression": "0 1 * * *",
        "timezone": "UTC"
      }
    },
    "condition": {
      "max_age": "7d",
      "max_count": 14,
      "min_count": 7
    },
    "time_limit": "1h"
  },
  "snapshot_config": {
    "date_format": "yyyy-MM-dd-HH:mm",
    "timezone": "UTC",
    "indices": "*",
    "repository": "my_repository",
    "ignore_unavailable": "true",
    "include_global_state": "false",
    "partial": "false",
    "storage_type": "remote_snapshot"
  }
}

 

creation에서는 스냅샷을 생성하는 정책을 정의합니다.

  • schedule.cron.expression : 매번 반복적으로 스냅샷을 생성할 시간을 설정합니다.
  • schedule.cron.timezone : 반복적으로 작업될 타임존을 설정합니다.
  • time_limit : 스냅샷을 생성하는데 최대 시간을 설정합니다. 스냅샷을 생성할 때 지정한 시간이 넘어가면 실패로 처리됩니다.

deletion에서는 스냅샷을 삭제하는 정책을 정의합니다.

  • schedule.cron.expression : 매번 반복적으로 스냅샷을 삭제할 시간을 설정합니다.
  • schedule.cron.timezone : 반복적으로 작업될 타임존을 설정합니다.
  • time_limit : 스냅샷을 삭제하는데 최대 시간을 설정합니다. 스냅샷을 삭제할 때 지정한 시간이 넘어 가면 실패로 처리됩니다.
  • condition.max_age : 생성된 스냅샷이 보존되는 시간을 설정합니다.
  • condition.max_count : 최대로 가지고 있을 수 있는 스냅샷의 개수를 설정합니다.
  • condition.min_count : 최소로 유지할 스냅샷의 개수를 설정합니다.

snapshot_config에서는 스냅샷을 생성할 때 설정 값을 정의합니다.

  • date_format : 생성된 스냅샷의 이름 format을 설정합니다. ("폴리스명-yyyy-MM-dd-HH:mm-해쉬값" 형식으로 스냅샷이 생성됩니다.)
  • timezone : date_format에서 사용되는 타임존을 설정합니다.
  • indices : 스냅샷을 생성할 인덱스 리스트를 설정합니다.
  • repository : 스냅샷을 저장할 Repository를 설정합니다.
  • ignore_unavailable : 유효하지 않은 값을 무시하는지 설정합니다.
  • include_global_state : Cluster 상태 값을 포함할 것인지 설정합니다.
  • partial : 부분 스냅샷 저장을 허용할지 선택합니다. default는 false로 false일 경우 하나의 샤드가 문제가 발생하면 스냅샷이 실패합니다.
  • storage_type : searchable snapshot을 적용할 경우 remote_snapshot를 설정하고 아닐 경우 local로 설정합니다.

 

SM Policy 확인하기

SM Policy를 정상적으로 생성되었는지 확인합니다.

GET _plugins/_sm/policies/my-policy/_explain

 

 

SM Policy Start & Stop

자동적으로 스냅샷을 관리하는 기능을 잠시 중단하는 기능을 제공하고 있습니다.

# SM Policy 중지
POST _plugins/_sm/policies/my-policy/_stop

# SM Policy 시작
POST _plugins/_sm/policies/my-policy/_start

 

SM Policy 삭제

더 이상 사용되지 않는 SM Policy가 있다면 삭제합니다.

DELETE _plugins/_sm/policies/my-policy

 

 

스냅샷을 통한 데이터 복구는 이전 글을 확인하여 주세요.

https://stdhsw.tistory.com/entry/Opensearch-Snapshot-AWS-S3%EB%B0%B1%EC%97%85-%EB%B0%8F-%EB%B3%B5%EA%B5%AC

 

Index Template은 인덱스를 생성할 때 사용되는 설정 템플릿입니다. 새로운 인덱스가 생성될 때 매번 정의하는 번거러움을 인덱스 템플릿 설정을 통하여 해결할 수 있습니다. 인덱스 템플릿을 선언을 통해 자동적으로 새롭게 생성되는 인덱스에 설정을 적용하여 일관된 인덱스 구조와 매핑을 유지하고 반복적인 작업을 줄이는 데 사용됩니다.

 

Index Template 생성

실습으로 my-log라는 Index Template을 생성하여 데이터 필드가 잘 적용되는지 확인하도록 하겠습니다.

 

인덱스 생성 시 설정 파라미터에 대한 정보

https://opensearch.org/docs/1.2/opensearch/rest-api/index-apis/create-index/

 

Create index

Create index Introduced 1.0

opensearch.org

PUT _index_template/my-log
{
    "index_patterns": [
        "my-log-*"
    ],
    "template": {
        "settings": {
            "number_of_shards": 4,
            "number_of_replicas": 1,
            "refresh_interval": "5s",
            "opendistro.index_state_management.policy_id": "my-ism"
        },
        "mappings": {
            "properties": {
                "cluster": {
                    "type": "keyword"
                },
                "node": {
                    "type": "keyword"
                },
                "level": {
                    "type": "keyword"
                },
                "message": {
                    "type": "text"
                },
                "timestamp": {
                    "type": "long"
                }
            },
            "dynamic_templates": [
                {
                    "default_string": {
                        "match": "default*",
                        "match_mapping_type": "string",
                        "mapping": {
                            "type": "keyword"
                        }
                    }
                },
                {
                    "default_number": {
                        "match": "default*",
                        "match_mapping_type": "long",
                        "mapping": {
                            "type": "integer"
                        }
                    }
                }
            ]
        }
    }
}

index_patterns을 통해 "my-log-"로 시작하는 새롭게 생성된 인덱스에 해당 Template을 적용하도록 지정합니다.

 

number_of_shards 해당 인덱스의 샤드 개수를 설정합니다. Opensearch의 경우 Cluster가 여러 Data Node로 구성되는데 하나의 Data Node에 데이터가 집중되지 않도록 number_of_shards를 설정하여 여러 Data Node에 샤드를 골고루 배치할 수 있습니다.

 

number_of_replicas는 primary shard를 복제한 replica shard의 개수를 설정합니다. 위 설정에서는 1로 설정하여 각각의 primary shard가 복제한 샤드를 한 개씩 구성할 수 있도록 설정하여 HA 구성이 되도록 설정하였습니다.

 

refresh_interval은 간단하게 설명하면 검색할 수 있는 document를 refresh 하는 것을 의미합니다. Opensearch는 데이터가 들어왔다고 바로 검색이 가능한 것이 아니고 데이터가 들어오고 해당 인덱스가 refresh가 되었을 경우 검색이 가능하게 됩니다. refresh_interval을 1s로 설정하였다면 1초마다 refresh 되어 실시간처럼 검색이 가능하게 되지만 indexing 성능에 영향을 미칠 수 있습니다. 만약 본인의 환경이 실시간을 중요하게 여기지 않고 indexing 성능을 우선으로 한다면 refresh_interval값을 크게 가져가는 것을 추천합니다. 보다 자세한 정보를 원하신다면 "Lucene"에 대해 알아보는 것을 추천합니다.

 

opendistro.index_state_management.policy_id 설정은 ISM Policy 정책을 지정하는 것입니다. ISM은 Index state Management로 인덱스의 Lifecycle을 관리해 주는 역할을 수행합니다. 아래 링크에 정리해 두었으니 자세한 내용을 링크에서 확인해 주세요

https://stdhsw.tistory.com/entry/Opensearch-ISMIndex-State-Management

 

mappings.properties 설정은 데이터의 field의 타입을 지정합니다. string 타입에는 대표적으로 keyword, text 타입이 있는데 keyword는 Aggregation과 같이 통계를 내거나 반듯이 같은 값을 검색하는 데 사용되며 text의 경우 Analyzer를 통해 데이터를 분석하는 데 사용됩니다. 그 밖에 많은 field type이 존재하는데 자세한 내용은 Opensearch document를 확인해 주세요

https://opensearch.org/docs/latest/field-types/supported-field-types/index/

 

Supported field types

Supported field types

opensearch.org

mappings.dynamic_templates 설정은 properties로 지정하지 않은 field의 데이터가 들어올 경우 해당 데이터의 필드 타입을 지정합니다. dynamic_templates 은 어떠한 데이터 필드가 들어올지 명확하지 않거나 동일한 이름 형식의 필드가 너무 많을 경우 사용합니다. 위에서 설정된 default_string의 경우 필드의 이름이 default로 시작되며 데이터 형식이 string일 경우 필드의 타입을 keyword로 지정하는 설정이며 default_number의 경우 default로 시작하여 데이터 형식이 number일 경우 필드의 타입을 integer로 설정합니다.

 

Index Template 확인하기

방금 생성한 my-log Template을 확인합니다.

GET _index_template/my-log

 

테스트 해보기

지정된 Template이 잘 적용되는지 bulk를 이용하여 실제 데이터를 넣어 확인합니다. (참고로 bulk는 HTTP Body의 가장 마지막 라인에 enter를 한번 더 넣어줘야지 Error가 발생하지 않습니다.)

POST _bulk
{ "index": { "_index": "my-log-2023-07-10"} }
{ "cluster": "my-test-cluster", "node": "worker-1", "level": "debug", "message": "[debug] data send successful", "timestamp": 1688992028}
{ "index": { "_index": "my-log-2023-07-10"} }
{ "cluster": "my-test-cluster", "node": "worker-1", "level": "debug", "message": "[debug] data send successful", "timestamp": 1688992038}
{ "index": { "_index": "my-log-2023-07-10"} }
{ "cluster": "my-test-cluster", "node": "worker-1", "level": "debug", "message": "[debug] data send successful", "timestamp": 1688992048}
{ "index": { "_index": "my-log-2023-07-10"} }
{ "cluster": "my-test-cluster", "node": "worker-2", "level": "debug", "message": "[debug] data send successful", "timestamp": 1688992029}
{ "index": { "_index": "my-log-2023-07-10"} }
{ "cluster": "my-test-cluster", "node": "worker-2", "level": "debug", "message": "[debug] data send successful", "timestamp": 1688992030}
{ "index": { "_index": "my-log-2023-07-10"} }
{ "cluster": "my-test-cluster", "node": "worker-2", "level": "debug", "message": "[debug] data send successful", "timestamp": 1688992031}
{ "index": { "_index": "my-log-2023-07-10"} }
{ "cluster": "my-test-cluster", "node": "worker-2", "level": "debug", "message": "[debug] data send successful", "timestamp": 1688992033}
{ "index": { "_index": "my-log-2023-07-10"} }
{ "cluster": "my-test-cluster", "node": "worker-1", "level": "err", "message": "[err] failed data send", "timestamp": 1688992048}
{ "index": { "_index": "my-log-2023-07-10"} }
{ "cluster": "my-test-cluster", "node": "worker-1", "level": "warn", "message": "[warn] json format is not valid", "timestamp": 1688992041}
{ "index": { "_index": "my-log-2023-07-10"} }
{ "cluster": "my-test-cluster", "node": "worker-1", "level": "err", "message": "[err] failed data send", "timestamp": 1688992042}
{ "index": { "_index": "my-log-2023-07-10"} }
{ "cluster": "my-test-cluster", "node": "worker-2", "level": "debug", "message": "[debug] data send successful", "timestamp": 1688992031, "default-text": "APP Test"}
{ "index": { "_index": "my-log-2023-07-10"} }
{ "cluster": "my-test-cluster", "node": "worker-2", "level": "debug", "message": "[debug] data send successful", "timestamp": 1688992033, "default-text": "APP Test"}
{ "index": { "_index": "my-log-2023-07-10"} }
{ "cluster": "my-test-cluster", "node": "worker-1", "level": "err", "message": "[err] failed data send", "timestamp": 1688992048, "default-text": "APP Test"}
{ "index": { "_index": "my-log-2023-07-10"} }
{ "cluster": "my-test-cluster", "node": "worker-1", "level": "warn", "message": "[warn] json format is not valid", "timestamp": 1688992041, "default-number": 111111}
{ "index": { "_index": "my-log-2023-07-10"} }
{ "cluster": "my-test-cluster", "node": "worker-1", "level": "err", "message": "[err] failed data send", "timestamp": 1688992042, "default-number": 222222}

 

정상적으로 데이터가 들어갔는지 확인해 봅니다.

POST my-log-2023-07-10/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match_all": {}
        }
      ]
    }
  },
  "from": 0,
  "size": 20
}

 

마지막으로 저장된 데이터와 해당 데이터의 필드 타입이 어떻게 설정되었는지 확인합니다.

GET my-log-2023-07-10/

결과는 다음과 같습니다.

{
    "my-log-2023-07-10": {
        "aliases": {},
        "mappings": {
            "dynamic_templates": [
                {
                    "default_string": {
                        "match": "default*",
                        "match_mapping_type": "string",
                        "mapping": {
                            "type": "keyword"
                        }
                    }
                },
                {
                    "default_number": {
                        "match": "default*",
                        "match_mapping_type": "long",
                        "mapping": {
                            "type": "integer"
                        }
                    }
                }
            ],
            "properties": {
                "cluster": {
                    "type": "keyword"
                },
                "default-number": {
                    "type": "integer"
                },
                "default-text": {
                    "type": "keyword"
                },
                "level": {
                    "type": "keyword"
                },
                "message": {
                    "type": "text"
                },
                "node": {
                    "type": "keyword"
                },
                "timestamp": {
                    "type": "long"
                }
            }
        },
        "settings": {
            "index": {
                "opendistro": {
                    "index_state_management": {
                        "policy_id": "my-ism"
                    }
                },
                "refresh_interval": "5s",
                "number_of_shards": "4",
                "provided_name": "my-log-2023-07-10",
                "creation_date": "1688992258343",
                "number_of_replicas": "1",
                "uuid": "n24SvkagRl2OI0NcHb5SXw",
                "version": {
                    "created": "136297827"
                }
            }
        }
    }
}

template으로 지정한 필드의 경우 정상적으로 필드의 타입이 적용된 것을 확인할 수 있습니다. 그리고 default-number와 default-text의 경우 직접적으로 지정하지는 않았지만 dynamic_templates 설정을 통하여 자동적으로 문자열 타입은 keyword로 지정되고 숫자 타입은 integer로 설정된 것을 확인할 수 있습니다.

 

Opensearch에서 ISM은 Index State Management의 약자로 인덱스의 상태를 관리하는 Management 플러그인입니다. 인덱스의 상태란 인덱스의 생명주기를 관리하고 관리 작업을 자동화하는 기능을 제공합니다. 이를 통해 데이터 유지 정책을 정의하여 자주 사용하는 데이터를 Hot, 자주 사용되지는 않지만 종종 사용되는 데이터를 Cold, 그리고 사용되지 않는 데이터를 Delete를 하여 Opensearch의 자원을 보다 효율적으로 사용할 수 있게 할 수 있습니다.

 

ISM Policy 생성

# ISM 생성
PUT _plugins/_ism/policies/my-ism
{
    "policy": {
        "description": "my-index ism policy",
        "default_state": "hot",
        "states": [
          {
            "name" : "hot",
            "actions" : [],
            "transitions" : [
              {
                "state_name" : "cold",
                "conditions" : {
                  "min_index_age" : "3d"
                }
              }
              ]
          },
          {
            "name" : "cold",
            "actions" : [],
            "transitions" : [
              {
                "state_name" : "delete",
                "conditions" : {
                  "min_index_age" : "7d"
                }
              }
              ]
          },
          {
            "name" : "delete",
            "actions" : [
              {
                "delete" : {}
              }
            ],
            "transitions" : []
          }
        ]
    }
}

# 결과
{
    "_id": "my-ism",
    "_version": 1,
    "_primary_term": 409,
    "_seq_no": 936,
    "policy": {
        "policy": {
            "policy_id": "my-ism",
            "description": "my-index ism policy",
            "last_updated_time": 1688990475484,
            "schema_version": 1,
            "error_notification": null,
            "default_state": "hot",
            "states": [
                {
                    "name": "hot",
                    "actions": [],
                    "transitions": [
                        {
                            "state_name": "cold",
                            "conditions": {
                                "min_index_age": "3d"
                            }
                        }
                    ]
                },
                {
                    "name": "cold",
                    "actions": [],
                    "transitions": [
                        {
                            "state_name": "delete",
                            "conditions": {
                                "min_index_age": "7d"
                            }
                        }
                    ]
                },
                {
                    "name": "delete",
                    "actions": [
                        {
                            "retry": {
                                "count": 3,
                                "backoff": "exponential",
                                "delay": "1m"
                            },
                            "delete": {}
                        }
                    ],
                    "transitions": []
                }
            ],
            "ism_template": null
        }
    }
}

인덱스 policy 동작 순서

  1. default_state의 값을 hot으로 정의하여 인덱스가 생성될 때 hot 상태로 생성됩니다. 
  2. transitions.min_index_age를 통해 3일 동안 hot 데이터 형식으로 유지됩니다.
  3. 3일이 지나면 transitions.state_name으로 명시한 cold 상태로 전달됩니다.
  4. cold 상태에서 명시한 7일 동안 데이터를 유지하고 delete 상태로 전달됩니다.
  5. delete 상태에서는 action으로 정의된 delete를 통해 인덱스를  삭제합니다.

간단히 정리하면 명시한 ISM은 3일 동안 Hot 데이터로 유지하고 3일 뒤에는 Delete 되는 Policy입니다. 

 

ISM Policy 확인

ISM을 잘 생성하였다면 GET을 통해 확인합니다.

GET _plugins/_ism/policies

 

ISM 삭제

더 이상 사용되지 않는 ISM을 삭제합니다.

DELETE _plugins/_ism/policies/my-ism

 

Hot에서 바로 Delete로

개발자의 환경에 따라 Cold 데이터를 사용하지 않는 경우가 많습니다. 이런 경우 굳이 Cold 상태까지 선언하는 것보다는 Hot 다음에 바로 Delete로 보내면 됩니다. 아래 예시는 3일 동안 Hot 데이터로 유지하고 3일 뒤에 인덱스가 삭제되는 예시입니다.

PUT _plugins/_ism/policies/my-ism
{
    "policy": {
        "description": "my-index ism policy",
        "default_state": "hot",
        "states": [
          {
            "name" : "hot",
            "actions" : [],
            "transitions" : [
              {
                "state_name" : "delete",
                "conditions" : {
                  "min_index_age" : "3d"
                }
              }
              ]
          },
          {
            "name" : "delete",
            "actions" : [
              {
                "delete" : {}
              }
            ],
            "transitions" : []
          }
        ]
    }
}

 

 

자세한 내용은 아래 링크(Opensearch Document)를 확인해 주세요.

 

https://opensearch.org/docs/latest/im-plugin/ism/index/

 

Index State Management

Index State Management

opensearch.org

 

+ Recent posts