개발자 소식으로 돌아가기

모범 사례: Presto를 Meta 규모로 실행하기

2023년 4월 11일제작:Neerad Somanchi 및 Philip Bell

Presto는 무료 오픈 소스 SQL 쿼리 엔진입니다. 저희는 지난 10년 동안 이 엔진을 Meta에서 사용하였고 그 과정에서 많은 것을 배웠습니다. 도구, 프로세스, 서비스 중 무엇이든 대규모로 실행하려면 예상치 못하게 발생하는 문제를 극복하는 문제 해결 능력이 필요합니다. Presto를 Meta 규모로 확장하는 동안 알게 된 4가지 교훈과 대규모로 쿼리를 실행하고 싶은 분에게 도움이 될 만한 몇 가지 조언을 소개합니다!

늘어나는 수요에 맞춰 Presto를 빠르게 확장하면서 저희는 어떤 문제에 부딪혔을까요?

새로운 Presto 릴리스 배포

그림 1: 새로운 버전의 Presto를 푸시하기 위한 프로세스 워크플로(그림: Philip S. Bell)

Meta는 전 세계 곳곳에 위치한 데이터 센터에서 수많은 Presto 클러스터를 실행합니다. 적어도 한 달에 한 번, 때로는 두 번씩 새로운 버전의 Presto가 빌드되고 배포할 준비가 끝납니다. Meta에서 Presto 사용량이 급격히 늘어나면서 가장 먼저 부딪혔던 문제는 지속적인 가용성과 신뢰성을 보장하면서도 이 쿼리 엔진을 대량의 클러스터에 배포하는 것이었습니다. 이 문제는 사용자가 쿼리를 시작하고 결과를 적극적으로 기다리는 Presto의 인터랙티브 사용 사례에서 더욱 두드러졌습니다. 자동 재시도를 통해 쿼리를 결과적으로 성공시키는 자동화된 '일괄' 사용 사례에서 쿼리 실패는 비교적 큰 문제가 아닙니다.

이 경우에는 해결 방법이 간단합니다. 모든 Presto 클러스터는 (Meta의 다른 시스템과 함께) Presto 쿼리를 적절한 클러스터로 보내는 작업을 담당하는 Gateway라는 로드 밸런서에 의존합니다. Presto 클러스터를 업데이트해야 하는 경우, 먼저 Gateway에서 빠져나온 것으로 표시됩니다. 즉, Gateway가 새로운 쿼리를 해당 클러스터로 보내는 것을 중단합니다. 그러면 자동화를 통해 현재 클러스터에서 실행 중인 쿼리가 완료되기를 일정 시간 기다립니다. 그러면 이 클러스터가 업데이트되고, 온라인 상태가 되면 Gateway에 표시됩니다. 이제 Gateway가 이 클러스터로 새 쿼리를 보내기 시작할 수 있습니다.

새로운 Presto 릴리스를 배포할 때의 또 다른 고려 사항은 가용성입니다. 클러스터가 업데이트되는 동안에도 사용자가 Presto를 사용할 수 있도록 보장해야 합니다. 이번에도 자동화를 통해 모든 물리적 지역에 있는 각 데이터 센터가 항상 필요한 수만큼 Presto 클러스터를 제공하도록 합니다. 물론, 한 번에 지나치게 많은 클러스터를 중단하거나(가용성 문제) 한 번에 너무 적은 클러스터를 중단시키지 않도록(배포에 너무 많은 시간 소요) 균형을 잘 맞춰야 합니다.

Presto 클러스터의 설정 및 폐기의 자동화

그림 2: 하드웨어를 클러스터에 추가하기 위한 자동화된 워크플로(그림: Philip S. Bell)

각 지역에서 Meta의 데이터 웨어하우스 배포는 끊임없이 발전하고 있습니다. 즉, 기존 Presto 클러스터를 정기적으로 폐기하면서도 새로운 Presto 클러스터를 설정해야 합니다. 이전에 Presto 클러스터의 숫자가 적었을 때는 이를 수작업으로 진행했습니다. 하지만 Meta의 규모가 커지면서 모든 변경 사항을 수동으로 추적하기가 어려워졌습니다. 이 문제를 해결하기 위해 클러스터의 설정과 해제를 처리하기 위한 자동화를 구현했습니다.

먼저 클러스터 구성을 표준화하여 Meta의 다양한 Presto 사용 사례에 대한 기본 구성을 빌드해야 했습니다. 그러면 각 클러스터는 기본 구성 위에 최소한으로 추가되거나 재정의된 사양을 갖게 됩니다. 이 작업이 끝나면 모든 새 클러스터는 기본 템플릿에서 구성을 자동으로 생성하여 활성화됩니다. 클러스터를 활성화할 때는 자동화 후크도 통합해야 Meta 전체의 다양한 인프라 서비스(예: Tupperware)와 데이터 웨어하우스 전용 서비스를 통합할 수 있습니다. 클러스터가 온라인 상태가 되면 몇 개의 테스트 쿼리를 클러스터로 보내고 자동화를 통해 클러스터에서 해당 쿼리를 성공적으로 실행했는지 확인합니다. 그러면 클러스터가 Gateway에 등록되고 쿼리를 서비스하기 시작합니다.

