본문 바로가기

IT Insights

엘라스틱서치의 멀티 테넌트 환경 적용하기- 케이스로 살펴보는 테넌트 적용 방법

엘라스틱서치에 멀티테넌트 환경이 필요할까?

4차 산업혁명 시대를 맞아 디지털 경제의 기반 인프라로 클라우드 컴퓨팅이 주목 받고 있습니다. 클라우드 환경의 멀티테넌트(Multi-tenant)는 하나의 소프트웨어 인스턴스를 공유함으로써 규모의 경제를 통한 비용 절감을 실현하는 아키텍처로 활용됩니다. 각 테넌트는 회사, 부서 등의 조직이나 이용자 유형 등의 단위로 구성되어 동일한 하나의 소프트웨어를 운영하면서도 테넌트마다 고유한 데이터와 특정 기능을 사용할 수 있습니다. 만약 여러분이 클라우드 서비스 공급자로서 전문 검색(Full-text search), 비정형 로그 데이터 수집과 통계 분석 등의 기능을 제공하기 위해 엘라스틱서치(Elasticsearch)를 사용한다면 앞서 언급한 전제에 따라 테넌트 환경 적용을 고려할 필요가 있습니다. 본 아티클에서는 멀티테넌트 환경에서 엘라스틱서치를 구성하는 방법과 고려 사항을 살펴보겠습니다.


엘라스틱서치의 멀티테넌트 환경 구성

엘라스틱서치의 데이터는 인덱스(Index)라는 논리적인 집합 단위로 관리됩니다. 인덱스는 멀티테넌트 환경을 구성하기 위한 기준으로서 크게 다음의 두 가지 구성 사례를 들 수 있습니다.
 
 - 각 테넌트마다 별도의 인덱스 사용하기
 - 모든 테넌트가 하나의 인덱스 사용하기
 
각 테넌트마다 별도의 인덱스 사용하기

테넌트마다 인덱스를 사용하면 유연성과 데이터 격리 측면에서 이점을 꾀할 수 있습니다. 하지만 운용 환경에서 테넌트 수와 각 인덱스의 데이터 볼륨이 증가하면서 잠재적인 문제점을 야기할 수 있습니다.
하나의 인덱스에는 방대한 양의 데이터를 저장할 수 있지만 엘라스틱서치가 구동되는 서버 하드웨어의 한도를 초과할 수도 있습니다. 이를 방지하기 위해 인덱스는 샤드(Shard)라는 조각으로 분할해 관리합니다.

[그림 1] 인덱스별 샤드 구성


[그림 1]은 각 인덱스의 샤드 구성을 나타내는 예시입니다. 클러스터(Cluster)는 엘라스틱서치가 구동되는 단일 서버인 노드(Node)들의 집합으로 이루어져 있습니다. 각 노드에는 인덱스의 샤드가 분산 배치되며 노드와 샤드의 수는 운영 환경에 따라 적합하게 조정 가능합니다. 테넌트마다 인덱스를 생성하면 [그림 1]과 같이 테넌트의 수만큼 샤드의 수도 증가합니다. 그런데 샤드 수의 증가는 메모리 사용량과 OS 자원 점유의 오버헤드 발생 측면에서 밀접한 관련이 있습니다. 엘라스틱서치는 아파치 검색 라이브러리인 루씬(Lucene)을 기반으로 만들어져 [그림 2]와 같은 구조로 구성되어 있는데 각 샤드를 이루고 있는 루씬 인덱스가 오버헤드를 유발하기 때문입니다. 따라서 테넌트마다 인덱스를 추가하는 만큼 필요한 하드웨어 자원은 증가하고 사용량이 거의 없는 테넌트라도 기본적으로 요구되는 하드웨어 자원이 발생하게 됩니다.

[그림 2] 샤드 구조


그렇다면 인덱스를 여러 개 사용하면 안 되는 것일까요? 테넌트 별로 인덱스를 생성할 데이터 구조가 상이하거나 특정 테넌트의 데이터가 하나의 인덱스에서 관리하기에 적합하지 않을 정도로 비대해진 경우에는 다수의 인덱스 운용을 고려해 볼 수 있을 것입니다.

