사용자 스토리

1,000% 성능 강화를 위한 Elasticsearch 활용

Voxpopme는 전 세계적으로 앞서가는 비디오 인사이트 플랫폼 중 하나입니다. 2013년에 시작된 회사로서, 비디오가 동시에 수십만 명의 사람들에게 목소리를 제공하는 가장 강력한 방법이라는 간단한 전제를 기반으로 하여 설립되었습니다. Voxpopme만의 특별한 소프트웨어는 소비자가 녹화한 비디오와 긴 형식의 콘텐츠(포커스 그룹 등)를 다양한 설문조사 파트너로부터 수집하여, 그래프, 검색 가능한 주제, 사용자 정의 가능한 쇼 릴의 형태로 유용한 마케팅 데이터를 제공합니다.

창사 이래, 우리는 비디오를 통해 얻을 수 있는 인사이트의 최적화와 자동화 작업에 4년이라는 시간을 쏟았습니다. 브랜드와 고객 간의 실질적인 연결을 막고 있는 장벽을 없애기 위해서입니다. 클라이언트가 보다 나은 경험을 할 수 있도록, 우리는 설문조사 응답자의 감정을 파악하고 집계하기 위해 IBM Watson의 최신 NLP 도구를 사용하고 있으며, 얼굴 표정을 분석하기 위해 Affectiva와 독점 파트너십을 체결했습니다. 2017년에 우리는 클라이언트에게 가능한 최상의 환경을 제공하기 위해 Elasticsearch를 우리의 도구에 포함시켰습니다.

레거시 인프라에서 탈피

Voxpopme의 테크놀로지 스택은 지난 12개월에 걸쳐 상당한 변화를 겪어왔습니다. 2017년에 우리 플랫폼은 50만 건(이전 4년을 합한 것과 같은 양)의 비디오 설문조사를 처리했으며, 2018년에는 그 두 배가 되었습니다. 우리는 확장 문제에 직면하게 되었고, 이런 문제를 겪게 된 것은 좋은 일이었지만, 문제는 문제였습니다.

이 문제는 우리의 레거시 시스템에서 발생했습니다. 이 레거시 시스템은 모놀리식 PHP 애플리케이션으로 구성되었고, 주요 기능을 위해 수많은 다른 데이터베이스와 상호 작용했습니다. 이러한 원래의 데이터 분리 이면의 논리는 처음에는 다음과 같이 타당한 것이었습니다.

  • 우리 데이터의 대다수는 MySQL 데이터베이스에 저장되었습니다. 여기에는 사용자, 개별 비디오 응답과 같은 것 — 외래 키로 서로 연결되며 RESTful API를 이용해 만들고 읽고 업데이트하고 삭제되는 구조화된 관계형 데이터가 포함되었습니다.
  • 클라이언트 데이터는 MongoDB 클러스터에 저장되었고, 이것을 우리는 제공된 형태가 무엇이든 그대로 받았습니다. 따라서 사용자는 자신의 용어를 사용해 자신의 동영상에 태그를 지정하고 주석을 달고 필터링할 수 있었습니다.
  • 우리는 작은 Elasticsearch 클러스터에 응답자들의 비디오 녹취록을 저장했고, 이것을 전체 텍스트 조회에 사용했습니다.

오랫동안, 이 접근 방식은 아주 잘 진행되었지만 분명한 문제가 한 가지 있었습니다.

계산 능력의 비용 및 규모적 한계

우리 플랫폼에서의 검색은 엄청나게 간단할 수도 있고 대단히 복잡할 수도 있습니다. 좀더 간단한 인스턴스에서는 사용자들이 비디오 응답의 기본 키를 이용해 검색하도록 할 수 있습니다. 이것은 쉽고, 색인된 MySQL 데이터베이스로의 간단한 쿼리만 포함됩니다.

그러나 사용자가 색인된 정수, 자신의 자유형 데이터 중 일부를 이용해 필터링을 하고, 아울러 특정 주제를 언급하는 모든 응답자로 결과 범위를 좁히고 싶어할 수도 있는 쿼리는 어떨까요? 예를 들어,

