구독해서 새 게시물에 대한 알림을 받으세요.

Workers, Durable Objects, Queues로 Super Slurper를 5배 더 빠르게 만들기

2025-04-10

8분 읽기
이 게시물은 English, 繁體中文, Français, Deutsch, 日本語, Español简体中文로도 이용할 수 있습니다.

Super Slurper는 Cloudflare의 데이터 마이그레이션 도구로, 클라우드 개체 스토리지 공급자와 Cloudflare R2 간의 대규모 데이터 전송을 쉽게 처리할 수 있도록 설계되었습니다. 출시 이후 수천 명의 개발자가 Super Slurper를 사용하여 AWS S3, Google Cloud Storage, 기타 S3 호환 서비스 에서 페타바이트 규모의 데이터를 R2로 이동했습니다.

하지만 Cloudflare는 그 속도를 훨씬 더 높일 수 있는 기회를 발견했습니다. Cloudflare Workers, Durable Objects, Queues 등을 기반으로 Cloudflare 개발자 플랫폼을 활용하여 Super Slurper를 처음부터 다시 설계하고, 전송 속도를 최대 5배 개선했습니다. 이 게시물에서는 초기 아키텍처, Cloudflare가 파악한 성능 병목 현상, 이를 해결한 방법, 이러한 개선 사항이 실제 환경에 미치는 효과에 대해 자세히 살펴보겠습니다.

초기 아키텍처 및 성능 병목 현상

Super Slurper는 원래 AWS S3에서 Cloudflare Images로 이미지를 대량으로 가져오기 위해 구축된 도구인 SourcingKit와 아키텍처를 공유했습니다. SourcingKit은 Kubernetes에 배포되어 Images 서비스와 함께 실행되었습니다. Super Slurper 구축을 시작했을 때 Cloudflare는 이를 자체 Kubernetes 네임스페이스로 분리하고, 개체 스토리지 사용 사례에 더 쉽게 사용할 수 있도록 몇 가지 새로운 API를 도입했습니다. 이 설정은 잘 작동했으며 수천 명의 개발자가 데이터를 R2로 이동하는 데 도움이 되었습니다.

하지만 어려움이 없었던 것은 아닙니다. SourcingKit은 페타바이트급 대규모 전송에 필요한 규모를 처리하도록 설계되지 않았습니다. SourcingKit, 더 나아가 Super Slurper는 핵심 데이터 센터 중 한 곳에 위치한 Kubernetes 클러스터에서 운영되었으므로 Cloudflare의 제어판, 분석 및 기타 서비스와 컴퓨팅 리소스 및 대역폭을 공유해야 했습니다. 마이그레이션 수가 증가함에 따라 이러한 리소스 제약은 명백한 병목 현상이 되었습니다.

개체 스토리지 공급자 간에 데이터를 전송하는 서비스의 경우 작업은 간단합니다. 소스에서 개체를 나열하고, 대상으로 복사하는 작업을 반복하는 것입니다. 이것이 원래 Super Slurper가 작동한 방식이었습니다. 소스 버킷에서 개체를 나열하고, 해당 목록을 Postgres 기반 대기열(pg_queue)에 푸시한 다음, 일정한 속도로 이 대기열에서 가져와 개체를 복사했습니다. 개체 스토리지 마이그레이션의 규모를 고려할 때 대역폭 사용량은 필연적으로 높을 수밖에 없었습니다. 이로 인해 확장하기가 어려웠습니다.

핵심 데이터 센터에서만 작동하는 대역폭 제약을 해결하기 위해, Cloudflare는 추가로 Cloudflare Workers를 도입했습니다. 데이터 복사를 핵심 데이터 센터에서 처리하는 대신, Worker가 실제 복사를 수행하도록 호출하기 시작한 것입니다.

Super Slurper의 사용량이 증가함에 따라 Kubernetes 리소스 사용량도 함께 증가했습니다. 데이터 전송 중 상당한 시간이 네트워크 I/O 또는 스토리지 대기 시간에 소요되었으며, 실제로 컴퓨팅 집약적인 작업을 수행하는 데는 사용되지 않았습니다. 따라서 더 많은 메모리나 더 많은 CPU가 필요한 것이 아니라 더 높은 동시성이 필요했습니다.

수요를 따라잡기 위해 Cloudflare는 복제본 수를 계속 증가시켰습니다. 하지만 결국 한계에 부딪혔습니다. Cloudflare는 수십 개의 포드 규모로 실행할 때 확장성 문제에 직면했으며, 수십 배 더 큰 규모를 원했습니다.

