brunch

You can make anything
by writing

C.S.Lewis

by 손주식 Jul 28. 2019

Tinder의 지리 기반 추천 Part 2

Tech Review of Tinder #2

지난 리뷰

https://brunch.co.kr/@andrewhwan/58


원문 

제목 : Geosharded Recommendations Part 2: Architecture

글쓴이: Frank Ren|Director, Backend Engineering, Xiaohu Li|Manager, Backend Engineering, Devin Thomson| Lead, Backend Engineer, Daniel Geng|Backend Engineer

https://medium.com/tinder-engineering/geosharded-recommendations-part-2-architecture-3396a8a7efb


이 글은 위 원문을 번역한 글입니다. 이해가 쉽도록 일부 의역한 부분도 존재합니다. 


지난 포스트에서 우리는 geoshard 클러스터의 이론적 기반이 되는 sharding mechanism을 소개하였다. 이번 Part 2에서는, 우리가 사업의 요구사항을 만족시키기 위하여 어떻게 고성능의 확장성 있고 균형 잡힌 인프라스트럭쳐를 만들었는지 설명하고, 그 과정에서 우리가 극복했던 재미있는 기술적인 난관들과 고려사항들을 소개할 것이다. 


큰 그림 소개 (High-Level Architecture)

우리가 sharding 알고리즘을 완성하고 하나의 내부적인 microservice로서 추상 계층을 구현하였을 때, 대략적인 클러스터의 큰 그림은 아래와 같았다.

사용자의 위치가 바뀌면, shard를 옮겨야 할지 말지 판단하기 위해 사용자의 프로필(User Profile)을 거쳐서 location service로 들어갔고, 그다음에 구체적인 경로 정보와 함께 shard 이동이 수행되었다. 


사용자가 추천 정보를 받을 때는, 추천 엔진(recommendation engine)에서 사용자의 현재 위치와 거리 필터 기반으로 몇 개의 shard에 질의를 날려야 할지 판단하기 위해 이 논리적인 계층(Location Microservice)을 사용했고, 그다음에 그 정보를 토대로 질의를 처리하였다. 이렇게 각 shard에서 수집되고 통합된 정보는 다시 사용자에게 보내졌다. 


이 과정에서 우리는 다음과 같은 질문을 하게 되었다.

이런 우리의 색인 구조는 어떻게 구현하는 것이 최선일까?

geosharding 클러스터를 위한 최적의 설정값은 어떻게 결정할 수 있을까?

부하는 어떻게 분산해야 할까?


다중 색인 vs 다중 클러스터

지금까지 우리가 사용한 "shard"라는 단어는 단지 사용자의 위치 정보를 기반으로 색인을 분리한 것을 표현하는 논리적인 용어였다. 아직 이것의 기술적인 구현이 어떻게 될지는 결정하지 않았다. Elasticsearch의 세상에서는, 각 shard에는 하나의 색인만 들어있다. 하지만 우리는 모든 색인을 하나의 클러스터에 집어넣거나(다중 색인), 각각 하나의 shard를 표현하는 여러 개의 클러스터(다중 클러스터)를 만들 수 있다. 


아래 내용은 각 방식의 장단점이다.


다중 색인:

장점: 별도의 복잡한 설정 없이 Elasticsearch 기본 기능만으로 구현이 가능하고 운영 관점에서도 유지보수가 더 쉽다.

단점: shard 하나에 부하가 쏠리면 scale up/down 하기가 어렵고 다른 shard에도 영향을 줄 수 있다.


다중 클러스터:

장점: 분리 수준이 낮아지고 클러스터 단위로 scale up/down 하기가 수월하다.

단점: 다중 클러스터에 대한 질의 기능이 아직 없었다. shard 이동 상황에서는 명시적으로 똑같은 질의를 병렬로 날려야 했다.