응답자의 가구 소득이 10만 달러~12만 5천 달러 범위에 있고 응답자가 ‘너무 비싸다’는 언급을 했으며 응답 날짜가 6월 1일~6월 30일 사이인 모든 레코드를 찾아보세요.

우리의 접근 방식은 다음과 같았습니다.

voxpopme-legacy-process.png

레거시 시스템 하에서는 다음과 같은 상황이 발생했을 것입니다.

  • MySQL 쿼리가 해당 날짜 범위 내의 모든 레코드를 찾습니다.
  • MongoDB 쿼리가 해당 소득 범위 내의 모든 레코드를 찾습니다. (소득은 단지 한 예일 뿐이며, 클라이언트는 완전히 임의적인 정보를 저장할 때가 많습니다.)
  • Elasticsearch 쿼리가 녹취록에서 일치하는 구절이 있는 모든 레코드를 찾습니다.
  • 세 개의 데이터 세트에서 ID의 교집합이 계산됩니다.
  • 각 레코드에 대한 전체 데이터 세트를 얻기 위해 새로운 세트의 MySQL과 MongoDB 쿼리가 실행됩니다.
  • 레코드가 정렬되고 페이지가 매겨집니다.

간단한 검색 기준 세트로 다섯 개의 별개의 데이터베이스 쿼리를 쉽게 포함시킬 수 있습니다. 원래 이것은 1초 미만이 소요되는 작업이었을 것이지만, 5년치에 해당하는 데이터의 양과 PHP가 단일 스레드라는 사실은 데이터베이스 쿼리가 하나씩 하나씩 실행되어야 하며, 최대 30초까지도 쉽게 소요될 수 있으리라는 뜻이었습니다.

이 모델 덕분에 우리는 신속하게 제품을 출시할 수 있었고 여러 해 동안 이 모델을 잘 사용했지만 원활하게 확장되는 모델은 아니었습니다. 우리 사용자들의 환경이 저하되기 시작하는 것을 알게 되자 우리는 검색 메커니즘을 다시 써야 할 때라는 결정을 내렸습니다.

우리는 이미 기존 스택에서 Elasticsearch를 어느 정도 경험한 적이 있었고, 따라서 Elastic 영업 관리자와 얘기를 나누며 여러 장소에 있는 다양한 데이터를 대상으로 복잡한 검색을 수행하는 동안 우리가 직면하게 된 문제들에 대해 논의했습니다. 적합한 솔루션을 찾는 것이 시급했습니다. 우리 제품은 우리 데이터에서 신속하고 효율적으로 계산을 표시하고 수행하는 것을 중심으로 하며, 데이터를 검색하는 데 있어 병목 현상이 생기는 것은 용납되지 않습니다.

기존 MongoDB 클러스터 확장도 고려해 보았지만, Elastic과 간단한 전화 상담을 하고 난 후, 개발 팀은 우리가 데이터(즉, 집계)를 쉽게 저장, 검색, 조작할 수 있도록 해주는 유일한 솔루션은 Elasticsearch라는 결정을 내렸습니다.

첫 사용 소감

우리가 텍스트 검색을 위해 사용했던 Elasticsearch 클러스터는 Compose.io의 버전 1.5 클러스터였습니다. 인프라의 나머지 부분에서 AWS에 상당한 투자를 했기 때문에, 처음에는 5.x 버전 클러스터를 실행하는 Amazon Elasticsearch Service를 선택했습니다.

우리의 새 모델은 클라이언트의 좀더 까다로운 자유형 데이터에 대해 알려진 키를 갖는 중첩된 값을 사용해서, 하나의 레코드의 서로 다른 데이터를 모두 단일한 Elasticsearch 문서 내에 유지하는 것과 관련되어 있었습니다. 그러면 모든 사용자 검색이 단일한 Elasticsearch 쿼리에 의해 처리될 수 있을 것입니다.

일주일 이내에 우리는 몇천 개의 문서를 로드하여 기본 개념 증명을 했고, Kibana에서 쿼리를 실행한 첫 소감은 백 엔드 검색 메커니즘을 완전히 다시 쓰는 것을 계속 밀고 나갈 수 있을 정도로 충분히 좋다는 것이었습니다.