Cloudflare는 지금까지 이어온 아키텍처에 의존하는 대신 처음부터 전체 접근 방식을 재고하기로 결정했습니다. 약 일주일 만에 Cloudflare Workers, Durable Objects, Queues를 사용하여 대략적인 개념 증명을 구축했습니다. Cloudflare는 소스 버킷에서 개체를 나열하고, 대기열에 푸시한 다음, 대기열의 메시지를 사용하여 전송을 시작했습니다. 이는 원래 구현 방식과 매우 유사해 보일 수 있지만, Cloudflare 개발자 플랫폼을 기반으로 구축함으로써 이전보다 수십 배 더 높은 규모를 자동으로 달성할 수 있었습니다.

  • Cloudflare Queues: 비동기식 개체 전송을 가능하게 하고, 마이그레이션되는 개체 수에 맞게 자동으로 확장됩니다.

  • Cloudflare Workers: Kubernetes의 오버헤드 없이 간단한 컴퓨팅 작업을 실행하며, 프로세스의 각 부분이 실행되는 위치를 최적화하여 대기 시간을 줄이고 성능을 개선합니다.

  • SQLite 기반 Durable Objects(DO): 완전히 분산된 데이터베이스 역할을 하여 단일 PostgreSQL 인스턴스의 한계를 제거합니다.

  • Hyperdrive: 원래 PostgreSQL 데이터베이스에서 이전 작업 데이터에 빠르게 액세스할 수 있도록 하여 이를 아카이브 저장소로 유지합니다.

몇 가지 테스트를 진행한 결과, 수백 개 개체 수준의 소규모 전송에서는 개념 증명 구현이 기존 구현 방식보다 느렸지만, 전송 규모가 수백만 개의 개체로 확장됨에 따라 기존 성능과 맞먹거나 이를 초과하는 결과를 달성했습니다. 이는 Cloudflare가 개념 증명을 프로덕션으로 전환하기 위해 시간을 투자할 필요가 있다는 신호였습니다.

Cloudflare는 개념 증명 해킹을 제거하고 안정성을 개선했으며, 전송을 훨씬 더 높은 동시성으로 확장할 수 있는 새로운 방법을 찾아냈습니다. 몇 차례의 반복 작업 끝에, 만족할 만한 결과물을 얻을 수 있었습니다.

새로운 아키텍처: Workers, Queues, Durable Objects

처리 계층: 마이그레이션 흐름 관리

처리 계층의 중심에는 대기열, 소비자 작업자가 있습니다. 이 과정은 다음과 같이 진행됩니다.

마이그레이션 시작

클라이언트가 마이그레이션을 트리거하면 API Worker로 전송된 요청으로 마이그레이션이 시작됩니다. 이 작업자는 마이그레이션에 대한 세부 정보를 가져와 데이터베이스에 저장하고, List Queue에 메시지를 추가하여 프로세스를 시작합니다.

소스 버킷 개체 나열

List Queue Consumer는 본격적인 작업이 시작되는 지점입니다. 대기열에서 메시지를 가져오고, 소스 버킷 에서 개체 목록을 검색하며, 필요한 필터를 적용하고, 중요한 메타데이터를 데이터베이스에 저장합니다. 그런 다음 개체 전송 메시지를 Transfer Queue에 추가하여 새로운 작업을 생성합니다.

Cloudflare는 즉시 새로운 작업 배치를 대기열에 추가하여 동시성을 극대화합니다. 내장된 제한 메커니즘은 종속 시스템 다운과 같은 예기치 않은 장애가 발생했을 때 Cloudflare 대기열에 메시지를 더 추가하지 않도록 합니다. 이를 통해 안정성을 유지하고 중단 시 과부하를 방지할 수 있습니다.

효율적인 개체 전송

Transfer Queue Consumer Workers는 대기열에서 개체 전송 메시지를 가져와 각 개체가 한 번만 처리되도록 데이터베이스에서 개체 키를 잠급니다. 전송이 완료되면 개체의 잠금이 해제됩니다. 더 큰 개체의 경우, 이를 적절한 크기로 분할하여 멀티파트 업로드 방식으로 전송합니다.

원활한 오류 처리

분산 시스템에서는 오류가 불가피하기 때문에, Cloudflare는 이에 대한 대비를 마련해야 했습니다. 일시적인 오류에 대해서는 자동 재시도 메커니즘을 구현하여, 마이그레이션 흐름이 중단되지 않도록 했습니다. 그러나 재시도로 해결할 수 없는 문제는 Dead Letter Queue (DLQ)로 메시지를 이동시켜 추후 검토 및 해결을 위해 로그에 기록합니다.