엘라스틱서치는 인덱스나 샤드의 수에 엄격한 제약을 두지 않습니다. 그러나 서버의 가용 자원 제약과 성능 최적화를 위해 엘라스틱서치를 개발한 엘라스틱(Elastic)사에서는 가급적 샤드의 크기를 20~40GB로 유지할 것을 권장합니다. 이와 더불어 인덱스의 샤드 수는 한 번 설정되면 변경이 어려우므로 이러한 경우에 새로운 인덱스의 추가를 고려할 수 있습니다.
 
모든 테넌트가 하나의 인덱스 사용하기

하나의 인덱스를 사용하여 멀티테넌트 환경을 구성하는 방법은 다음과 같습니다.
 
 - 검색 필터(Filter)
 - 커스텀 라우팅(Custom routing)
 - 커스텀 라우팅과 인덱스 별명(Index alias)
 
아시다시피 검색 기능에서 필터는 지정된 조건으로 결과를 걸러내는 것을 의미합니다. 검색 웹사이트에서 검색 수행 시 이미지, 뉴스, 동영상, 블로그 등을 기준으로 검색 결과를 거르는 기능을 말합니다.
엘라스틱서치에서 데이터를 찾기 위해 질의하는 형태는 쿼리(Query)와 필터(Filter)로 나눌 수 있습니다. 쿼리와 필터를 일반적인 검색 포털 사이트에서 검색하는 행위로 비유해보면 사용자가 키워드로 검색한 것은 쿼리에 해당하고 검색 결과 중 뉴스에 해당하는 내용만 거르는 것을 필터라고 볼 수 있습니다.
 
일반적으로 검색 엔진은 품질 측면에서 이용자가 찾고자 하는 내용과 관련 있는 결과를 도출하기 위해 알고리즘에 따라 검색 대상 데이터에 점수(Score)를 부여합니다. 쿼리가 들어오면 엘라스틱서치는 검색 내용의 일치 여부와 유사도 점수(Relevance score)에 따라 응답하지만 필터는 이 점수에 영향을 받지 않는 특성이 있습니다. 또한 필터는 쿼리와 달리 질의 내용과 정확하게 일치하는 결과만을 찾고 자주 사용되는 필터는 자동으로 캐시(Cache)에 저장되어 처리 속도상 이점이 있습니다.
 
필터는 유사도 점수와 상관없이 정확히 일치하는 대상만 찾을 수 있다는 점에서 멀티테넌트를 지원하기 위한 용도로 사용할 수 있습니다. 테넌트에 부여한 고유의 식별자(ID)를 필터로 하여 구분하는 것입니다. 하지만 필터는 캐시를 사용하기 때문에 비어있는 캐시에 값이 적재(Warm cache)되는 과정에서 단일테넌트 환경과 비교하여 속도 저하가 발생할 수 있습니다. 아울러 테넌트의 수가 지속적으로 늘어나면 요구되는 하드웨어 자원도 증가하는 상황이 발생합니다.

[그림 3] 인덱스와 도큐먼트


멀티테넌트를 지원하기 위한 다른 방법으로 커스텀 라우팅을 사용할 수도 있습니다. 인덱스는 도큐먼트(Document)라는 단위 정보의 집합입니다. 예를 들어 [그림 3]과 같이 고객 정보 인덱스는 여러 고객 정보가 각 도큐먼트에 표현됩니다. 하나의 인덱스를 구성하는 여러 개의 샤드에는 이 도큐먼트들이 나뉘어 저장됩니다. 커스텀 라우팅은 각 도큐먼트가 정의된 규칙에 따라 지정된 샤드에 저장되고 해당 샤드를 대상으로 검색을 수행할 수 있도록 합니다. 그러므로 테넌트마다 각기 다른 라우팅 값을 설정함으로써 각 테넌트에 독립된 검색 결과를 제공할 수 있고 전체 샤드 목록에서 지정된 샤드로 한정 검색하여 성능상 이점이 있습니다.

 
한편, 각 테넌트의 데이터 크기는 상이하기 때문에 각 샤드의 필요한 크기도 모두 달라집니다. 하나의 테넌트가 회사 단위로 구성되어 고객 정보를 엘라스틱서치에 저장하는 경우를 생각해보겠습니다.
G사 서비스 이용자 수가 1억 명이고 N사 서비스 이용자 수는 1천만 명일 때 G사의 데이터가 라우팅 되는 샤드에 더 많은 데이터가 저장되어 한 노드(서버)에만 부하가 집중되는 클러스터의 불균형을 야기할 수 있습니다. 이 때 인덱스 파티션(Index partition)으로 라우팅 하면 샤드의 하위 집합을 구성하여 한 테넌트의 데이터를 여러 개의 샤드에 나누어 분배할 수 있습니다. 샤드의 크기는 성능에 영향을 미치므로 운용 환경에 따라 적정 수준의 크기를 유지하면서 클러스터의 불균형을 방지하려면 인덱스 파티션으로 라우팅하는 방법을 고려해 볼 필요가 있습니다.

