[OpenSearch] OpenSearch 클러스터 운영

Hyunjun Kim·2025년 7월 13일
0

Data_Engineering

목록 보기
102/153

3 OpenSearch 클러스터 운영

챕터 1~2에서는 인덱스와 도큐먼트 CRUD, 인덱싱, 도큐먼트 검색 등 인덱스와 도큐먼트 수준에서 OpenSearch에 대해 살펴봤다. 챕터 3에서는 아키텍처 수준에서 OpenSearch의 분산 시스템 구성을 알아보고, OpenSearch가 어떻게 대량의 인덱스와 도큐먼트를 안정적으로 처리하고 있는지 알아볼 것이다.

3.1 OpenSearch의 분산 아키텍처 구성 요소

OpenSearch가 데이터를 분산 처리하기 위해 사용하는 분산 아키텍처의 핵심 구성 요소는 다음과 같다.

  • 클러스터(Cluster)
  • 노드(Node)
  • 샤드(Shard)
  • 세그먼트(Segment)

3.1.1 클러스터(Cluster)와 노드(Node)

출처: https://codingexplained.com/coding/elasticsearch/introduction-elasticsearch-architecture

출처: https://codingexplained.com/coding/elasticsearch/introduction-elasticsearch-architecture

클러스터는 노드의 집합이며, 노드는 클러스터를 구성하는 하나의 OpenSearch 인스턴스로써 도큐먼트가 저장되는 곳이다. 쉽게 말해 노드는 OpenSearch가 설치된 물리 혹은 논리(가상) 서버다. 일반적으로 물리적인 서버 하나에 노드 하나를 설치하는 방식을 권장하지만, 단일 서버에 복수의 노드를 설치해도 된다. 클러스터와 노드는 모두 고유한 이름으로 식별된다.

클러스터는 도큐먼트를 여러 노드에 분산시켜 저장할 수 있고, 모든 노드는 클러스터의 인덱싱 및 도큐먼트 검색 기능에 참여한다. OpenSearch에는 다양한 노드 타입이 있으며, 타입에 따라 수행하는 역할이 다르다. 하나의 노드에 복수의 타입을 할당할 수 있다. 노드 타입에 대해서는 3.2에서 자세히 설명할 예정이다.

노드와 외부 클라이언트 간의 통신은 Application 계층에서 HTTP 프로토콜로 동작하는 반면, 클러스터 내부에 있는 노드 간의 통신은 Transport 계층에서 동작한다. 외부 클라이언트가 REST API를 사용하여 클러스터에 요청을 전송하면 특정 타입의 노드는 해당 요청을 수신하고, 필요한 경우 Transport 계층을 사용하여 다른 노드로 추가 작업을 요청한다. 기본적으로 모든 노드는 외부 클라이언트의 HTTP 요청을 처리할 수 있지만, 클라이언트의 HTTP 요청를 처리하는 역할은 특정 타입의 노드만 수행하도록 제한된다.

3.1.2 샤드(Shard)

출처: https://nidhig631.medium.com/primary-shards-replica-shards-in-elasticsearch-269343324f86

출처: https://nidhig631.medium.com/primary-shards-replica-shards-in-elasticsearch-269343324f86

OpenSearch가 확장성이 뛰어난 이유 중 하나는 샤딩(Sharding) 때문이다. 샤딩은 데이터를 여러 개의 조각으로 나눠 분산 저장하여 관리하는 기술이며, 샤드란 샤딩을 통해 나눠진 데이터 블록이다. OpenSearch에서는 인덱스를 샤딩하여 샤드로 관리하고, 노드는 복수의 샤드로 구성된다. 앞서 도큐먼트를 인덱스에 저장한다고 했지만, 사실 인덱스는 논리적 단위이며 실제 도큐먼트의 인덱싱과 검색은 샤드에서 이뤄진다. 즉 분산된 샤드를 하나의 논리적 단위로 묶은 것이 인덱스다.

OpenSearch에서 샤드는 왜 필요할까? 다음과 같은 상황을 가정해보자. 1TB 크기의 인덱스에 복수의 도큐먼트가 저장되어 있고, 클러스터에는 총 2개의 노드가 있으며, 각 노드의 여유 저장 공간은 512GB다. 즉 인덱스를 하나의 노드에 저장할 수 없는 상황이다. 인덱스의 크기가 단일 노드의 하드웨어 리소스 제한을 초과하는 경우 인덱스를 샤딩하여 문제를 해결할 수 있다. 앞선 예의 경우, 다음 그림처럼 1TB 인덱스를 256GB 도큐먼트 묶음 4개로 나눈 후에 노드 2개에 분산시켜 저장하면 된다.

OpenSearch는 기본적으로 샤드 복제를 지원한다. 샤드를 복제하면, 원본은 프라이머리 샤드, 복제본은 레플리카 샤드(또는 레플리카)라고 불린다. 프라이머리 샤드와 레플리카 샤드에 대해서는 3.3에서 자세히 설명할 예정이다.

