본문 바로가기
개발/ELK

[Elasticsearch] 클러스터 구축하기 - 3

by parkkingcar 2023. 8. 21.

 

앞서 작성한 '클러스터 구축하기 - 1'을 기반으로, 시나리오를 통해 실제 클러스터를 구성할 때 고려해야 할 항목들을 알아보겠습니다.

 

엘라스틱서치는 클라이언트에 저장되는 로그를 장기간 모아 데이터를 분석/집계하는 분석 엔진으로 사용되거나, 검색에 쓰일 데이터를 저장하여 사용자의 검색 요청에 데이터를 제공하는 형태의 검색 엔진으로 서비스하는 것이 일반적입니다. 

 

 

시나리오 #1 - 일 100GB 데이터 분석용 클러스터

첫번째 시나리오는 분석 엔진으로 엘라스틱서치 클러스터를  가정하는 시나리오입니다.

 

- 시나리오 #1 : 하루에 100GB 정도의 데이터를 저장하면서 보관 기간이 한 달인 분석 엔진 클러스터

- 인덱스 이름 패턴 : elasticsearch-YYYY.MM.dd

- 프라이머리 샤드 기준 하루에 색인되는 인덱스 용량 : 100GB x 1일 = 100GB

- 인덱스 보관 기간 : 30일

- 레플리카 샤드  개수 : 1개

- 클러스터에 저장되는 전체 예상 용량 : 100GB x 30일 x 2(레플리카 샤드 1개) = 6TB

- 데이터 노드 한 대에 저장할 수 있는 용량 : 2TB

- 클러스터에 저장될 인덱스의 총 개수 : 30개

 

먼저 시나리오를 바탕으로 위와 같이 전체 용량 사이즈를 산정할 수 있습니다. 위 내용을 살펴보면 클러스터 전체 데이터 사이즈를 6TB로 산정해서 노드 3대만으로 구성하면 될 것 같지만 클러스터 전체용량은 이보다 더 크게 할당해야 합니다. 엘라스틱서치는 노드의 디스크 사용량이 85%가 넘으면 해당 노드에 샤드 할당을 지양합니다. 따라서 노드의 최대 데이터 적재 용량을 80%로 잡고 생각해보겠습니다.

 

그러면 클러스터의 전체용량을 7.5TB로 산정할 수 있습니다. 즉, 노드의 대수도 3대가 아님 4대가 되어야 합니다. 하지만 노드의 디스크 사용량 측면에서는 이렇게 정상적인 상태만 고려해서는 안됩니다. 예를들어 데이터 노드의 장애가 오랜 시간 이어질 경우 다른노드에서 충분한 용량을 고려해야 합니다. 노드를 5대로 구성하게 되면 데이터 노드 한 대에 장애가 발생하더라도 8TB까지 저장할 수 있게됩니다. 이런 상황까지 고려하여 클러스터를 총 5대의 데이터 노드로 구성해야합니다.

 

용량에 대한 노드 개수를 산정하였다면, 데이터 노드간 볼륨 사용량의 불균형이 발생하지 않도록 데이터 노드의 n배로 샤드의 개수를 산정합니다. 보통 분석 엔진에서는 샤드 하나의 크기를 20-40GB정도로 할당하는 것을 권고합니다.

 

 

위 내용을 바탕으로 시나리오 #1의 전체 클러스터 구성을 정리하면 다음과 같습니다.

 

- 시나리오 #1 : 하루에 100GB 정도의 데이터를 저장하면서 보관 기간이 한 달인 분석 엔진 클러스터

- 인덱스 이름 패턴 : elasticsearch-YYYY.MM.dd

- 프라이머리 샤드 기준 하루에 색인되는 인덱스 용량 : 100GB x 1일 = 100GB

- 인덱스 보관 기간 : 30일

- 레플리카 샤드  개수 : 1개

- 클러스터에 저장되는 전체 예상 용량 : 100GB x 30일 x 2(레플리카 샤드 1개) = 6TB

- 데이터 노드 한 대에 저장할 수 있는 용량 : 2TB

- 클러스터에 저장될 인덱스의 총 개수 : 30개

- 데이터 노드 대수 : 5대

- 데이터 노드 한 대의 볼륨 할당률 : 6TB / 10TB x 100 = 60%

- 데이터 노드 한 대 장애 시 나머지 데이터 노드 한 대당 볼륨 할당률 : 6TB / 8TB x 100 = 75%

- 인덱스를 구성하는 프라이머리 샤드의 개수 : 10개