작업 완료 및 수명 주기 관리

모든 개체가 나열되고 전송이 진행되면 Lifecycle Queue Consumer가 전체 프로세스를 주시합니다. 이 Consumer는 진행 중인 전송을 모니터링하여 누락된 개체가 없도록 보장합니다. 모든 전송이 완료되면 작업이 완료된 것으로 표시되고, 마이그레이션 프로세스가 마무리됩니다.

데이터베이스 계층: 내구성 있는 스토리지 및 레거시 데이터 검색

새로운 아키텍처를 구축할 때, Cloudflare는 대규모 데이터 세트를 처리할 수 있으면서도 과거 작업 데이터의 검색을 보장할 수 있는 강력한 솔루션이 필요하다는 것을 알고 있었습니다. Durable Objects(DO)Hyperdrive의 조합이 바로 그 역할을 했습니다.

Durable Objects

Cloudflare는 각 계정에 마이그레이션 작업을 추적할 수 있도록 전용 Durable Object를 할당했습니다. 각 작업의 DO에는 버킷 이름, 사용자 옵션, 작업 상태 등의 중요한 세부 정보가 저장됩니다. 이를 통해 모든 것이 체계적이고 관리하기 쉽게 유지될 수 있었습니다. Cloudflare는 대규모 마이그레이션을 지원하기 위해 전송 대기 중인 모든 개체를 관리하는 Batch DO를 추가했으며, 여기에는 전송 상태, 개체 키, 추가 메타데이터가 저장됩니다.

마이그레이션이 수십억 개의 개체로 확장됨에 따라 Cloudflare는 스토리지에 보다 창의적으로 접근해야 했습니다. Cloudflare는 요청 부하를 분산하기 위해 샤딩 전략을 구현하여 병목 현상을 방지하고, SQLite DO의 10GB 스토리지 한계를 극복했습니다. 개체가 전송됨에 따라, Cloudflare는 해당 세부 정보를 정리하여 스토리지 공간을 최적화했습니다. 십억 개의 개체 키가 얼마나 많은 스토리지를 필요로 하는지는 놀라울 정도입니다!

Hyperdrive

Cloudflare는 수년간의 마이그레이션 기록이 있는 시스템을 재구축하고 있었으므로 과거의 모든 마이그레이션 세부 정보를 보존하고 액세스할 수 있는 방법이 필요했습니다. Hyperdrive는 레거시 시스템과의 연결고리 역할을 하며, 핵심 PostgreSQL 데이터베이스에서 과거 작업 데이터를 원활하게 검색할 수 있도록 지원합니다. 이 기능은 단순한 데이터 검색 메커니즘이 아니라, 복잡한 마이그레이션 시나리오를 위한 아카이브 역할도 수행합니다.

결과: Super Slurper, 이제 R2로 최대 5배 빠르게 데이터 전송

그렇다면, 이 모든 작업 끝에 전송 속도를 높이려는 목표를 실제로 달성했을까요?

Cloudflare는 AWS S3에서 R2로 75,000개의 개체를 마이그레이션하는 테스트를 진행했습니다. 기존 구현으로는 전송에 15분 30초가 소요되었습니다. 그러나 성능 개선 이후에는 동일한 마이그레이션이 단 3분 25초 만에 완료되었습니다.

올해 2월부터 프로덕션 환경에서 새로운 서비스를 사용한 마이그레이션이 시작되면서, 개체 크기 분포에 따라 일부 사례에서는 훨씬 더 큰 성능 개선이 확인되었습니다. Super Slurper는 출시된 지 약 2년이 되었지만, 성능 개선 덕분에 훨씬 더 많은 데이터를 이동할 수 있게 되었습니다. Super Slurper를 통해 복사된 전체 개체 중 무려 35%가 지난 두 달 동안 전송된 것입니다.

한계 및 이슈

새로운 아키텍처를 구축하며 Cloudflare가 직면한 가장 큰 과제 중 하나는 중복 메시지를 처리하는 것이었습니다. 중복이 발생할 수 있는 경우는 몇 가지가 있었습니다.

  • Queues는 최소 한 번 전송하므로, 전송을 확실히 보장하기 위해 소비자는 동일한 메시지를 두 번 이상 받을 수 있습니다.

  • 오류와 재시도 또한 명백한 중복을 만들어낼 수 있습니다. 예를 들어, 개체가 이미 전송된 후 Durable Object에 대한 요청이 실패하면, 재시도를 통해 동일한 개체를 다시 처리할 수 있습니다.

올바르게 처리되지 않으면 동일한 개체가 여러 번 전송될 수 있습니다. Cloudflare는 이 문제를 해결하기 위해 각 개체가 정확하게 처리되고 한 번만 전송되도록 몇 가지 전략을 구현했습니다.

  1. 개체 목록 작성은 순차적으로 진행되기 때문에(예: 개체 2를 얻으려면 개체 1 목록에서 얻은 연속 토큰이 필요함), Cloudflare는 각 목록 작성 작업에 고유한 시퀀스 ID를 할당합니다. 이를 통해 중복 목록을 감지하고 여러 프로세스가 동시에 시작되는 것을 방지할 수 있습니다. 이 방법은 데이터베이스와 대기열 작업이 완료될 때까지 기다리지 않고 다음 배치를 나열하기 때문에 특히 유용합니다. 목록 2가 실패하면 다시 시도할 수 있으며, 목록 3이 이미 시작된 경우 불필요한 재시도를 건너뛸 수 있습니다.

  2. 각 개체는 전송이 시작될 때 잠금이 설정되어 동일한 개체가 병렬로 전송되는 것을 방지합니다. 전송이 성공적으로 완료되면 개체 키를 데이터베이스에서 삭제하여 잠금을 해제합니다. 추후에 해당 개체에 대한 메시지가 다시 나타나더라도 데이터베이스에 키가 더 이상 존재하지 않으면, 메시지가 이미 전송된 것으로 안전하게 간주할 수 있습니다.

  3. Cloudflare는 카운트를 정확히 유지하기 위해 데이터베이스 트랜잭션에 의존합니다. 개체가 잠금 해제에 실패하면 카운트는 변경되지 않습니다. 마찬가지로, 데이터베이스에 개체 키 추가가 실패하면 카운트가 업데이트되지 않고, 이후 작업이 다시 시도됩니다.

  4. 마지막 안전 장치로, 대상 버킷에 개체가 이미 존재하며, 그 개체가 Cloudflare가 마이그레이션을 시작한 후에 게시되었는지 확인합니다. 만약 개체가 마이그레이션 시작 이후에 업로드된 경우, 해당 개체는 Cloudflare 프로세스(또는 다른 프로세스)에 의해 이미 전송된 것으로 간주하고 건너뛰어도 안전합니다.

Super Slurper의 다음 계획은?

Cloudflare는 항상 Super Slurper를 더 빠르고, 확장 가능하며, 훨씬 더 사용하기 쉽게 만들기 위한 방법을 항상 모색하고 있습니다. 그리고 이는 시작에 불과합니다.

  • 최근에는 모든 S3 호환 스토리지 공급자에서 마이그레이션할 수 있는 기능을 출시했습니다!

  • 현재 데이터 마이그레이션은 계정당 3개의 동시 마이그레이션으로 제한되어 있지만, Cloudflare는 이 제한을 늘리고자 합니다. 이를 통해 개체 접두사를 별도의 마이그레이션으로 나누어 병렬로 실행할 수 있게 되어, 버킷 마이그레이션 속도가 획기적으로 향상될 것입니다. Super Slurper 및 기존 개체 스토리지에서 R2로 데이터를 마이그레이션하는 방법에 대한 자세한 내용은 Cloudflare 문서를 참조하세요.

추신 이번 업데이트를 통해 API 사용 방법도 훨씬 간단해져, 이제 마이그레이션을 프로그래밍 방식으로 관리할 수 있습니다!

Cloudflare에서는 전체 기업 네트워크를 보호하고, 고객이 인터넷 규모의 애플리케이션을 효과적으로 구축하도록 지원하며, 웹 사이트와 인터넷 애플리케이션을 가속화하고, DDoS 공격을 막으며, 해커를 막고, Zero Trust로 향하는 고객의 여정을 지원합니다.

어떤 장치로든 1.1.1.1에 방문해 인터넷을 더 빠르고 안전하게 만들어 주는 Cloudflare의 무료 애플리케이션을 사용해 보세요.

더 나은 인터넷을 만들기 위한 Cloudflare의 사명을 자세히 알아보려면 여기에서 시작하세요. 새로운 커리어 경로를 찾고 있다면 채용 공고를 확인해 보세요.
Developer WeekR2 Super SlurperCloudflare WorkersDurable ObjectsCloudflare QueuesQueues

X에서 팔로우하기

Cloudflare|@cloudflare

관련 게시물