이것이 우리의 새로운 접근 방식이었습니다.

voxpopme-new-process.png

(지금은 레거시 데이터베이스가 변경되지 않은 상태이지만, 복잡한 검색을 위한 새로운 Elasticsearch 클러스터의 도입으로, 우리 스택에서 MongoDB를 완전히 제거할 계획을 세울 수 있습니다.)

새로운 스택을 이용해 우리는 전처럼 MySQL과 MongoDB에 계속해서 써나갔습니다. 그러나, 각각의 쓰기는 또한 다양한 소스의 모든 우리 데이터를 Elasticsearch로 삽입되는 단일한 JSON 문서로 수집하여 병합시키는 이벤트를 트리거하게 됩니다.

백 엔드에서 우리는 사용자들의 기존 검색 요청에서 복잡한 Elasticsearch 쿼리를 생성한 새로운 검색 메커니즘을 썼고, MySQL과 MongoDB를 쿼리한 레거시 코드를 완전히 제거했습니다. 단일한 Elasticsearch 클러스터를 쿼리함으로써 우리는 전에 비해 몇 분의 일 밖에 안되는 시간에 동일한 데이터를 얻을 수 있었습니다.

우리의 Elasticsearch 문서 구조에서도 추가적인 이점이 생겼습니다. 우리의 API는 JSON 문서를 반환하며 따라서 우리는 Elasticsearch 문서를 구조화하여 기존 API 출력에 정확히 일치시킬 수 있었습니다. 이로써 전에 MySQL과 MongoDB에서 나온 데이터를 결합하여 재구성하는 데 들었던 시간을 추가로 절약하게 되었습니다.

비즈니스 내의 다양한 관련자에게 우리 플랫폼의 주요 부분들에 대해 앞으로 1,000%의 성능 향상이 예상된다고 보고할 수 있었습니다.

AWS에서 Elastic Cloud로 전환

모두에게 1,000%의 성능 향상을 약속한 후, 다음 단계는 모든 데이터를 색인하고 압력 상태에서도 이 숫자가 보존되기를 바라는 것이었습니다.

AWS 클러스터를 색인한 우리의 모든 데이터는 불행히도 잘 대처해내지 못했습니다.

데이터를 그저 한 번 색인하고 나서 그것을 여러 번 쿼리하는 경우라면, 문제가 없었을 수도 있습니다. 그러나 우리 모델은 MySQL이나 MongoDB에서 업데이트될 때마다 데이터를 결합하고 다시 색인하는 데 전적으로 달려 있었습니다. 이것은 어쩌면 검색 대상이 되는 기간 동안 수천 번이 될 수도 있었습니다.

우리는 성능이 타격을 입고 쿼리 시간이 때로 몇 초 대로 떨어지는 것을 발견했습니다. 이것은 우리 PHP 애플리케이션을 잠재적으로 잠그게 되는 부작용을 야기했고, 그럴 경우 이제 MySQL 연결이 계속 열려 있는 상태로 있게 될 수 있으며, 끔찍한 도미노 효과로 우리의 전체 스택이 응답을 하지 않게 될 수도 있었습니다.

우리가 이런 문제를 겪고 있던 무렵 Elastic{ON}이 런던을 방문했고, 우리는 AMA 부스에서 클러스터 크기 및 우리가 겪고 있는 문제에 대해 Elastic 담당자와 얘기를 나눌 기회를 갖게 되었습니다. 우리는 가장 좋은 경우의 쿼리 시간이 40ms 대였다고 언급했고 AWS 대신 Elastic의 자체 Elastic Cloud 서비스를 사용하면 기본 설정으로 응답 시간이 1ms에 가까울 수 있을 것이라는 조언을 들었습니다.

Elastic Cloud의 Elastic Stack 확장 기능(이전 X-Pack 패키지)제공은 우리에게는 엄청난 혜택이었습니다. 그 당시 제한된 로깅 및 그래프 시스템만 가동하고 있었기 때문입니다. X-Pack은 AWS의 Elasticsearch 제품에서는 누락되어 있었으므로, 우리는 나란히 비교해 보는 테스트를 일부 진행하며 Elastic Cloud 클러스터를 사용해 보기로 결정했습니다. 최소한 AWS 클러스터 정도라도 성능을 내주면 다른 혜택만으로도 기꺼이 바꾸려고 했던 것입니다.