- 클러스터의 전체 인덱스에 의해 생성되는 샤드의 총 개수 : 10 x 30일 x 2(레플리카 샤드) = 600개

- 인덱스 하나를 기준으로 노드 한 대에 할당되는 샤드 개수 : 10 x 2(레플리카 샤드) / 5(노드개수) = 4개

- 인덱스 전체를 기준으로 노드 한 대에 할당되는 샤드의 총 개수 : 10 x 2(레플리카 샤드) x 30일 / 5(노드개수) = 120개

 

추가적으로 색인이 끝난 인덱스는 forcemerge API로 검색 성능을 확보하고, readonly로 설정하여 Shard Request Cache가 삭제하지 않도록 설정하여 분석 엔진에 성능을 향상시킬 수 있습니다.

 

 

 

시나리오 #2 - 일 1GB 데이터 분석과 장기간 보관용 클러스터

두번째 시나리오는 하루에 1GB 미만의 소량의 데이터를 저장하면서 보관기간이 연 단위가 넘는 클러스터를 가정하는 시나리오입니다.

 

- 시나리오 #2 : 하루에 1GB 정도의 데이터를 저장하면서 보관 기간이 3년인 분석 엔진 클러스터

- 인덱스 이름 패턴 : elasticsearch-YYYY.MM.dd

- 프라이머리 샤드 기준 한 달에 색인되는 인덱스 용량 : 1GB x 30일 = 30GB

- 인덱스 보관 기간 : 3년

- 레플리카 샤드  개수 : 1개

- 클러스터에 저장되는 전체 예상 용량 : 30GB x 36개월 x 2(레플리카 샤드 1개) = 2.16TB

- 데이터 노드 한 대에 저장할 수 있는 용량 : 2TB

- 클러스터에 저장될 인덱스의 총 개수 : 36개

 

몇 가지 사항을 제외하면 시나리오 #1과 유사하지만 시나리오 #2에서는 인덱스를 일별 단위가 아니라 월별로 보관하는 것이 좋습니다. 저장공간 측면을 고려하면 데이터 노드는 2대로 충분하지만 노드의 장애발생을 고려하여 3대로 산정합니다.

 

이렇게 되면 클러스터의 전체 가용량이 6TB가 됩니다. 이때 남는 용량은 레플리카 샤드를 추가해서 안정성을 강화하는 형태로 활용할 수 있습니다. 레플리카 샤드가 2개가 되면 장애가 발생하더라도 모든 복제본이 하나씩 더 존재하여 데이터 안정성이 보장됩니다. 또한, 장애가 발생하지 않은 상황에서는 더 많은 노드가 검색 요청에 대한 응답을 줄 수 있기 때문에 검색 성능도 향상됩니다.

 

 

위 내용을 바탕으로 시나리오 #2의 전체 클러스터 구성을 정리하면 다음과 같습니다.

 

- 시나리오 #2 : 하루에 1GB 정도의 데이터를 저장하면서 보관 기간이 3년인 분석 엔진 클러스터

- 인덱스 이름 패턴 : elasticsearch-YYYY.MM.dd

- 프라이머리 샤드 기준 한 달에 색인되는 인덱스 용량 : 1GB x 30일 = 30GB

- 인덱스 보관 기간 : 3년

- 레플리카 샤드  개수 : 1개 ->2개

- 클러스터에 저장되는 전체 예상 용량 : 30GB x 36개월 x 3(레플리카 샤드 2개) = 3.24TB

- 데이터 노드 한 대에 저장할 수 있는 용량 : 2TB

- 클러스터에 저장될 인덱스의 총 개수 : 36개

- 데이터 노드 대수 : 2대

- 데이터 노드 한 대의 볼륨 할당률 : 3.24TB / 6TB x 100 = 54%

- 데이터 노드 한 대 장애 시 나머지 데이터 노드 한 대당 볼륨 할당률 : 3.24TB / 4TB x 100 = 81%

- 인덱스를 구성하는 프라이머리 샤드의 개수 : 6개

- 클러스터의 전체 인덱스에 의해 생성되는 샤드의 총 개수 : 6 x 36개월 x 3(레플리카 샤드) = 648개

- 인덱스 하나를 기준으로 노드 한 대에 할당되는 샤드 개수 : 6 x 3(레플리카 샤드) / 3(노드개수) = 6개

- 인덱스 전체를 기준으로 노드 한 대에 할당되는 샤드의 총 개수 : 6 x 3(레플리카 샤드) x 36개월 / 3(노드개수) = 216개

 