shard_num = (hash(_routing) + hash(_id) % routing_partition_size) % num_primary_shards

[식 1] 인덱스 파티션으로 라우팅하는 샤드 배정 공식

[식 1]은 인덱스 파티션으로 라우팅 할 때 샤드를 배정하는 방법을 나타내고 있습니다. hash(_routing)은 각 라우트 구분을 위한 식별자의 해쉬 값이고 hash(_id)는 인덱스 도큐먼트에 부여되는 식별자의 해쉬 값에 해당합니다. 공식에 따르면 각 테넌트의 데이터가 배치될 샤드는 나머지(modulo) 연산에 따라 결정됩니다. 테넌트 별로 다른 샤드에 데이터를 배치하기 위해서는 공식에 변수로 주입되는 식별자의 값을 어떻게 관리해야 할까요? 또한 테넌트의 수가 샤드의 수를 넘어서면 어떻게 해야 할까요? 제한된 샤드의 환경에서 2개 이상의 테넌트가 한 샤드로 라우팅 될 수 있다면 필터를 함께 사용하는 방법이 있습니다. 이미 살펴본 바와 같이 필터는 캐시에 값이 적재되는 과정이 필요하지만 이 경우에는 파티션 크기로 한정된 샤드 내에서 검색을 수행하는 이점이 있습니다.
 
마지막으로 커스텀 라우팅에 인덱스 별명을 사용하면 멀티테넌트 환경에서 보다 유연한 인덱스 관리가 가능합니다. 인덱스 별명이란 하나 또는 그 이상의 인덱스를 참조하기 위해 사용하는 논리적인 이름입니다. 또한 인덱스 별명이 참조하는 대상 인덱스를 다른 인덱스로 변경할 수도 있습니다. 이러한 인덱스 별명의 특성은 다수의 인덱스를 대상으로 검색을 수행하거나 기존 인덱스를 수정하는 경우 또는 새로운 인덱스로 변경하는 작업을 용이하게 합니다.

[그림 4] 인덱스 별명 사용 사례


엘라스틱서치로 검색 등을 요청하는 연계 서비스 측에서는 인덱스 별명을 사용하기 때문에 만일 운용 과정에서 인덱스에 문제가 식별되더라도 [그림 4]와 같이 인덱스 별명이 참조하는 인덱스만 바꿈으로써 연계 서비스 측의 수정과 서비스 중단 시간을 최소화 할 수 있습니다.

[그림 5] 필터링된 인덱스 별명


커스텀 라우팅으로 각 테넌트마다 지정 샤드를 사용하는 환경에서는 필터를 활용하여 인덱스 별명을 사용할 수 있습니다. 필터링 된 인덱스 별명(Filtered aliases)은 라우팅 설정과 함께 인덱스에서 테넌트 식별자로 사용할 필드를 대상으로 필터링하여 연계 서비스 측에서 [그림 5]와 같이 요청 가능합니다.

 
한 테넌트의 데이터 유입이 활발해서 샤드가 권장 크기(20~40GB)를 넘어서거나 전체 테넌트 목록에서 소수의 대규모 테넌트로 인하여 클러스터의 균형을 유지하기 위한 관리에 어려움이 있다면 필터링 된 인덱스 별명을 활용해 해당 테넌트를 별도의 인덱스로 분리하는 것을 검토할 수 있습니다.