클러스터를 해제하는 작업은 대체로 이 과정을 역으로 진행하는 것과 같습니다. 클러스터가 Gateway에서 해제되고 실행 중인 쿼리를 모두 완료합니다. Presto 프로세스가 종료되고 클러스터 구성이 삭제됩니다.

이 자동화는 데이터 웨어하우스의 하드웨어 설정 및 해제 워크플로에 통합됩니다. 최종적으로는 새로운 하드웨어가 데이터 센터에 표시되고 Presto 클러스터가 온라인 상태가 되어 쿼리를 서비스하고, 하드웨어가 해제되었을 때 종료되기에 이르기까지의 모든 과정이 완전히 자동화됩니다. 이 자동화를 구현한 덕분에 인력이 들이는 귀중한 시간을 아꼈고 하드웨어 유휴 시간이 줄어들었으며 인간의 오류가 최소화되었습니다.

디버깅 및 복구 자동화

그림 3: 불량 호스트 탐지(그림: Philip S. Bell)

Meta에서는 Presto를 대규모로 배포하기 때문에 당직자(Presto 담당자)의 삶을 편하게 해줄 도구와 자동화가 꼭 필요합니다.

저희는 수년에 걸쳐 당직자가 그날 발생하는 이슈의 근본 원인을 효율적으로 디버깅하고 평가하는 데 도움이 되는 여러 가지 '분석기'를 빌드했습니다. 고객에 대한 SLA 위반이 발생할 경우 모니터링 시스템에서 알림을 보냅니다. 그러면 분석기가 트리거됩니다. 분석기는 여러 모니터링 시스템(Operational Data Store 또는 ODS), Scuba에 게시된 이벤트, 호스트 수준 로그에서 정보를 가져옵니다. 분석기의 맞춤 로직이 모든 정보를 연결해서 유력한 근본 원인을 추론합니다. 이는 근본 원인 분석을 제시하여 바로 잠재적인 이슈 해결 옵션을 살펴볼 수 있어 당직자에게는 매우 큰 도움이 됩니다. 어떤 경우에는 디버깅과 복구를 모두 완전히 자동화하여 당직자가 관여할 필요가 없도록 했습니다. 아래에 몇 가지 예시를 설명하였습니다.

불량 호스트 탐지

대량의 시스템에서 대규모로 Presto를 실행하는 과정에서 특정 '불량' 호스트가 과도한 쿼리 실패를 일으키는 것을 발견하였습니다. 조사를 통해서 다음과 같이 호스트를 '불량'으로 변하게 하는 몇 가지 근본 원인을 알아냈습니다.

  • 커버리지 부족으로 인해 플릿 전체 모니터링 시스템에서 아직 발견하지 못한 하드웨어 수준 문제
  • 지속적으로 쿼리가 실패하게 되는 문제를 일으키는 난해한 JVM 버그

이 문제를 해결하기 위해 Presto 클러스터의 쿼리 실패를 모니터링하게 되었습니다. 특히, 쿼리가 실패할 때마다 원인이 되는 호스트를 찾아냅니다(가능한 경우). 또한 특정 호스트에서 비정상적으로 많은 쿼리 실패가 발생할 시 알림을 보내도록 설정했습니다. 그러면 자동화가 실행되어 해당 호스트를 Presto 플릿에서 제외하고 실패를 방지합니다.

대기열 관련 이슈 디버깅

각 Presto 클러스터는 사용 사례, 하드웨어 구성, 쿼리 크기에 따라 실행 중인 쿼리의 동시성이 최대치에 도달하면 쿼리를 대기시키는 기능을 지원합니다. Meta에는 리소스를 최적으로 활용하면서 쿼리를 실행할 수 있는 '적절한' 클러스터로 Presto 쿼리를 보내는 정교한 라우팅 메커니즘이 있습니다. Presto 외에도 여러 시스템이 라우팅 결정을 내리는 데 참여하고 이들은 다음과 같은 여러 가지 요소를 고려합니다.

  • Presto 클러스터에서 현재의 대기 상태
  • 다양한 데이터 센터에 걸친 하드웨어 분포
  • 쿼리가 사용하는 테이블의 데이터 지역성

이런 복잡성 때문에 당직자가 프로덕션 과정에서 발생하는 대기열 문제의 근본 원인을 찾아내기가 매우 어려울 수 있습니다. 이 경우에도 분석기가 여러 소스에서 정보를 가져와 결론을 제시하는 데 활용됩니다.

로드 밸런서 안정성

그림 4: 로드 밸런서 안정성(그림: Philip S. Bell)