Elastic Cloud UI는 대단히 깔끔하고 사용하기 쉬웠습니다. 색인 작업을 진행하면서 데이터를 처리할 인력을 많이 추가했기 때문에 우리는 규모를 확장해야 했고, 클러스터 관리가 얼마나 쉬운지를 보고 깊은 인상을 받았습니다. 슬라이더를 왼쪽이나 오른쪽으로 이동하고 ‘업데이트’를 클릭하면 되니까요.

일단 데이터가 새 클러스터에서 성공적으로 색인되면, 우리는 잠금이 거의 또는 전혀 없이 복잡한 쿼리를 2ms라는 빠른 시간 안에 수행할 수 있었습니다. (그 이래로 우리는 잠금을 아예 모두 제거하기 위해 최적화를 진행했습니다.) 여기저기서 몇 밀리초는 보통 최종 사용자가 인식할 수 있는 정도는 아니지만, 우리의 내부 기술 전문가들은 대기 시간이 이전 수준의 5%로 줄어든 것을 보고 기뻐했습니다.

데이터의 활용 최대화

그러나 우리는 구식이 되고 싶지 않았고, Elasticsearch를 사용자 대면 검색 엔진으로만 사용하고 싶지 않았습니다. 테크놀로지에 대해 들어왔던 수많은 성공담이 로깅에 중점을 두고 있는 것 같고, 우리도 예외가 아니라는 것을 금방 알게 되었습니다.

우리는 Kubernetes 포드의 로그를 수집하기 위해 별도의 로깅 클러스터를 설정했고, 이전에는 볼 수 없었던 우리 서버의 상태를 이제 한결 분명하게 보고 훨씬 신속하게 문제에 대처할 수 있게 되었습니다.

우리 플랫폼의 사용자에게도 이와 비슷한 것을 제공할 수 있게 되었습니다. 집계를 사용하여 우리 사용자에게 자신의 데이터를 시각화할 수 있는 그래픽 방법을 제공할 수 있었습니다. 이것은 그 자체로 우리 플랫폼에 큰 추가 사항이었고, 순전히 Elasticsearch에 우리 데이터를 저장하는 데 대한 부대 효과로 이루어진 것입니다.

지난 몇 달에 걸쳐 우리는 프로세스를 조정하고 세심하게 다듬었고, 매주 Elasticsearch 클러스터의 성능이 훨씬 더 향상되는 것을 보고 있습니다. 이러한 성능 강화에는 fielddata를 요구하는 필드를 기본 클러스터에서 덜 빈번하게 액세스되는 보다 작은 클러스터로 이동함으로써 얻게 된 상당한 메모리상의 이점도 포함되었습니다. (우리의 기준 메모리 압력은 이제 75%에서 25%로 줄었습니다.) 또한 우리는 요청 기반이 아닌 대량으로 새 데이터를 쓰도록 우리 코드를 최적화했습니다. 따라서 클러스터가 최고 이용시간대 중에 훨씬 더 신속하게 반응할 수 있게 되었습니다.

앞으로, 우리는 회사로서 우리가 보유하고 있는 내부 데이터를 분석하기 위해 Elasticsearch를 많이 활용할 계획입니다. 클라이언트 대면 제품의 핵심 부분으로 스스로를 입증했으며, 우리는 이미 Elasticsearch 인덱스에 자체 데이터를 저장하는 실험을 시작했고, 회사로서 우리의 운영 효율성에 대한 인사이트를 얻기 위해 Elastic Stack을 사용하려고 합니다.



voxpome-david.jpegDavid Maidment는 Voxpopme의 수석 소프트웨어 엔지니어로서, 회사가 기하급수적인 성장을 겪게 됨에 따라 플랫폼의 코드베이스에 대한 미래 검증을 전문적으로 다루고 있습니다.



voxpopme-andy.jpgAndy Barraclough는 Voxpopme의 공동 창립자이자 CTO로서, 기술 팀 관리와 회사의 장기 비전 조정을 중점적으로 담당하고 있습니다.