시나리오 #2와 같이 인덱스를 오랜 기간 저장하는 구조에서는 close API가 유용할 수 있습니다. 특히, 실제 조회는 하지 않지만 보관해야 할 필요가 있을때 유용합니다.

 

추가적으로 색인이 끝난 인덱스는 forcemerge API로 검색 성능을 확보하고, readonly로 설정하여 Shard Request Cache가 삭제하지 않도록 설정하여 분석 엔진에 성능을 향상시킬 수 있습니다. 또한, 조회하지 않는 1년이 지난 인덱스는 read only 설정을 해제하고 close API로 클러스터의 open 샤드에서 제외 처리 하여 성능을 확보합니다.

 

 

 

 

시나리오 #3 - 검색 엔진으로 활용하는 클러스터

세번째 시나리오는 검색 엔진으로 서비스하는 상황을 가정하는 시나리오입니다.

 

- 100ms 내에 검색 결과가 제공되어야 합니다.

- 검색 엔진에 사용할 데이터는 500GB입니다.

- 검색 요구사항이 변경되어 매핑이 변경될 수 있습니다.

 

검색 엔진의 경우 가장 중요한 부분은 응답속도입니다. 사용자가 요청한 검색 쿼리가 특정 시간내에 응답을 줄 수 있어야 합니다. 예를 들어 100ms내에 응답을 주어야 한다고 가정하면, 이 조건을 충족시키기 위해 데이터를 가지고 있는 노드가 모두 100ms 내에 응답해야 하고 동일한 성능을 보장해야 합니다.

 

엘라스틱서치는 기본적으로 1쿼리, 1샤드, 1스레드를 기준으로 검색 요청을 처리합니다. 만약 샤드가 지나치게 많을 경우 검색 요청의 처리를 완료하기 전까지 모든 검색 쿼리가 처리되지 못하고 검색 스레드 큐에 위치하고, 이 큐가 가득차면 검색 성능을 저하시키는 원인이 됩니다.

 

실제 테스트로 데이터 노드 한대로 클러스터를 구성하고, 해당 노드에 데이터를 저장한 후 사용자의 검색 쿼리에 대한 응답을 100ms 이하로 줄 수 있는지 테스트합니다. 이때 레플리카 샤드 없이 프라이머리 샤드 1개로 클러스터를 구성하고 해당 샤드에 데이터를 계속 색인하면서 샤드의 크기가 커짐에 따라 검색 성능이 어떻게 변화하는지 측정합니다. 테스트 결과를 바탕으로, 인덱스의 크기가 25GB였을 때 100ms응답속도에 도달한다고 가정하여 인덱스의 크기  500GB를 25GB로 나누어 샤드의 개수를 20개로 산정합니다.

 

검색 엔진의 경우는 클러스터의 전체 디스크 사용량보다 스레드 풀로 사용할 CPU코어와 메모리를 충분히 확보하는 것이 중요합니다. 노드 한 대당 30GB의 힙 메모리를 할당해 놓았다고 가정하면 20대로 구성해야 모든 데이터를 검색하는 쿼리가 입력되었을 때 Full GC 가 발생하지 않을 것 입니다.

 

 

위 내용을 바탕으로 시나리오 #3의 전체 클러스터 구성을 정리하면 다음과 같습니다.

 

- 시나리오 #3 : 사용자의 검색 요청에 대해 100ms 내에 응답을 줄 수 있는 검색 엔진 클러스터

- 인덱스 이름 패턴 : search_index_v1

- 프라이머리 샤드 기준 한 달에 색인되는 인덱스 용량 : 500GB x 1일 = 500GB

- 인덱스 보관 기간 : 재색인 요구가 있을 때 까지 보관

- 레플리카 샤드  개수 : 1개

- 클러스터에 저장되는 전체 예상 용량 : 500GB x 1일 x 2(레플리카 샤드) = 500GB

- 클러스터의 전체 인덱스에 의해 생성되는 샤드의 총 개수 : 20 x 2(레플리카 샤드) = 40개

 

- 데이터 노드 대수 : 20대

- 데이터 노드의 메모리 할당 : 64GB

- 데이터 노드의 힙 메모리 할당 : 30GB

- 클러스터 전체의 힙 사이즈 : 30GB x 20대 = 600GB ( > 인덱스 전체 크기 = 500GB)

- 인덱스 하나를 기준으로 노드 한 대에 할당되는 샤드 개수 : 20 x 2(레플리카 샤드) / 20(노드개수) = 2개

 

 

 

 

 

 

이 글은 '기초부터 다지는 ElasticSearch 운영 노하우 - 박상헌, 강진우'를 기반으로 작성하였습니다.

댓글