앞서 언급하였듯이 Meta의 Presto 클러스터는 Meta의 모든 Presto 쿼리 경로를 지정하는 로드 밸런서를 활용합니다. 처음에 Presto가 아직 지금과 같은 수준으로 확장되지 않았을 무렵에는 Gateway가 매우 단순했습니다. 그러나 Meta 전체에서 Presto 사용량이 늘어나면서 때로 확장성 문제에 부딪혔습니다. 그중 하나는 부하가 높을 때 Gateway가 실패하는 것인데, 이 문제로 인해 모든 사용자가 Presto를 사용하지 못하게 될 수 있습니다. 어떤 안정성 문제는 어떤 서비스가 의도치 않게 짧은 시간에 수백만 개의 쿼리를 Gateway에 쏟아부어서 Gateway 프로세스가 중단되고 아무 쿼리도 보내지 못하게 된 것이 원인인 경우도 있었습니다.

이런 상황이 발생하지 않도록 예방하기 위해 Gateway를 더욱 안정화하고 이런 의도치 않은 DDoS 스타일 트래픽을 잘 견뎌낼 수 있도록 하는 작업에 착수했습니다. 저희는 부하가 클 때 쿼리를 거부하는 사용 제한 기능을 구현했습니다. 이 사용 제한은 사용자, 소스, IP 등과 같이 다양한 기준에 따라 전체적으로 모든 쿼리에 대해 초당 쿼리 수를 기준으로 활성화할 수 있습니다. 또 다른 개선 사항으로는 자동 확장이 있습니다. Meta 전반에서 작업의 규모를 조정하도록 지원하는 서비스를 통해 이제 Gateway 인스턴스의 개수가 동적으로 조정됩니다. 즉, 부하가 클 때는 Gateway가 CPU/메모리의 한도 내에서 추가적인 트래픽을 처리하도록 확장되기 때문에 앞서 설명한 중단 사태가 발생하지 않습니다. 사용 제한 기능과 자동 확장 덕분에 Gateway가 안정화되었고 부정적이고 예측 불가능한 트래픽 패턴에도 견딜 수 있게 되었습니다.

Presto를 사용하여 데이터 레이크하우스를 확장하는 팀에게 드리는 조언

그림 5: Presto 아키텍처 확장(그림: Philip S. Bell)

Presto의 규모를 확장할 때 염두에 두어야 할 중요한 점은 다음과 같습니다.

  1. 이해하기 쉽고 명확히 정의된 고객 SLA를 설정합니다. 고객의 불만을 추적할 수 있도록 대기 시간, 쿼리 실패율 등의 중요한 지표를 중심으로 SLA를 정의하는 것은 Presto를 확장할 때 매우 중요한 사안입니다. 사용자 수가 많을 경우 적절한 SLA가 없으면 사고의 영향을 파악하는 데 혼란이 생겨 프로덕션 이슈를 완화하는 데 큰 장애가 됩니다.
  2. 모니터링과 자동화된 디버깅을 수행합니다. Presto가 확장되고 클러스터 수가 늘어나면 모니터링과 자동화된 디버깅이 중요해집니다.
    • 철저한 모니터링을 하면 지나치게 피해가 커지기 전에 프로덕션 이슈를 찾아내는 데 도움이 됩니다. 조기에 이슈를 알아차리면 사용자에게 미치는 영향을 최소화할 수 있습니다.
    • 고객에게 영향을 미치는 프로덕션 이슈가 발생했을 때 수작업으로 조사를 하면 확장이 불가능합니다. 근본 원인을 빨리 찾아낼 수 있도록 자동화된 디버깅을 필수로 준비해야 합니다.
  3. 부하 분산이 우수해야 합니다. Presto 플릿의 규모가 커지면 Presto 클러스터에 우수한 부하 분산 솔루션을 배치하는 것이 중요합니다. 규모가 클 때는 부하 분산에서 약간의 비효율만 발생해도 워크로드의 규모 때문에 엄청나게 부정적인 영향을 미칠 수 있습니다.
  4. 구성을 관리해야 합니다. 대규모 Presto 클러스터 플릿의 구성을 관리하는 것은 잘 계획하지 않으면 매우 큰 문제가 될 수 있습니다. 가능한 경우 구성을 핫 리로드가 가능하게 하여 Presto 인스턴스를 다시 시작하거나 업데이트할 때 중단이 발생하지 않도록 해야 합니다. 인스턴스를 중단하면 쿼리가 실패하고 고객의 불만이 발생하게 됩니다.

이 문서는 Meta의 프로덕션 엔지니어인 Neerad Somanchi와 Meta의 개발자 성공 멤버인 Philip Bell과 공동으로 작성하였습니다.

Presto에 대해 자세히 알아보려면 prestodb.io를 방문하거나 YouTube에서 Philip Bell의 Presto에 대한 간단한 설명을 시청하거나, Twitter, FacebookLinkedIn에서 Presto를 팔로우하세요.

Meta Open Source에 대해 자세히 알아보려면 오픈 소스 사이트를 방문하여 YouTube 채널을 구독하거나 Twitter, FacebookLinkedIn을 팔로우하세요.