AWS 기술 블로그
동작 방식과 함께 알아보는 최적의 Amazon OpenSearch Service 사이징
서론
OpenSearch 는 Apache Lucene 검색 라이브러리로 구동되며, K-NN 검색, SQL, 이상 탐지, 전체 텍스트 검색 등 다양한 검색 및 분석 기능을 제공하는 Elasticsearch 에서 파생된 오픈 소스 입니다. 그리고 Amazon OpenSearch Service 는 OpenSearch 및 레거시 Elasticsearch OSS (7.10까지) 를 지원하는 관리형 오픈 소스 검색 엔진입니다.
이러한 OpenSearch Service 를 활용하기 위해서는 도메인이라고 불리는 OpenSearch 클러스터를 구성하게 됩니다. 최근에는 서버리스로 제공되는 Amazon OpenSearch Serverless 옵션도 있지만, 이를 활용하는 경우를 제외하면 각 도메인은 지정한 컴퓨팅 및 스토리지 리소스를 갖습니다. 따라서, 최적의 성능을 위해서는 스토리지 크기부터 올바른 인스턴스 타입 및 개수, 인덱스 관리 전략 그리고 논리적인 샤드와 복제본을 구성하는 등의 많은 고민이 필요합니다. 관리형으로 제공되는 서비스기 때문에 자체적으로 스케일링을 지원하지만, 여전히 분산 환경으로 제공되는 서비스에서는 사용자가 직접 리밸런싱을 위한 추가적인 작업이 필요합니다.
이번 블로그에서는 OpenSearch Service 의 아키텍처와 동작 방식 등을 살펴보며, 각각에서 도메인의 최적의 성능을 위한 리소스 사이징에 관해 알아보겠습니다.
Amazon OpenSearch Service 아키텍처
OpenSearch Service 에서 도메인을 생성하게 되면 구성에 따라 차이는 있지만 전용 마스터 노드 (Dedicated master nodes) 와 Hot 데이터 노드 (Data nodes) 그리고 웜 및 콜드 데이터 스토리지 (UltraWarm and cold storage) 를 구성하게 됩니다.
여기에서 전용 마스터 노드는 클러스터의 전체 노드, 인덱스, 샤드에 대한 추적과 라우팅, 상태 업데이트 등 리소스 관리 작업을 전담하기 때문에 사용하는 것을 권장합니다. 인스턴스 타입을 선택할 때는 데이터 노드와 이후에 설명할 샤드의 개수를 고려해야 하며, 다음 문서에서 권장하는 타입을 선택하는 것이 좋습니다. 세개의 인스턴스로 마스터 노드가 구성되는 이유는 고가용성 및 쿼럼 방식의 복원을 하기 때문이며 평소에 다른 두개의 노드는 대기상태로 모니터링 합니다.
데이터 노드는 실제로 인덱싱된 데이터를 저장하고 있는 노드로 검색과 쿼리 요청을 수행합니다. 3개의 가용영역을 모두 활용하기 위해 3개 이상의 인스턴스를 구성하는 것을 권장하며 이때, 인스턴스 타입이나 개수는 워크로드에 기반해 결정해야 합니다. OpenSearch Service 에서는 인스턴스 타입에 따른 CPU, 메모리 외에도 지원되는 EBS 볼륨의 크기 제한이 있습니다. 다음 문서에서 EBS 볼륨 할당량에 대한 확인이 가능하며, 이 블로그에서 설명하는 내용을 참고하여 초기 구성한 뒤에도 지속적인 모니터링으로 리소스를 조절하는 작업이 필요합니다.
Amazon OpenSearch Sevice 의 내부 구조
OpenSearch Service 동작 방식과 리소스 사이징을 위해서는 우선 내부 구조에 대한 이해가 필요합니다.
OpenSearch 의 내부는 이와 같이 구성되어 있습니다. 우선 단일 데이터 단위인 문서(Document)는 사용자가 입력하는 데이터의 최소단위로 JSON 포맷으로 되어 있습니다. 이러한 문서가 모인 전체 데이터 세트가 인덱스(Index)에 해당합니다. 그리고 이러한 인덱스는 샤드(Shard)라는 단위로 분산되어 데이터 노드에 랜덤하게 분산됩니다. 내부적으로 Apache Lucene 을 사용하기 때문에 샤드는 Lucene 인덱스와 1:1 로 연결됩니다. 그리고 Lucene 인덱스가 문서를 세그먼트(Segment) 단위로 나누어 스토리지에 저장을 합니다. 사용자가 입력한 개별 문서는 실제로는 여기에 존재하게 되는 구조입니다. Lucene 에 대한 자세한 정보는 다음 문서에서 확인할 수 있습니다.
샤드에 관한 이해와 최적의 성능을 위한 샤드 구성
앞서 설명한 것과 같이 인덱스는 여러 개의 샤드로 분할되어 데이터를 저장하고 관리하게 됩니다. 샤드는 Lucence 의 단일 검색 인스턴스인데, 기본 샤드 (Primary Shard) 와 복제본 샤드 (Replica Shard) 가 존재합니다.
- 기본 샤드 (Primary Shard): 오리지날(Original) 샤드라고도 하며, 인덱스 내의 데이터를 보관하고 실제 검색 및 쿼리가 이루어 집니다.
- 복제본 샤드 (Replica Shard): 각 기본 샤드의 복제본으로 내구성 및 처리량 향상에 사용됩니다. 기본 샤드와는 다른 데이터 노드에 분산됩니다.
OpenSearch Service 는 디폴트로 각 인덱스를 5개의 기본 샤드로 나누는 5:1 샤딩 전략을 사용합니다. 위 그림의 빨간색 기본 샤드는 여러 데이터 노드에 랜덤하게 저장되며, 실제 리소스 활용과 직결되기 때문에 성능과 밀접한 관계가 있습니다. 복제본 샤드의 경우 디폴트로 1개를 갖게 됩니다.
이 때 각각의 샤드 고정적인 CPU 와 메모리를 할당받기 때문에 적절한 개수를 지정해주는게 중요합니다. 특히 기본 샤드의 경우 처음 인덱스 생성 시에 지정하고 그 뒤에 변경하는 경우 시간이 많이 소요되는 리인덱스 (reindex) 작업이 필요해 신중하게 구성되어야 합니다. 복제본 샤드는 상대적으로 변경이 자유로워 동적으로 운영이 가능합니다.
기본 샤드의 개수를 지정할 때는 다음과 같은 계산식을 참고할 수 있습니다.
(Source data + room to grow) * (1 + indexing overhead) / desired shard size = approximate number of primary shards
하지만 이 역시 절대적인 공식은 아니며, 인덱스 패턴과 데이터 크기, 노드 개수 등을 고려해 구성해야 합니다.
- 일반적으로 많은 샤드를 적은 수로 구성할 경우 리소스 할당에는 무리가 없지만 개별 샤드의 크기가 늘어나서 할당된 리소스에서 검색해야하는 데이터가 늘어납니다.
- 반대로 작은 샤드를 많이 구성하게 되면 샤드 당 처리 속도는 빠르지만 모든 샤드가 메모리 큐에 들어간 뒤 순차 처리되는 지연이 발생합니다. 또한 샤드 당 고정 리소스가 할당되기 때문에 더 큰 클러스터가 필요합니다.
- 결과적으로 상대적으로 큰 샤드를 적게 유지하는 것이 작은 샤드를 많이 유지하는 것보다는 동일한 크기의 클러스터에서는 유리합니다. 또한 샤드의 개수는 CPU 코어 대비 해서 < 1:1 을 유지하는 것을 추천합니다.
- 대부분의 경우 샤드의 크기는 50GB 이하로 구성하는 것이 좋습니다. 하지만, 페타바이트 이상의 데이터를 다루는 경우에는 100GB 로 구성하는 것이 추천됩니다. 인덱스 패턴에 따라 Long-term retention 인덱스 는 10GB ~ 30GB 를 추천하며, Rolling 인덱스의 경우 10GB ~ 50GB 를 추천합니다.
마지막으로 샤드는 데이터 노드와도 밀접한 연관이 있습니다. 아래는 데이터 노드를 3개 구성한 상황에서 샤드가 분산되는 것을 표현한 것입니다.
모든 노드의 자원을 충분히 활용하기 위해서는 샤드가 골고루 배포되어야 하는데, 우측과 같이 노드의 배수가 아닌 샤드를 구성하는 경우 세번째 노드는 샤드가 배포되지 않아 낭비됩니다. 그래서 이와 같이 데이터 노드를 3개 구성했다면 기본 샤드는 3, 6, 9 와 같은 노드의 배수로 구성하는 것이 좋습니다.
정리해보면, 기본 샤드는 데이터 노드의 배수로 구성하고, 이 때 인덱스 패턴과 용량을 함께 고려하여 개수를 설정해야 합니다.
복제본 샤드는 구성 변경이 비교적 자유롭기 때문에 디폴트 값인 1개를 유지해도 되지만, 가용성을 높이고 싶다면 가용 영역과 데이터 노드를 고려해 구성해야 합니다.
복제본 샤드는 가용성을 위해 기본 샤드와는 다른 데이터 노드에 배포가 이루어집니다. 운영 환경에서 OpenSearch Service 의 도메인은 3개의 가용 영역을 사용하게 구성하는게 일반적인데, 이 중 2개에 문제가 생기더라도 데이터를 문제 없이 구성하기 위해서는 좌측과 같이 복제본 샤드를 2개 구성해주는 것이 좋습니다. 참고로 OpenSearch 는 문서 업데이트시 기본 샤드에 먼저 쓴 뒤에 복제본으로 전파가 이루어지기 때문에 너무 많은 복제본을 구성하시면 업데이트 성능에 영향이 발생할 수 있습니다.
예시를 통해 지금까지의 내용을 바탕으로 샤드 개수를 계산해보겠습니다.
(예시)
- 하루에 200GB 씩 발생하는 로그성 데이터, 인덱스는 일 단위 (혹은 200GB 용량 단위) 로 롤오버 (Rollover)
- Rolling 인덱스 패턴이므로 샤드의 크기는 30GB 로 구성
- 3개의 가용영역에 3개의 데이터 노드를 구성
이러한 상황에서 기본 샤드를 위에 설명 드렸던 공식에 따라 계산해보면 다음과 같습니다.
- (Source data + room to grow) * (1 + indexing overhead) / desired shard size = approximate number of primary shards
- (200GB + 0GB) * (1 + 0.1) / 30GB = 7.33
- 이 때 기본 샤드를 7개로 구성할 경우 3개의 데이터 노드에 균등하게 분산되지 않으므로 이를 6이나 9로 구성할 수 있습니다.
- 여기에서 상대적으로 큰 샤드를 적게 유지하는 것이 유리하므로 기본 샤드 개수는 6개로 생각해볼 수 있습니다.
- 이 때 복제본 샤드를 2개로 구성하게 되면 6 * 2 = 12개가 구성됩니다.
이 외에도 샤드 개수를 계산하는 추가적인 예시는 다음 문서에서 확인하실 수 있습니다.
스토리지 옵션과 필요한 크기 예측
OpenSearch Service 에서는 데이터 접근이나 사용 빈도에 따라 Hot, UltraWarm, Cold 스토리지의 세가지 옵션을 제공합니다.
- Hot: 데이터에 대한 빠른 액세스를 제공하며 인덱싱 및 업데이트에 사용됩니다. 각 노드에 연결된 인스턴스 저장소나 Amazon EBS 볼륨이 사용됩니다. 실시간 분석 및 자주 액세스가 필요한 데이터를 저장할 때 적합하며, 가장 높은 성능을 제공하는 대신 비용도 가장 높습니다.
- UltraWarm: Amazon S3 를 사용하여 읽기 전용 데이터를 저장하는데, 성능 향상을 위해 캐싱 솔루션이 함께 사용됩니다. 자주 쿼리하지 않는 읽기 전용 데이터에 대해서 Hot 스토리지 만큼의 성능은 필요 없는 경우 비용 효과적으로 활용할 수 있습니다. 데이터를 수정을 위해서는 Hot 스토리지로 이동한 뒤 수정을 해야합니다. 데이터에 처음 접근할 때 S3 에서 데이터를 UltraWarm 노드로 이동시켜 캐싱하기 때문에 후속 쿼리에 대해서는 성능이 올라갑니다.
- Cold 스토리지: 액세스 빈도가 매우 낮은 데이터를 오랫동안 보관하는 것에 최적화 되어 있습니다. 인스턴스가 없기 때문에 Cold 스토리지의 데이터는 직접 검색할 수 없으며, UltraWarm 계층으로 데이터를 가져온 뒤에 접근할 수 있습니다.
전체적으로 왼쪽으로 갈수록 비용과 성능이 높아지고, 오른쪽으로 갈수록 비용이 낮아지는 대신 검색 성능 역시도 낮아지게 됩니다.
각각의 스토리지 옵션 별로 크기를 예측할 때는 고려할 점이 있습니다. 공통적으로 인덱스의 매핑 정보를 고려해서 오버헤드 10% 정도를 생각해야 합니다. 그 외에는 각각 다음의 내용이 고려되어야 합니다.
- Hot: 복제본 샤드가 존재하기 때문에 우선 구성한 복제본 수만큼의 스토리지가 추가로 필요합니다. 그 다음 OS 가 사용할 예약 공간을 계산해서 나누어 주게 되는데, 보통 5% 가 필요합니다. 마지막으로 OpenSearch Service 가 내부적으로 사용하는 오버헤드가 노드별로 20% 존재하며 보통은 20GB 까지 사용됩니다.
- UltramWarm 및 Cold 스토리지: 인덱스가 S3 에 저장되기 때문에 인덱스의 오버헤드에 대한 고려만 필요하며, 복제본이나 OS, OpenSearch Service 오버헤드를 고려할 필요는 없습니다.
예시를 통해 스토리지 크기도 계산해 보겠습니다.
(예시)
- 하루에 300GB 씩 발생하는 로그성 데이터, 인덱스는 일 단위로 롤오버
- 복제본 샤드는 2개 구성
- 데이터는 5년 (1866일) 을 저장하는데, 이 때 Hot 에는 8일, UltraWarm 에 360일, 이 후 1498일은 Cold 스토리지에 저장
이 경우 각각의 스토리지 티어 별로 필요한 크기는 아래와 같이 계산해볼 수 있습니다.
- Hot: (300GB * (1 + 0.1) * (1 + 2) / (1 – 0.05) / (1 – 0.2)) * 8일 = 약 10TB
- UltraWarm: (300GB * (1 + 0.1) * (1+0)) * 360일 = 약 116TB
- Cold 스토리지: (300GB * (1 + 0.1) * (1 + 0)) * 1498일 = 약 482TB
이 외에도 스토리지 크기를 계산하는 추가적인 내용 및 예시는 다음 문서에서 확인해볼 수 있습니다.
이번 블로그에서는 노드에서 사용되는 데이터 볼륨 옵션에 관해 다루지는 않습니다. 현재 EBS 를 활용할 경우 gp2, gp3, io1 등이 지원되며, r6gd, i3 와 같은 인스턴스 타입을 활용하시는 경우 NVMe 기반의 인스턴스 스토어를 사용할 수도 있습니다. 해당 스토리지 옵션에 대한 자세한 내용은 다음의 블로그를 참고하시기 바랍니다.
인스턴스 타입 선택 가이드
OpenSearch Service 는 전용 마스터 (Dedicated master) 노드, Hot 데이터 노드, UltraWarm 과 같이 다양한 인스턴스로 도메인이 구성되는 서비스입니다. 이번 블로그의 마지막 주제로 각각의 노드 별로 권장하는 인스턴스 타입을 살펴 보겠습니다.
1. 전용 마스터 (Dedicated master) 노드
전용 마스터 노드는 인덱스, 샤드, 노드를 추적하고 라우팅, 상태 업데이트 등 리소스 및 작업 관리를 전담하는 역할을 수행합니다. 운영 환경에서는 데이터 노드가 문서 업데이트나 검색 작업만을 수행할 수 있도록 마스터 노드를 구성하는 것이 권장되며, 쿼럼 방식의 복원을 지원하기 때문에 3개를 구성하는 것을 권장합니다. 짝수개를 선택해서는 안됩니다. 이 때 하나의 마스터 노드만 활성화되고, 나머지 노드들은 후보 노드로 유휴 상태로 동작하기 때문에 5개 이상 구성하시는 것도 바람직하지 않습니다.
이 때 전용 마스터 노드는 클러스터 크기, 인덱스와 샤드 개수에 따라 구성이 되어야 합니다. 일반적으로 전체 인스턴스 수에 따라 추천되는 인스턴스 타입이 있으며, 지원 가능한 최대 샤드의 개수도 함께 고려되어야 합니다. 아래 표를 참고해 구성하는 것을 추천합니다.
인스턴스 개수 | 최대 지원 가능한 샤드 개수 | 추천 인스턴스 타입 |
1 ~ 10 | 10,000 | m5.large.search / m6g.large.search |
11 ~ 30 | 30,000 | c5.2xlarge.search / c6g.2xlarge.search |
31 ~ 75 | 40,000 | r5.xlarge.search / r6g.xlarge.search |
76 ~ 125 | 75,000 | r5.2xlarge.search / r6g.2xlarge.search |
126 ~ 200 | 75,000 | r5.4xlarge.search / r6g.4xlarge.search |
한가지 주의할 것은 데이터 노드와 프로세서 종류를 동일하게 구성해야 합니다. 가령, 데이터 노드를 Graviton 으로 선택했다면 마스터 노드 역시 Graviton 으로 구성을 해야하고, Intel 의 경우도 마찬가지 입니다. 또한 표의 인스턴스 개수는 Hot 데이터 노드, UltraWarm 노드, 마스터 노드의 합으로 이해하시면 됩니다.
2. 데이터 노드
데이터 노드는 Hot 데이터 노드와 UltraWarm 노드가 존재하는데, Hot 노드는 Amazon EC2 를 선택하는 것과 유사하게 인스턴스 타입을 선택할 수 있습니다. 예상 스토리지 사용량과 필요한 샤드 개수를 결정한 뒤에 데이터 노드의 인스턴스 타입도 결정이 되어야 합니다. 이 때도 고가용성을 위해 최소 2개, 권장 3개 이상 구성하는 것을 추천합니다.
스토리지의 경우 앞부분 설명에 따라 크기를 예측했다면, 실제로 노드 별로 필요한 스토리지 양은 구성하는 노드의 개수로 나누어 생각하면 됩니다. 가령 스토리지 요구량이 184GB 였고, 노드를 3개 구성할 예정이라면 184 / 3 = 약 61.33GB 를 각 노드에 필요한 스토리지 양으로 생각할 수 있습니다.
OpenSearch Service 에서 지원되는 대표적인 인스턴스 타입은 C, M, R, I 패밀리가 있으며 각각이 갖는 vCPU 대 메모리 비율 등의 차이가 있습니다. 일반적인 워크로드에서는 M 패밀리로 구성하는게 좋은 선택이며, 테스트를 수행하며 Amazon CloudWatch 에서 CPUUtilization, JVMMemoryPressure 등의 지표를 확인하며 이를 변경할 수 있습니다.
UltraWarm 의 경우 다음의 표와 같이 2가지의 인스턴스 유형을 제공합니다. 유형 별로 관리할 수 있는 스토리지 크기, vCPU 와 메모리, 캐싱 스토리지의 크기에 차이가 있기 때문에 워크로드를 고려하여 알맞은 노드를 선택해야 합니다.
인스턴스 타입 | 최대 스토리지 | vCPU 및 메모리 | 캐싱 스토리지 | 최대 지원 가능한 샤드 개수 |
ultrawarm1.medium.search | 1.5TB | 2 vCPU, 16GB 메모리 | 475GB | 약 400개 |
ultrawarm1.large.search | 20TB | 16 vCPU, 122GB 메모리 | 3.8TB | 약 1,000개 |
이 외에도 더욱 상세한 인스턴스 타입별 할당량은 다음 문서를 참고하시기 바랍니다.
결론
지금까지 Amazon OpenSearch Service 를 보다 효과적으로 활용하기 위해 내부 아키텍처부터 최적의 샤드와 스토리지를 예상하고, 그에 따른 데이터 노드를 선택하는 방법까지 살펴봤습니다.
사이징에는 여러가지 고려해야할 점이 있지만, 어떻게 시작해야할지 고민이 되신다면 위의 순서대로 도메인을 구성해보는 것이 좋은 시작이 될 수 있습니다. 우선 스토리지 크기와 샤드 개수를 계산하고, 그에 기반하여 적절한 노드를 선택하는 방식입니다. 이러한 방식에 정확한 답은 없으며 비용 효율적으로 원하는 성능의 도메인을 구성할 때까지 인스턴스 유형을 변경하거나 추가하면서 반복적으로 테스트를 해볼 수 있습니다.
사이징 뿐 아니라 모니터링이나 보안 등 더욱 다양한 방면의 모범 사례는 다음 문서를 참고할 수 있으며, 고가용성 구성에 관한 문서나 인덱싱 성능을 최적화 하는 문서도 함께 참고해보시기 바랍니다.