샤딩이 필요한 이유를 정리하면 다음과 같다.

  • 여러 물리 장비를 활용하여 클러스터의 하드웨어 리소스를 수평 확장할 수 있다.
  • 동일한 인덱스에 대한 요청을 여러 노드에 걸쳐 병렬적으로 처리할 수 있기 때문에 성능이 향상된다.
  • 레플리카 샤드 덕분에 고가용성(High availability)과 빠른 처리 속도를 확보할 수 있다. (3.3에서 자세히 설명할 예정)

인덱스의 샤드 개수는 인덱스를 생성할 때 선택적으로 지정할 수 있으며, 기본적으로는 인덱스 하나에 5개의 샤드가 생성된다. 샤드 개수는 클러스터의 하드웨어 스펙과 인덱스의 데이터량 등을 고려하여 적절한 값으로 설정하면 된다.

ℹ️ 과거 버전에서는 number of shards 의 default 가 5였다. 하지만 overshard 문제 때문에, 최근에 1로 변경되었다. ℹ️ overshard 란 인덱스에 저장된 데이터 대비 shard 가 너무 잘게 쪼개져 있어서 불필요하게 성능의 손해가 발생하는 것을 말한다.

인덱스가 생성된 후에는 샤드 개수를 변경할 수 없다. 그러나 원하는 샤드 개수로 새로운 인덱스를 만들고, 기존 인덱스의 데이터를 새로운 인덱스로 이동시켜서 목적을 달성할 수는 있다.

인덱스의 샤드 개수를 변경할 수 없는 이유는 OpenSearch의 라우팅 방식 때문이다. 도큐먼트를 저장할 샤드를 결정하는 것을 라우팅이라고 한다. 도큐먼트는 기본적으로 모든 노드에 걸쳐 고르게 분산되어 저장되기 때문에 하나의 샤드에 많은 데이터가 몰리지 않는다. 라우팅은 기본적으로 자동 처리되며, OpenSearch에서는 다음 공식을 사용하여 도큐먼트가 저장될 샤드를 결정한다.

shard = hash(_routing) % 프라이머리 샤드 개수

기본적으로 _routing 값은 저장하려는 도큐먼트의 ID와 동일하다. _routing 값은 해시 함수를 거쳐 숫자로 변환되고, 이것을 프라이머리 샤드 개수로 나눈 나머지 값이 바로 도큐먼트가 저장될 샤드 넘버다.

_routing 값을 직접 지정할 수도 있는데, 이를 커스텀 라우팅이라고 한다. 커스텀 라우팅을 사용하면 같은 _routing 값을 공유하는 도큐먼트들을 동일한 샤드에 저장할 수 있다. 그러나 커스텀 라우팅의 경우 몇 가지 주의해야 할 부분이 있다. 그 중 하나는 도큐먼트가 프라이머리 샤드에 고르게 분산되지 않을 수 있다는 것이다. 예를 들어, movie 인덱스에서 genre를 _routing 값으로 사용하는 경우, 대부분의 영화가 같은 genre라면 도큐먼트는 특정 샤드에 몰릴 수 밖에 없다.

라우팅 공식에 프라이머리 샤드 개수가 포함되어 있으므로, 인덱스의 샤드 개수를 변경하면 도큐먼트에 대한 라우팅 공식 실행 결과도 바뀌게 된다. 라우팅 공식은 도큐먼트를 ID로 검색할 때도 사용되는데, 샤드 개수를 변경하는 경우 도큐먼트가 저장될 때와 검색될 때의 라우팅 공식이 달라지게 되고, 그 결과 도큐먼트를 찾지 못할 수도 있다. 이것이 인덱스의 샤드 개수를 변경할 수 없는 이유다. OpenSearch의 기본 라우팅 공식을 사용해서 도큐먼트를 인덱싱한 후에 동일한 인덱스에 커스텀 라우팅을 도입하는 경우에도 동일한 문제가 발생할 수 있다.

3.1.4 세그먼트(Segment)

출처: https://fdv.github.io/running-elasticsearch-fun-profit/003-about-lucene/003-about-lucene.html

출처: https://fdv.github.io/running-elasticsearch-fun-profit/003-about-lucene/003-about-lucene.html

위의 그림처럼 OpenSearch 샤드는 하나의 Lucene 인스턴스다. Lucene 인스턴스는 여러 개의 세그먼트를 포함하고 있다. 세그먼트는 OpenSearch에서 인덱스가 물리적으로 저장되는 가장 작은 단위로, 세그먼트 내부에는 인덱싱된 데이터가 역색인 구조로 저장되어 있다. 기본적으로 인덱싱된 도큐먼트는 하나의 세그먼트로 저장된다. Lucene 인덱스는 검색 요청이 들어오면 모든 세그먼트에 걸쳐 순차적으로 검색을 수행하고, 이를 통합해서 하나의 결과로 응답한다.

세그먼트는 한 번 디스크에 저장되면 수정이 불가능한 불변 데이터(Immutable)다. 도큐먼트를 수정할 경우 도큐먼트가 포함된 세그먼트를 변경하지 않고, 수정 사항이 반영된 새로운 세그먼트를 생성한 다음 기존 세그먼트를 삭제하는 방식을 활용한다. 도큐먼트를 삭제하는 경우에도 세그먼트 내의 도큐먼트를 실제로 삭제하지 않고, 삭제할 도큐먼트라고 표시만 해둔 다음 검색할 때는 해당 도큐먼트를 읽지 않도록 처리한다.