우리는 조심스럽게 '다중 색인' 방식을 선택했는데, 서버 로직을 엄청나게 단순화시키면서 운영 비용도 줄일 수 있기 때문이었다. 다만, 우리는 이 방식의 단점을 극복하기 위해 새로운 부하 분산 전략을 개발하기 시작했다. 그리고 우리는 replica만 추가하면 언제나 shard 하나를 scale up 할 수 있게 하였다.


적절한 크기 찾기

다음 기술적 난관은 클러스터의 적절한 크기를 결정하는 것이었다. 우리가 튜닝해야 하는 수많은 변수들이 있을 때 특히 더 문제였다. 다음과 같은 질문들에 답해야 했다.


각 shard의 크기는 얼마가 최선인가?

우리는 총 몇 개의 host가 필요한가?

각 host는 어느 정도의 computation power가 필요한가?

각 shard는 몇 개의 replica가 필요한가?

우리의 환경을 위한 최적의 Elasticsearch/jvm 설정값은 무엇인가?


위 질문들에 답하기 위해서는, 대대적인 성능 테스트가 필요했다. 우리는 JMeter를 사용하여 성능 테스트 프레임워크를 만들었다. 성능 테스트 프레임워크는 coordinating node, data node, 그리고 JMeter remote server node로 구성되었는데, JMeter remote server node는 coordinating node로 보낼 요청을 발생시키는 역할을 하였고, coordinating node에서 이 트래픽을 data node로 보냈다가 결과를 받아 다시 remote server node로 보내주었다. 


대부분의 경우, 우리가 설정한 변수들은 따로 놀지 않았다. 예를 들어, 인스턴스 종류, shard 크기, replica 개수들은 서로에게 많은 영향을 주는 편이다. 만약 shard 크기가 커지면 같은 종류의 인스턴스에 더 적은 수의 shard를 넣어야 하고, 결국 host 수가 같다면 replica 수도 적게 가져갈 수밖에 없게 된다. 


가능한 모든 경우의 수를 무조건 다 테스트하는 것이 아니라 shard 크기에서 시작해서 사용자와 geological 영역 개수에 따라 필요한 설정들을 결정해보자.


일단 감을 잡기 위해, 우리는 사용자 위치와 지도 정보를 집계하여 Tinder 인구가 가장 밀집된 지역을 찾았다. 일단 여기서 우리는 잠정적으로 그 지역을 하나의 shard에 넣어서 밀집 구역의 사용자들이 cross shard 질의가 발생할 확률을 낮추었다.  


그다음 우리는 shard 당 사용자 수 정도의 사용자 문서를 data node로 보내 테스트 데이터를 준비하였다. 우리는 또한 사용자 문서의 위치를 기반으로 기본 shard area 크기도 선택하여 정보가 사각형 영역 안에서 무작위이지만 골고루 분산되도록 하였다. 


게다가 실제 사용자의 나이 분포와 나이 필터를 활용하고, 무작위성을 추가하여 트래픽 패턴과 각 질의의 수를 가능한 한 정확하게 시뮬레이션하기 위해 노력하였다.


작은 규모의 트래픽부터 시작하여 각 질의 node 혹은 data node의 CPU나 Memory가 한계에 도달할 때까지 올려나가는 방식으로, shard 크기, replica 개수, 인스턴스 종류, jvm 설정 등을 바꿔가면서 Production 환경에서 사용할 최적의 설정을 찾아나갔다. 


Time Zone 처리하기


같은 geoshard에 속한 사용자들은 지리적으로 인접한 위치에 있기 때문에 대부분 같거나 유사한 Time Zone을 사용한다. Time zone에 따라서 peak 시간대와 off-peak 시간대가 달라지고 또 지역에 따라 서로 다른 사용 패턴이 나타날 수 있기 때문에, shard 마다 트래픽의 균형이 맞지 않을 확률이 존재한다.


서로 다른 두 지역의 24시간 트래픽 패턴을 가상으로 나타낸 그래프

위 그림에서 볼 수 있다시피, peak 트래픽은 거의 비슷하지만, 서로 다른 시간대에 발생한다. 만약 하나의 shard를 하나의 물리 box에 할당했다면, node마다 서로 다른 부하를 처리하게 될 것이다. 초당 요청 수 기준으로 생각해보면 shard 간의 부하 차이가 최대 10배 이상 날 수도 있게 된다.