[그림 6] 인덱스 별명이 가리키는 인덱스의 변경


[그림 6]은 C Client의 테넌트가 하나의 인덱스에서 관리하기 어려울 정도로 비대해지고 빈번하게 호출되는 'hotness' 문제가 발생했을 때 인덱스 별명이 가리키는 인덱스를 새로운 것으로 변경하는 예를 나타냅니다. 이와 같이 구성된 환경에서는 C Client 테넌트의 부하를 감당하기 위해 새로운 인덱스를 추가하였지만 C Client의 연계 서비스 측은 인덱스 별명을 사용 중이므로 인덱스 관리 및 구성에 상관없이 변경 사항을 최소화 하면서 서비스를 지속 운영할 수 있습니다.


멀티테넌트 환경을 적용하는 방법의 정답은?

엘리스틱서치의 사용을 검토 중이거나 사용하기로 결정했다면 인덱스를 생성할 대상 도메인을 분석해야 합니다. 인덱스의 단위 정보(도큐먼트)를 구성하는 필드 정의와 데이터 양에 따라 샤드의 개수와 크기를 산정하여 운용 환경의 요구에 부합하도록 성능 측면에서 고려할 필요가 있습니다. 장기 관점에서 운용 중 확장 가능성이나 예상되는 변경에 대한 검토도 병행해야 합니다. 엘라스틱서치의 멀티테넌트 환경 구성은 이러한 성능과 운용 측면에서 사전에 참작할 필요가 있습니다.
 
먼저 성능 관점에서 생각해보겠습니다. 검색 쿼리에 대한 최소 응답 시간은 데이터, 쿼리 유형 그리고 샤드의 크기에 따라 달라집니다. 인덱스를 많은 개수의 작은 샤드로 구성하면 각 샤드의 처리 속도가 빨라지는 반면 더 많은 작업을 큐에 넣고 순서대로 처리해야 하기 때문에 지연이 발생할 수 있습니다. 엘라스틱사는 권장하는 최대 샤드 크기를 넘지 않는 선에서 가능한 큰 크기의 샤드들로 운용하는 것이 효율적이라고 밝히고 있습니다. 그러나 최적의 성능은 데이터와 쿼리 형태 등에 의존적인 변수가 있기 때문에 이를 고려한 실제 데이터와 벤치마크 테스트를 통해서 최대 샤드 크기를 결정하는 것이 최고의 방법으로 생각됩니다.
 
멀티테넌트 환경은 예상되는 인덱스의 도큐먼트와 테넌트 수가 많지 않다면 필터만 사용해도 별 지장 없이 서비스를 운용할 수 있습니다. 하지만 테넌트의 수는 요구 사항에 따라 회사 단위로 수백 개가 될 수 있고 각 사용자 단위로 수십만 개를 웃돌 수도 있습니다. 테넌트의 수가 수십만 개에 이르는 경우 한 테넌트의 데이터를 검색하기 위해 인덱스 전체를 찾는 것은 비효율적이므로 커스텀 라우팅을 통해 해당 테넌트의 도큐먼트가 존재하는 샤드만을 한정해서 작업하는 것이 필요합니다. 아울러 테넌트 별로 검색 대상을 구분하는 것은 데이터와 쿼리 형태에 영향을 미칠 수 있으므로 권장 내용과 같이 벤치마크 테스트를 통하여 최적의 샤드 설정을 찾아갈 수 있습니다.
 
운용 측면에서는 향후 변경 대응 과정에서 발생하는 자원이나 시간의 비용, 변경 사항 반영을 위해 불가피하게 발생할 수 있는 서비스 다운타임(Downtime)의 최소화 방안을 생각해 볼 수 있습니다. 인덱스는 한 번 생성하는 것으로 끝나지 않고 사용 현황에 맞게 점진적으로 변화할 수 있습니다. 인덱스의 설정을 변경하거나 엘라스틱서치의 버전 업그레이드 과정에서 많은 시간이 소요될 수 있는 리인덱스(Reindex)를 실행할 수 있고 더 이상 업데이트가 되지 않는 인덱스를 대상으로 인덱스 수축(Index shrink)을 수행하여 더 큰 샤드로 병합할 수도 있습니다. 기존에 단일테넌트 환경으로 구성한 인덱스를 멀티테넌트 환경의 적용으로 마이그레이션 해야 할 경우도 있습니다. 인덱스 라우팅은 인덱스 초기 설정 시에 적용되어야 하므로 멀티테넌트 환경을 위해 인덱스를 다시 만들어야 할 수도 있습니다. 이미 멀티테넌트를 지원하기 위해 인덱스 라우팅을 사용하는 상태에서 극히 일부 테넌트가 전체 부하의 대부분을 차지하는 상황이 발생하면 해당 테넌트는 별도의 인덱스를 사용하도록 분리해야 할 수도 있습니다. 인덱스 별명은 이러한 인덱스의 수정 작업에 따른 연계 서비스의 변경 사항과 운영 시간에 미치는 영향을 최소화하는 수단으로 활용할 수 있습니다.
 
앞서 엘라스틱서치에 멀티테넌트 환경을 적용하기 위한 몇 가지 방법들을 살펴보았습니다. 각 방법의 한계점과 개선안도 알아보았습니다. 인덱스 라우팅에 필터링 된 인덱스 별명을 활용하여 한정된 샤드 내에서 작업을 효율적으로 수행할 수 있도록 설정하고 부하가 몰리는 대상 테넌트는 새로운 인덱스로 분리할 수 있음을 확인하였습니다.
 
단일테넌트로 인덱스 설정 시 멀티테넌트 환경을 감안하여 처음부터 모든 방법을 적용할 필요는 없지만 잠재적으로 멀티테넌트 환경을 구성할 가능성이 있다면 초기 설계 때 충분한 검토가 선행되어야 합니다. 인덱스의 데이터가 많아질수록 마이그레이션 작업에 들어가는 비용이 배로 증가할 수 있으며 궁여지책으로 임시 조치한 사항은 차후에 운영 환경에서 성능 문제를 유발하거나 최악의 경우 시스템이 다운되는 요인으로 작용할 수도 있기 때문입니다. 또한 멀티테넌트 환경을 구현한 후에는 각 테넌트의 부하 정도에 따라 인덱스 분리 등의 작업을 수행할 수 있도록 인덱스와 샤드의 모니터링이 필요함도 유추할 수 있습니다.
 
엘라스틱서치는 그 인기만큼이나 버전 업데이트가 활발하게 이루어지고 있어 한 달에 한 번 꼴로 마이너 버전이 갱신되고 있습니다. 향후 지속적으로 추가될 다양한 기능들 속에서 이번에 살펴본 방법보다 더 개선된 멀티테넌트 적용 방안을 찾아볼 수 있기를 기대합니다.

 

# References
[1] https://www.gartner.com/smarterwithgartner/6-trends-on-the-gartner-hype-cycle-for-the-digital-workplace-2020
[2] https://www.elastic.co/blog/what-is-an-elasticsearch-index
[3] https://www.elastic.co/blog/found-multi-tenancy
[4] https://www.elastic.co/guide/kr/elasticsearch/reference/current/gs-basic-concepts.html
[5] https://discuss.elastic.co/t/multi-tenancy-performance/3732
[6] https://www.elastic.co/guide/en/elasticsearch/reference/current/query-filter-context.html
[7] https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-routing-field.html
[8] https://www.elastic.co/guide/en/elasticsearch/guide/current/shared-index.html
[9] https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-add-alias.html
[10] https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-aliases.html
[11] https://www.elastic.co/blog/how-many-shards-should-i-have-in-my-elasticsearch-cluster

▶  해당 콘텐츠는 저작권법에 의하여 보호받는 저작물로 기고자에게 저작권이 있습니다.
▶  해당 콘텐츠는 사전 동의 없이 2차 가공 및 영리적인 이용을 금하고 있습니다.

박정우
에스코어㈜ 소프트웨어사업부 개발플랫폼그룹

개발플랫폼그룹에서 클라우드 플랫폼 관련 연구 개발을 담당하고 있습니다.