세그먼트에 대한 검색은 병렬적으로 수행될 수 없기 때문에 세그먼트 수가 많을수록 검색 속도가 느려진다. 그래서 Lucene은 검색 성능을 높이기 위해 정기적으로 백그라운드에서 세그먼트 파일을 물리적으로 하나의 큰 세그먼트 파일로 병합하고, 삭제 표시한 도큐먼트를 실제로 세그먼트에서 삭제한다. 이를 세그먼트 병합이라고 한다. 세그먼트 병합은 OpenSearch API로 트리거할 수도 있다. 세그먼트 병합은 물리적인 저장 공간에서 이뤄지기 때문에 많은 CPU와 I/O 리소스를 사용한다. 대량의 데이터 인덱싱을 수행할 때는 세그먼트 병합을 비활성화하는 것이 좋고, 적절한 세그먼트 병합 정책과 스케쥴러를 활용하여 서버 성능을 최적화할 수도 있다.

Lucene이 세그먼트를 생성하고 병합하는 방법에 대한 구체적인 설명은 공식 문서를 참고하자.(https://lucene.apache.org/core/9_4_2/index.html)

3.2 OpenSearch의 노드 타입과 역할

앞서 노드는 타입에 따라 수행하는 역할이 다르다고 했다. OpenSearch에서 제공하는 노드 타입에 대해 알아보자.

기본적으로 모든 노드는 어떤 타입도 될 수 있으며, 다양한 역할을 수행할 수 있다. 노드에 OpenSearch를 설치하고 설정 파일을 수정하지 않으면, 해당 노드는 기본적으로 모든 역할을 수행하게 된다. OpenSearch 설정 파일에서 노드 타입을 지정해줄 경우 하나의 역할만 수행하는 노드가 되는데, 이 노드를 전용 노드라고 부른다.

3.2.1 클러스터 매니저(Cluster manager) 노드

  • 클러스터는 반드시 하나의 매니저 노드를 가져야 하며, 매니저 노드가 없으면 클러스터가 멈춘다.
  • 매니저 노드는 클러스터의 상태를 모니터링하고, 노드에 샤드를 할당하는 등의 역할을 담당한다. 클러스터 상태 정보는 클러스터 설정, 인덱스 설정(매핑 정보, 물리적 위치 등), 노드 상태 등을 포함한다.
    • 예를 들어, 매니저 노드는 ping 요청을 실행하여 노드 상태를 확인할 수 있다.

3.2.2 클러스터 매니저 후보(Cluster manager eligible) 노드

  • 매니저 후보 노드는 선출 과정을 통해 매니저 노드가 될 수 있으며, 매니저 후보 노드만 선출 과정에 참여할 수 있다.
  • 매니저 노드 선출 과정은 다음과 같다.
    1. 매니저 후보 노드끼리 서로에게 투표한다.
    2. 과반수 득표를 얻은 노드가 매니저 노드가 된다.
    3. 선출된 매니저 노드가 클러스터에서 이탈할 경우 1~2를 반복하여 매니저 노드를 다시 선출한다.

3.2.3 데이터(Data) 노드

  • 데이터 노드는 모든 데이터 관련 작업(인덱싱, 검색, 집계)을 담당한다.
  • 실질적인 데이터 처리를 담당하기 때문에 일반적으로 가장 부하를 많이 받는 노드 타입이다.
    • 다른 타입의 노드보다 더 많은 컴퓨터 리소스(CPU, 메모리, 디스크 등)를 사용한다.
    • 모니터링을 통해 데이터 노드의 부하 상태를 체크하는 것이 중요하다.
    • 특정 노드에 부하가 몰렸다면, 해당 노드의 샤드를 다른 데이터 노드로 재배치하여 부하를 분산시킬 수 있다.
  • 클러스터를 구성할 때 매니저 노드와 데이터 노드를 전용 노드로 구성하는 것이 좋다.
    • 단일 노드가 데이터 노드와 매니저 노드의 역할을 같이 수행하는 경우, 데이터 노드의 부하가 매니저 노드의 성능에 영향을 미칠 수 있다.
    • 매니저 노드의 성능 저하는 클러스터 전체의 안정성을 해칠 수 있으므로, 가능하면 매니저 노드와 데이터 노드를 전용 노드로 구성하는 것이 좋다.

3.2.4 인제스트(Ingest) 노드

출처: https://hevodata.com/learn/elasticsearch-ingest-pipeline/

출처: https://hevodata.com/learn/elasticsearch-ingest-pipeline/

  • 인제스트 노드는 인제스트 파이프라인을 실행하는 역할을 담당한다.
    • 인제스트 파이프라인은 도큐먼트를 인덱싱하기 전에 도큐먼트를 전처리하는 역할을 담당한다.
    • 인제스트 파이프라인은 다양한 프로세서로 구성된다. 프로세서란 파이프라인의 실행 단위로, 프로세서 종류와 순서에 따라 파이프라인의 출력이 달라지게 된다.
  • 많은 데이터를 수집하고 복잡한 데이터 전처리 파이프라인을 실행할 계획이라면, 인제스트 전용 노드를 사용하는 것이 좋다.

3.2.5 코디네이터(Coordinator) 노드

출처: https://levelup.gitconnected.com/elastic-search-simplified-part-2-342a55a1a7c7

출처: https://levelup.gitconnected.com/elastic-search-simplified-part-2-342a55a1a7c7

  • 코디네이터 노드는 외부 클라이언트의 HTTP 요청을 처리하는 역할을 처리한다.
    • OpenSearch에서 로드밸런서와 비슷한 역할을 수행하며, 요청을 데이터 노드에 위임하고, 결과를 수집하여 하나의 최종 결과로 집계하여 클라이언트에 응답한다.
    • 코디네이터 노드가 데이터 쿼리를 요청 받은 경우 다음과 같은 순서로 해당 요청을 처리한다.
      1. 외부 클라이언트로부터 쿼리를 요청 받는다.
      2. 클러스터의 모든 노드에게 동일한 쿼리를 요청한다. (클라이언트의 요청에 _routing 값이 포함된 경우 _routing을 통해 어떤 노드의 어떤 샤드가 해당 데이터를 갖고 있는지 알 수 있으므로, 해당 노드에게만 쿼리를 요청)
      3. 2에서 요청한 노드로부터 결과 데이터를 받아 집계(aggregation)한다.
      4. 집계 결과를 클라이언트로 보낸다.
  • 검색이나 집계 요청이 많은 경우 병목 현상을 방지하기 위해 두 개의 코디네이터 전용 노드를 사용하는 것이 좋다.
  • 데이터를 집계할 때 CPU와 메모리를 많이 사용하기 때문에 가능한 코어가 많은 CPU를 사용하는 것이 좋다.

3.3 OpenSearch의 레플리카 샤드

3.1.2에서 설명한 레플리카 샤드에 대해 자세히 알아보자.

3.3.1 샤드를 복제하는 이유

출처: https://codingexplained.com/coding/elasticsearch/understanding-replication-in-elasticsearch

출처: https://codingexplained.com/coding/elasticsearch/understanding-replication-in-elasticsearch

레플리카 샤드의 주된 목적은 노드나 샤드에 장애가 발생했을 경우에 백업 역할을 수행해서 고가용성을 제공하는 것이다. 고가용성을 실현하기 위해 레플리카 샤드는 프라이머리 샤드와 같은 노드에 할당되지 않는다. 즉 노드 하나가 클러스터를 이탈하더라도 다른 노드에 프라이머리 샤드의 레플리카가 최소한 1개 이상 존재하기 때문에 요청에 정상적으로 응답할 수 있다.

또한 부수적인 목적으로, 레플리카 샤드는 OpenSearch의 검색 성능을 높여준다. 프라이머리 샤드가 위치한 노드에 부하가 심해 응답이 느린 경우, 해당 프라이머리 샤드의 레플리카 샤드가 위치한 다른 노드에 대신 요청해 좀 더 빠른 검색을 수행할 수 있다. 부하 상태 노드에 추가적인 부하를 주는 것을 막을 수 있으므로, 클러스터의 전체적인 처리 성능 안정성도 높일 수 있다. 클러스터에 검색 요청이 많다면 인덱스 당 두 개 이상의 레플리카 샤드를 할당해서 검색 성능을 높이는 것이 좋다.

레플리카 샤드 개수는 인덱스를 생성할 때 정의되며, 기본적으로 프라이머리 샤드 한 개 당 레플리카 샤드 한 개가 생성된다. 즉 기본 설정으로 인덱스를 생성하는 경우, 프라이머리 샤드 5개와 레플리카 샤드 5개를 합쳐서 총 10개의 샤드가 생성된다.

3.3.2 샤드 동기화

출처: https://codingexplained.com/coding/elasticsearch/understanding-replication-in-elasticsearch

출처: https://codingexplained.com/coding/elasticsearch/understanding-replication-in-elasticsearch

프라이머리 샤드는 여러 개의 레플리카 샤드로 복제될 수 있는데, 모든 레플리카 샤드는 프라이머리 샤드와 동기화된 상태를 유지해야 한다. 레플리카 샤드 그룹이 프라이머리 샤드와 동일한 상태를 유지하지 못할 경우, 쿼리 결과의 일관성이 떨어지게 된다. 예를 들어 도큐먼트가 레플리카 샤드 A에서만 삭제되었다면 A에서 쿼리가 수행될 경우에는 도큐먼트를 읽어올 수 없지만, 다른 레플리카 샤드에서 쿼리가 수행될 경우에는 도큐먼트를 읽어올 수 없게 된다.

OpenSearch는 모든 샤드를 동기화하기 위해 프라이머리 샤드를 모든 인덱싱 작업의 진입점으로 사용한다. 즉 도큐먼트 추가, 수정, 삭제 등 인덱스에 영향을 미치는 모든 요청은 가장 먼저 프라이머리 샤드로 전달된다. 프라이머리 샤드는 다음과 같은 과정을 거쳐 레플리카 샤드와 동기화된 상태를 유지한다.

  1. 요청된 작업을 검증하고, 유효하지 않은 경우 거부한다. (예를 들어 number 타입으로 매핑된 필드에 object 타입이 요청된 경우)
  2. 로컬에서 요청된 작업을 수행한다.
  3. 모든 in-sync 레플리카 샤드에 동일한 작업을 요청하며, 여러 개의 in-sync 레플리카 샤드가 있는 경우 모든 작업은 병렬적으로 수행된다.
    • 클러스터 매니저 노드는 프라이머리 샤드로부터 작업을 요청받아야 하는 레플리카 샤드 목록을 관리한다. 이 목록에 있는 샤드를 in-sync 레플리카 샤드라고 하며, 프라이머리 샤드가 사용자에게 승인한 모든 인덱스 작업은 in-sync 레플리카 샤드에서 성공적으로 처리됐다는 것이 보장된다. 즉 프라이머리 샤드와 모든 in-sync 레플리카 샤드는 동일한 상태라는 것이 보장된다.
    • 레플리카 샤드가 오프라인 상태인 경우 in-sync 레플리카 샤드 목록에서 제외되며, 프라이머리 샤드는 해당 레플리카 샤드에 작업을 요청할 필요가 없다.
  4. 모든 in-sync 레플리카 샤드가 성공적으로 작업을 수행하고, 프라이머리 샤드에 응답하면, 프라이머리 샤드는 클라이언트에게 요청된 작업을 승인한다.

1~4를 처리하던 중에 프라이머리 샤드 또는 레플리카 샤드에 장애가 발생할 수도 있다. 프라이머리 샤드에 장애가 발생한 경우 해당 샤드를 호스팅하는 노드는 클러스터 매니저에게 메시지를 전송한다. 클러스터 매니저가 레플리카 샤드 중 하나를 새로운 프라이머리 샤드로 승격시키면, 인덱싱 작업은 새로운 프라이머리 샤드로 전송된다. 또한 클러스터 매니저는 노드 상태를 모니터링하여 프라이머리 샤드를 사전에 강등시킬 수도 있다.

프라이머리 샤드에서 작업이 성공했음에도 불구하고, 레플리카 샤드에서 작업이 실패할 수도 있다. 이 경우 프라이머리 샤드는 문제가 있는 샤드를 in-sync 레플리카 셋에서 제거해달라고 클러스터 매니저에게 요청한다. 클러스터 매니저가 샤드 제거를 승인한 경우에만 프라이머리 샤드는 최종적으로 클라이언트의 요청을 승인한다. 이후 클러스터 매니저는 시스템을 정상적인 상태로 복원하기 위해 다른 노드에 새로운 레플리카 샤드를 생성할 것을 요청한다.

3.4 OpenSearch 클러스터 구성 실습

3.4.1 OpenSearch 클러스터 시각화 클라이언트

Elasticvue는 다음과 같은 기능을 제공하는 OpenSearch 클라이언트다.

  • 클러스터, 노드, 샤드 상태 확인
  • 인덱스 관리
  • REST API 인터페이스
  • 스냅샵 관리

크롬 확장 프로그램으로 쉽게 설치할 수 있고, 복잡한 환경 설정이 필요 없다는 장점이 있다. 다음 링크를 클릭해서 크롬 확장 프로그램에 추가하고, 실행해보자.

https://chrome.google.com/webstore/detail/elasticvue/hkedbapjpblbodpgbajblpnlpenaebaa

Elasticvue를 실행하면 다음과 같은 Setup 화면이 뜬다. Uri에 OpenSearch REST API 주소를 입력하고 CONNECT를 누르자.

Screen Shot 2022-12-23 at 3.39.31 PM.png

다음과 같은 화면이 뜨면 연결에 성공한 것이다.

Screen Shot 2022-12-23 at 3.42.12 PM.png

클러스터, 노드, 샤드 모니터링이 필요한 경우 Elasticvue를 활용해보자.

3.4.2 멀티 노드 클러스터 구성

챕터 1.1에서는 EC2 인스턴스 한 대로 싱글 노드 클러스터를 구성했었다. 챕터 3.4에서는 EC2 인스턴스 여러 대에 OpenSearch 인스턴스를 한 개씩 설치하여 멀티 노드 클러스터를 구성해 볼 것이다. EC2 인스턴스 한 대에 여러 개의 OpenSearch 인스턴스를 설치할 수도 있지만, OpenSearch에서 권장하는 방법은 아니다.

지금부터 다음과 같은 4노드 클러스터를 구성할 것이다.

출처: https://opensearch.org/docs/latest/opensearch/cluster/

출처: https://opensearch.org/docs/latest/opensearch/cluster/

  • 클러스터 매니저 노드 1개 (전용 노드)
  • 코디네이터 노드 1개 (전용 노드)
  • 데이터 & 인제스트 노드 2개

4노드 클러스터를 구성하려면 EC2 인스턴스 4대가 필요하다. EC2 인스턴스 4개를 띄우고, 모든 인스턴스에 한 번씩 접속해서 OpenSearch를 설치하는 것은 번거로운 일이다. 반면 AMI를 활용하면 클릭 몇 번으로 OpenSearch가 미리 설치된 EC2 인스턴스를 시작할 수 있다. AMI는 EC2 인스턴스의 소프트웨어 구성이 정의된 템플릿이며, 기본적으로 EC2 인스턴스에 연결된 모든 EBS 볼륨의 상태도 같이 복제된다. 즉 AMI를 사용하면 특정 EC2 인스턴스와 똑같은 환경의 인스턴스를 시작할 수 있다.

  • AMI 를 사용하기위한 베이스는
  • EFK로 서버 로그 수집하기 강의자료의 4.1.1~4.1.3 과 4.1.9 를 따라한다.
    • opensearch를 실행은 시키지 않는다.
⚠️ AMI 생성 전 EC2 인스턴스에서 `$ sysctl vm.max_map_count`로 `vm.max_map_count`가 262144 이상인지 확인하자. 만약 262144보다 작다면 의 4.1.1-4.1.3를 참고해서 `vm.max_map_count`를 262144 이상으로 설정해줘야 한다. 챕터 1.1에서는 싱글 노드 클러스터로 실습했기 때문에 선택 사항이었지만, 멀티 노드 클러스터의 경우 vm.max_map_count를 262144 이상으로 설정해주지 않으면 다음과 같은 에러 메시지를 띄우면서 bootstrap check가 실패하게 된다. (멀티 노드 클러스터 기능을 사용할 경우 프로덕션 레벨의 OpenSearch 인스턴스로 간주되기 때문)

ERROR: [2] bootstrap checks failed
[1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

⚠️ AMI 생성 전 EC2 인스턴스에서 `$ rm -rf $OPENSEARCH_HOME/data/nodes/*`를 실행해서 OpenSearch 데이터를 초기화해야 한다. 해당 경로에는 같은 EC2 인스턴스 안에서 클러스터링된 모든 노드의 정보가 저장되어 있으며, OpenSearch는 데이터 경로를 지정하지 않는 이상 기본적으로 `$OPENSEARCH_HOME/data/nodes/0`에 노드 정보를 저장한다. 데이터 경로에는 노드 ID 정보가 저장되어 있기 때문에 서로 다른 노드가 같은 데이터 경로를 바라볼 경우 ID 충돌이 발생한다.

EC2 콘솔에서 OpenSearch가 설치된 EC2 인스턴스를 선택하고 (한국어 콘솔의 경우) 우측 상단의 작업이미지 및 템플릿이미지 생성 버튼을 눌러 AMI를 생성하자. AMI는 생성 요청 후 사용 가능 상태로 바뀌기까지 약 5~10분 가량의 시간이 걸린다.

Untitled

Untitled

AMI를 사용하여 인스턴스 4대를 시작하자.

Untitled

헷갈리지 않도록 인스턴스 이름에 노드 타입을 추가해주자.

Untitled

모든 인스턴스 대상으로 OpenSearch config를 수정해줘야 한다. config 파일 경로는 다음과 같다.

$OPENSEARCH_HOME/config/opensearch.yml

config에는 다음 속성 값을 수정해주면 된다.

  • cluster.name: 클러스터 이름
    • 클러스터 이름을 지정하지 않을 경우 opensearch라는 이름으로 클러스터가 생성된다. 프로덕션 환경에서는 클러스터 이름을 지정해주는 것이 좋다. 프로덕션과 동일한 네트워크에서 노드를 개발하다 실수로 프로덕션 클러스터에 해당 노드를 가입시킬 수 있기 때문이다. 네트워크를 분리해서 개발하는 것이 가장 좋겠지만, 혹시 모를 상황에 대비하는 것이 좋다.
  • node.name: 노드 이름
  • node.roles: 노드 타입
  • network.host: 노드의 IP 주소
  • discovery.seed_hosts: 클러스터에 속한 클러스터 매니저 또는 후보 노드의 IP 주소 목록
    • 노드가 처음 실행될 때 discovery.seed_hosts에 설정된 IP 주소로 클러스터 매니저 또는 후보 노드를 찾고, 해당 노드를 클러스터로 바인딩하는 과정을 디스커버리라고 한다.
    • IP 주소로 노드를 찾았을 때 클러스터 이름이 일치하지 않거나 찾은 노드가 클러스터 매니저 또는 후보 노드가 아닐 경우 다른 IP 주소로 디스커버리 과정을 반복한다.
  • cluster.initial_cluster_manager_nodes : 클러스터를 맨 처음 실행할 때, 최초로 뜨는 node.name 최초로 기동하는 cluster_manager 서버가 자기 자신의 node.name 을 입력하면 된다.

de-opensearch-cluster-practice_manager EC2 인스턴스에 접속해서 다음과 같이 OpenSearch config를 수정하고 $ sudo systemctl restart opensearch.service로 OpenSearch 노드를 재시작하자.

plugins.security.disabled: true
cluster.name: opensearch-cluster
node.name: opensearch-cluster_manager
node.roles: [ cluster_manager ]
network.host: 0.0.0.0
discovery.seed_hosts: [0.0.0.0]
cluster.initial_cluster_manager_nodes: ["opensearch-cluster_manager"]

Elasticvue에 클러스터 매니저 노드를 연결하고 NODES 탭을 클릭해보자. 클러스터에 opensearch-cluster_manager 노드가 바인딩된 것을 확인할 수 있다.

Untitled

de-opensearch-cluster-practice_data1 EC2 인스턴스에 접속해서 다음과 같이 OpenSearch config를 수정하고 $ sudo systemctl restart opensearch.service로 OpenSearch 노드를 재시작하자.

plugins.security.disabled: true
cluster.name: opensearch-cluster
node.name: opensearch-d1
node.roles: [ data, ingest ]
network.host: 0.0.0.0
discovery.seed_hosts: ["<CLUSTER_MANAGER_NODE_PUBLIC_IP>"]

Elasticvue의 NODES 탭을 새로고침하면, 클러스터에 opensearch-d1 노드가 추가 바인딩된 것을 확인할 수 있다.

Untitled

de-opensearch-cluster-practice_data2 EC2 인스턴스에 접속해서 다음과 같이 OpenSearch config를 수정하고 $ sudo systemctl restart opensearch.service로 OpenSearch 노드를 재시작하자.

plugins.security.disabled: true
cluster.name: opensearch-cluster
node.name: opensearch-d2
node.roles: [ data, ingest ]
network.host: 0.0.0.0
discovery.seed_hosts: ["<CLUSTER_MANAGER_NODE_PUBLIC_IP>"]

Elasticvue의 NODES 탭을 새로고침하면, 클러스터에 opensearch-d2 노드가 추가 바인딩된 것을 확인할 수 있다.

Untitled

de-opensearch-cluster-practice_coordinator EC2 인스턴스에 접속해서 다음과 같이 OpenSearch config를 수정하고 $ sudo systemctl restart opensearch.service로 OpenSearch 노드를 재시작하자.

모든 노드는 코디네이터 노드 역할을 수행하므로, node.roles를 빈 배열로 설정하면 해당 노드는 코디네이터 역할만 수행하는 코디네이터 전용 노드가 된다.

plugins.security.disabled: true
cluster.name: opensearch-cluster
node.name: opensearch-c1
node.roles: []
network.host: 0.0.0.0
discovery.seed_hosts: ["<CLUSTER_MANAGER_NODE_PUBLIC_IP>"]

Elasticvue의 NODES 탭을 새로고침하면, 클러스터에 opensearch-c1 노드가 추가 바인딩된 것을 확인할 수 있다.

Untitled

3.4.3 클러스터 매니저 노드 재선출

opensearch-cluster_manager 노드가 실행중인 EC2 인스턴스에 접속해서 다음 명령어로 클러스터 매니저 노드를 중지시켜보자.

$ sudo systemctl stop opensearch.service

Elasticvue의 NODES 탭을 새로고침하면, 노드를 찾을 수 없다는 화면이 보일 것이다.

Untitled

Elasticvue를 opensearch-c1 노드의 public IP로 연결할 경우 코디네이터 노드와 해당 노드에 포함된 샤드의 정보는 확인할 수 있지만, 클러스터, 노드, 인덱스의 정보는 확인할 수 없다. 클러스터 매니저 노드는 클러스터 설정, 인덱스 설정, 노드 상태를 관리하는 역할을 담당하기 때문에 매니저 노드가 없는 상황에서는 Elasticvue도 해당 정보를 읽어올 수 없다.

{
  "error": {
    "root_cause": [
      {
        "type": "cluster_manager_not_discovered_exception",
        "reason": null
      }
    ],
    "type": "cluster_manager_not_discovered_exception",
    "reason": null
  },
  "status": 503
}

Untitled

Untitled

Untitled

클러스터 매니저 노드에 장애가 발생하는 상황에 대비하기 위해서는 클러스터 매니저 후보 노드를 설정해둬야 한다. node.rolescluster_manager를 포함하는 경우 클러스터 매니저 후보 역할을 수행하게 된다.

opensearch-d2 노드의 node.roles를 다음과 같이 수정하고 노드를 재시작하자.

node.roles: [ data, ingest, cluster_manager ]

모든 노드의 discovery.seed_hostsopensearch-d2 노드의 IP 주소가 등록되어 있지 않기 때문에 discovery에 실패하게 된다. 따라서 Elasticvue에서는 여전히 클러스터 정보를 읽어올 수 없을 것이다. (Elasticvue는 코디네이터 노드에 연결된 상태) 이 경우 opensearch-cluster_manager 노드를 재시작해야 클러스터를 활성화시킬 수 있다. 이후 Elasticvue를 확인해보면 opensearch-d2 노드에 클러스터 매니저 후보(master eligile) 역할이 추가된 것을 확인할 수 있을 것이다.

Untitled

opensearch-cluster_manager 노드를 다시 중지시켜보자. opensearch-d2가 새로운 클러스터 매니저 노드로 선출되고 클러스터가 정상화될 것이라는 예상과는 달리 클러스터는 여전히 작동을 멈춘 상태다. 이유는 에러 로그를 통해 알 수 있는데, 클러스터 매니저 노드 선출에 필요한 최소 후보 노드 수를 충족하지 못했기 때문이다.

  • message: which is not a quorum;
[2022-12-25T09:51:57,621][WARN ][o.o.c.c.ClusterFormationFailureHelper] [opensearch-d2] cluster-manager not discovered or elected yet, an election requires a node with id [k3y5PllfSzCZQKACMQN4tg], have discovered [{opensearch-d2}{OpwmNmeiQSCd19yKmZfKfQ}{Q-z5SfsoSB2-U4o5GLVVwg}{172.31.100.141}{172.31.100.141:9300}{dim}{shard_indexing_pressure_enabled=true}] which is not a quorum; discovery will continue using [13.125.209.78:9300] from hosts providers and [{opensearch-d2}{OpwmNmeiQSCd19yKmZfKfQ}{Q-z5SfsoSB2-U4o5GLVVwg}{172.31.100.141}{172.31.100.141:9300}{dim}{shard_indexing_pressure_enabled=true}, {opensearch-cluster_manager}{k3y5PllfSzCZQKACMQN4tg}{mTQPHU7tTNOV8YfUONlEuQ}{172.31.100.191}{172.31.100.191:9300}{m}{shard_indexing_pressure_enabled=true}] from last-known cluster state; node term 10, last-accepted version 84 in term 10

최소 후보 노드 수를 충족하기 위해 opensearch-d1 노드의 node.roles를 다음과 같이 수정하고, opensearch-cluster_manager 노드와 opensearch-d1 노드를 모두 재시작하자.

node.roles: [ data, ingest, cluster_manager ]

모든 노드가 클러스터링된 것을 확인한 다음 opensearch-cluster_manager 노드를 다시 중지시켜보자. Elasticvue의 NODES 탭을 새로고침하면 다음과 같이 opensearch-d1 (또는 opensearch-d2 ) 노드가 매니저 노드로 선출된 것을 확인할 수 있다.

Screen Shot 2022-12-25 at 7.28.17 PM.png

이 상태에서 opensearch-cluster_manager 노드를 다시 클러스터에 참여시키려면 discovery.seed_hosts를 다음과 같이 수정하고 노드를 재시작해줘야 한다.

discovery.seed_hosts: ["<DATA2_NODE_PUBLIC_IP>"]

opensearch-cluster_manager 노드가 다시 클러스터에 참여해도 이미 선출된 매니저 노드는 바뀌지 않고, 그대로 opensearch-d1 노드인 것을 확인할 수 있다.

Untitled

안전한 클러스터 운영을 위해 클러스터 매니저 후보 노드는 최소 3개 이상(클러스터 매니저 노드 포함)으로 설정하는 것이 좋다. 또한 discovery.seed_hosts에는 모든 클러스터 매니저 후보 노드를 포함시키는 것이 좋다.

OpenSearch와 달리 ElasticSearch의 경우 7.3 버전부터 투표 전용 노드 타입을 지원한다. 클러스터 매니저 후보 노드 중에서 매니저 선출 투표에는 참여할 수 있지만, 본인은 매니저 노드가 될 수 없는 노드를 투표 전용 노드라고 한다. 투표 전용 노드는 매니저 노드가 될 수 없기 때문에 매니저 후보 노드보다 부담없이 사용할 수 있으며, 대다수의 매니저 후보 노드들에 장애가 발생했을 때도 매니저 후보 선출 과정이 정상적으로 진행될 수 있도록 돕는다.

ElasticSearch에서 투표 전용 노드를 만들고 싶다면 node.roles를 다음과 같이 설정해주면 된다.

node.roles: [ cluster_manager, voting_only ]

3.4.4 인제스트 노드 파이프라인 설정

PUT /_ingest/pipeline/test-pipeline API로 인제스트 파이프라인을 생성할 수 있다. Elasticvue의 REST 클라이언트를 사용해서 요청해보자.

요청 본문에서 processors 필드는 리스트 타입으로, 여러 개의 프로세서를 정의할 수 있고, 해당 프로세서들은 리스트에 정의된 순서대로 실행된다. set 프로세서는 특정 필드의 값을 추가 또는 수정하는 작업을 수행하고, lowercase 프로세서는 string을 소문자로 변환하는 작업을 수행한다.

$ curl -XPUT "$OPENSEARCH_REST_API/_ingest/pipeline/test-pipeline?pretty=true" \
	-H "Content-Type: application/json" \
	-d '
{
  "processors": [
    {
      "set": {
        "field": "field1",
        "value": 10
      }
    },
    {
      "lowercase": {
        "field": "field2"
      }
    }
  ]
}
'

Untitled

test-pipeline 파이프라인을 사용해서 도큐먼트를 test1 인덱스에 인덱싱해보자.

$ curl -XPUT "$OPENSEARCH_REST_API/test1/_doc/1?pipeline=test-pipeline&pretty=true" \
	-H "Content-Type: application/json" \
	-d '
{
	"field1": "test",
	"field2": "Hello, OpenSearch!"
}
'

Untitled

인덱싱한 도큐먼트의 field1은 10으로, field2는 모두 소문자로 변경된 것을 확인할 수 있다.

profile
Data Analytics Engineer 가 되

0개의 댓글