우리의 목표는 shard 당 부하를 가능하면 균형 잡히게 분산시키는 클러스터를 구성하는 것인데, 그래야 사용자들이 어느 지역에서든, 얼마나 많은 geoshard에 요청하든, 항상 유사한 응답 시간을 경험할 수 있기 때문이다.


이 목표를 달성하기 위한 우리의 해결책은 각 node의 부하가 하루 중 어느 시간대에도 유사해지도록 수동으로 여러 개의 shard를 하나의 node에 할당하는 것이었다. 우리는 이 방법으로 부하 균형을 가장 잘 맞출 수 있다는 것은 알고 있었지만, 너무 많은 수작업이 필요했고 사실상 NP hard 문제를 푸는 일이었다.


게다가, 사용자 규모가 커져서 reshard가 필요해졌을 때마다 다시 이 문제를 계산해야만 했다. 이것이 바로 우리가 결국에는 '무작위성'의 힘에 기대게 된 결정적인 이유였다.


우리가 한 일

replica 개수에 따라, replica와 shard 당 최대 트래픽을 X rps로 가정한다.

모든 shard를 5개의 트래픽 그룹으로 분류한다. 예를 들어, 0~0.2X rps의 트래픽을 받는 모든 shard는 Group 1, 0.2X ~ 0.4X rps는 Group 2로 분류한다.

각 최대 트래픽 그룹에서 최대 부하를 갖는 host가 하나 이상의 shard를 담당할 확률을 계산한다. (이 경우 아마 0.8X ~ 1.0X 부하의 Group이 될 것이다.)


이 모델을 사용함으로써, 우리는 NP hard 문제를 전형적인 통계 문제로 단순화하였다. 즉, 5 종류의 색깔을 가진 N개의 공을 (N = shard 수 * shard 당 replica 수) M개의 서랍(M = 물리 host 수)에 넣을 때 어느 서랍에 같은 색깔의 공이 두 개 들어갈 확률은 무엇인지 구하는 문제이다.


우리는 확률을 계산하고 shard 당 replica 개수를 조절하여 N 값을 바꿔가면서 테스트하였다. 최종적으로, 우리는 최적의 replica 수를 찾을 수 있었다. 당연하게도 여기에는 trade-off가 있다. replica 수가 많아질수록, 부하 균형이 더 잘 맞아지겠지만, write 용량과 memory는 host 쏠림 현상으로 고통받을 것이다. 


최종 단계

이런 모든 방법들과 테스트를 거친 뒤, 드디어 아래와 같이 우리의 geosharded 클러스터 구조가 완성되었다.

세 개의 master node 아래에, 우리는 coordinating node로만 구성된 ASG를 두 개, 그리고 모든 data node를 포함한 ASG를 하나 가지고 있다. 모든 request는 coordinating node로 보내진다. 각 data node는 무작위로 분산된 4개의 색인들을 가지고 있는데, 이 중에 하나는 반드시 특정 shard로 지정되고, 나머지들은 다른 어느 shard의 replica이다. 각 질의가 몇 개의 geoshard에 요청하느냐에 따라서, coordinating node가 해당 요청을 target geoshard를 가지고 있는 data node로 분배한다. 


이 방법으로 우리는 모든 geosharded 색인을 하나의 클러스터에서 처리하고 부하 균형도 맞출 수 있게 되었다. 


교훈

현재까지 우리는 대용량의 균형 잡힌 색인 클러스터를 잘 유지하고 있다. Production에서의 우리의 측정에 따르면, geosharded 클러스터는 이전 클러스터에 비해서 20배 더 좋은 성능을 보여준다.


핵심 요약: 

새로운 인프라스트럭쳐를 처음 도입할 때 성능 테스트는 매우 중요한 요소이다.

공학 환경에서 어려운 문제를 풀 때 무작위성을 활용하는 것을 고려해보라.


브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari