on
넷플릭스의 캐시 워밍 (Cache warming)
넷플릭스의 캐시 워밍 (Cache warming)
넷플릭스 테크 블로그 한글 번역
Cache warming: Agility for a stateful service
EVCache 는 넷플릭스 플랫폼에서 기본이 되는 부분(Tier-1 이라고 부릅니다)으로 수 페타바이트의 데이터를 가집니다. 넷플릭스의 캐시 레이어에서는 가입, 개인화, 검색 등 많은 부분들에서 사용됩니다. 캐시 레이어는 실 환경에서 수천개의 노드로 이루어져 있고 수백개의 클러스터로 구성되어 있습니다. 노드와 클러스터는 멤버가 증가함에 때라 주기적으로 스케일업을 진행합니다. 캐시를 무겁게 사용하는 경우에 대해 설명해 놓은 것은 여기를 참고하세요 -> Evolution of Application Data Caching: From RAM to SSD.
캐시 저장공간은 스케일 아웃(인스턴스 수를 늘림)이나 스케일 업(각 인스턴스의 성능을 높임)을 통해 확장될 수 있습니다(RAM에서 SSD로 움직이는 경우도 있습니다). 많은 서비스들이 매일 수조개의 쿼리를 탑색합니다. 이러한 부하는 네트워크와 캐시 인프라에 스트레스를 줍니다. 저장공간이 부족하거나 네트워크 수요로 인해 캐시 공간을 늘일 때 우리는 새로운 비어있는 캐시에 데이터를 미리 넣어 두어야 합니다. 우리는 이때 dual-write을 통해 이미 있는 캐시와 새로운 캐시에 동시에 데이터를 쌓고, 기존 캐시에 있는 데이터들이 Time to Live (TTL)이 지나 만료되기를 기다립니다. 이런 방법은 아주 잘 먹힙니다. 하지만 스케일 업을 할때마다 기존 클러스터의 캐시 데이터들이 모두 TTL이 지날때 까지 새로운 캐시 클러스터와 기존 캐시클러스터 두개를 동시에 유지해야한다는 비용적인 단점이 있습니다. 그래서 이 방법은 만료시간이 없거나 오랫동안 변화가 없는 데이터를 저장하는 클러스터 경우에는 좋지 않습니다. 그리고 이렇게 자연스럽게 warmup을 시도하는 경우 데이터 누락이 생길 수 있습니다.
Fig.1 EVCache data path for new deployments
앞선 문제를 설명하기 위해서, EVCache cache warmer infrastructure에 대해서 다음 기능들을 소개하겠습니다:
Replica warmer: 클라이언트들이 기존 레플리카를 사용하는데 문제가 없게 하면서 기존 레플리카에서 새로운 레플리카로 최대한 빠르게 데이터를 카피하는 방식
Instance warmer: 기존 노드가 대체되거나 종료되고나서 새로운 노드를 준비시키는 것
Cache Warmer
캐시 워밍 시스템의 디자인은 다음을 목표로 합니다:
현재 EVCache client들에개 네트워크 영향이 없도록 한다
EVCache node들의 메모리, 디스크 사용량을 최소한으로 한다
웜업 시간을 최소한으로 한다
언제든지 웜업이 가능하도록 한다(Peak/Non-peak periods)
기존 접근 방식
우리는 관련해서 몇가지 디자인 접근 방식에 대한 경험을 해보았습니다.
Piggybacking on Replication
넷플릭스의 클라우드 개발모델은 active-active 입니다. 이것은 우리가 세개의 AWS region들에 있는 EVCache cluster들의 싱크를 맞추고 있다는 뜻입니다. 우리는 자체 개발한 Kafka 기반의 cross-region replication system를 만들었습니다. 처음에는 카프카 큐를 이용해서 새 레플리카를 웜업 시킬 수 있을거라고 생각했습니다. 하지만 이 방법에서 어려웠던점은 TTL이 몇 주씩 되는 key들이 있을 때 비용이 너무 많이 든다는 것이었습니다. 또, 키를 무효화 시키려고 할 때 중복 제거문제도 있었습니다.
Using Key Dumps
이 방식에서는 각 노드 레플리카들에 있는 키와 메타데이터들을 S3로 업로드 하였습니다. 이 키 dump는 Memcached의 LRU crawler utility를 기반하여 만들었습니다. 키 덤프들을 캐시 생성기(cache populator application)에서 사용됩니다. 캐시 생성기는 S3에서 키덤프를 다운받고 각 키를 이용해 기존 레플리카에서 데이터를 받아와 새로운 레플리카에 저장합니다.
캐시 워밍을 위해 기존 레플리카에서 데이터를 가져오면 기존 클라이언트들의 네트웍에 영향이 가게 됩니다. (네트웍을 같이 쓰게 되니 클라이언트 입장에서 가용 네트웍이 줄어들게 됨) 이렇게 되면 운영에 영향을 주게 됩니다. 이런 캐시 워밍 방식은 요청이 많은 시간대에는 진행하기 어려울 수 있습니다. 그래서 네트웍에 영향을 주지 않도록 조금씩 데이터를 가져와서 저장할 수 있도록 해야 했습니다.
Cache Warmer Design
아래 다이어그램은 현재 캐시워밍 시스템의 구조를 대략적으로 보여주고 있습니다. 세개의 메인 블록으로 이루어져 있습니다 -
— Controller, Dumper 그리고 Populator. 좀 더 쉽게 설명하기 위해, Netflix 환경을 구성하는 배포나 클러스터 생성, 종료, 설정 서비스들을 여기서는 포함시키지 않았습니다.
Fig.2 Cache Warmer Architecture
컨트롤러는 오케스트라의 지휘자 역할을 합니다. 컨트롤러는 환경을 만들고 Dumper 와 Populator 사이의 커뮤니케이션 채널의 역할도 하고 리소스들을 정리하기도 합니다. Dumper는 EVCache 사이드카(sidecar)의 일부입니다. 사이드카는 Tomcat서비스에서 memcached와 함께 각 EVCache node에서 dump들을 생성해내는 역할을 합니다. Populator는 Dumper에서 생성한 덤프들을 소진하여 신규 레플리카에 데이터를 생성해주는 역할을 합니다.
Cache Warmer Controller
기존 레플리카에서 어떤 데이터들을 웜업 시킬지는 사람이 선택하거나 컨트롤러가 자주 사용되는 키들을 선택할 수 있습니다. 컨트롤러는 Dumper와 Populator 사이의 통신에 사용 될 SQS를 생성합니다. 그리고 레플리카 노드에서 캐시 덤프를 뜨기 시작합니다. 덤프 뜨는 작업이 되는 중에는 컨트롤러는 Populator클러스터를 만들어냅니다. Populator는 SQS queue 이름이나 다른 설정에서 설정값을 얻습니다. 컨트롤러는 SQS queue 메세지들이 Populator에 의해 모두 소진되기를 기다립니다. SQS queue 가 완전히 비어지면 Populator, SQS queue 등 다른 리소스들을 종료시킵니다.
The Dumper
기존 사용자들이 EVCache replica들을 기존과 똑같이 이용이 가능하게 하려면 각 EVCache node에서 덤프를 떠야합니다. 각 노드에서는 두단계를 거쳐서 데이터 덤프를 생성합니다.
memcached LRU Crawler utility를 이용해 키를 열거하고 수많은 key-chunk 파일들로 덤프를 만든다. Dumper는 각 key-chunk에 있는 키에 해당되는 데이터들을 다시 가져와 local data-chunk파일로 다시 덤프한다.
chunk 사이즈가 더 커질 수 없게 꽉 차면 data-chunk들을 S3로 업로드하여 업로드 한 S3 URI를 SQS queue 메세지로 보냅니다. warm-up id, hostname, S3 URI, dump format, key count와 같은 data-chunk의 메타데이터는 S3에 청크로 존재합니다. 이렇게 데이터를 각 청크별로 소진이 가능합니다. 데이터 청크 사이즈를 조절하면 덤프가 완전히 끝나지 않아도 소진이 가능하게 할 수 있습니다. 각 EVCache node에 많은 key-chunk들이 존재하니 덤프도 병렬로 이루어질 수 있습니다. 병렬 스레드의 갯수는 디스크 가용량, sidecar의 JVM Heap 사이즈, CPU Core에 따라 달라질 수 있습니다.
The Populator
Populator는 목적지 레플리카에 데이터들을 생성시켜주는 역할을 합니다. Populator는 컨트롤러가 세팅해 둔 Archaius의 동적 프로퍼티에서 SQS queue와 레플리카 대한 정보를 가져옵니다. Populator는 SQS queue에서 메세지를 가져옵니다. 메세지에는 S3 URI이 들어있는데 여기서 data-chunk를 다운받아 목적지 레플리카에 데이터를 생성합니다. Populator 는 'add'(i.e 키가 존재하지 않는 경우에만 생성) 작업을 하는데 웜업하는 기간동안 overwrite하지 않도록 하기 위함입니다.
data-dump가 만들어 지자마자 Populator가 바로 일을 시작하는데 Populator cluster는 SQS queue 안에 있는 데이터 청크의 양에 따라 자동으로 스케일링 됩니다.
Instance warmer
거대한 EVCache 배포중에는 하드웨어나 다른 이슈로 주로 AWS에서 노드들을 종료시키거나 대체시킵니다. 이렇게 되면 순간적으로 응답속도가 지연될 수 있는데 EVCache client에서 지금 EVCache 레플리카에 존재하지 않는 데이터들을 요청하게 되기 때문입니다. 이때 캐시의 hit rate도 줄어들게 됩니다.
이런 현상을 인스턴스를 새로 시작할 때 캐시를 빠르게 웜업시키는 방식으로 보완할 수 있습니다. 우리는 instance warming을 위해 cache Dumper를 조금 변경하고 EVCache nodes가 재시작될 때 알림 시그널을 추가하여 cache warmer 기능을 확장시켰습니다.
Fig.3 Instance Warmer Architecture
이 다이어그램은 instance warming의 구조입니다. 여기 세개의 EVCache 레플리카들이 있습니다. 셋 중 한개의 노드는 (가운데 붉은 색으로 보이는 것) 재시작이 되었고 웜업이 필요합니다.
컨트롤러가 EVCache node가 재시작되었다는 시그널을 받게되면 웜업 프로세스를 시작합니다. 컨트롤러는 이 재시작 된 레플리카가 데이터 소스 레플리카로 사용되지 않도록 합니다. EVCache는 가상 노드들을 consistent hashing로 관리합니다. 새로 생성된 노드에 필요한 데이터들은 전체 레플리카에 분산되어잇어 모든 노드의 데이터 덤프가 필요합니다. Controller에서 덤프를 시작하면 웜업이 필요한 특정 노드에 신호를 보냅니다. Dumper에서는 해싱 된 데이터 키만을 이용해서 덤프를 뜹니다. Populator는 data-chunks를 소진하면서 웜업이 필요한 노드에 데이터를 생성합니다.
이 instance warming프로세스는 각 노드에 있는 데이터의 일부씩만 다룬다는 점에서 replica warming보다 훨씬 가볍습니다.
실제 결과
cache warmer는 TTL이 몇시간 이상씩 되는 경우 스케일링을 할 때 주로 사용됩니다. 이는 특히 휴일 트래픽을 감당하면서 캐시를 스케일링해야할 때 매우 유용합니다.
아래 차트는 두개의 기존 레플리카 중 한대를 이용해 두 개의 새로운 레플리카를 워밍업 하는 모습을 보여줍니다. 기존 레플리카들은 약 50억개의 아이템을 가지고 약 12 테라바이트 데이터를 가지고 있었습니다. 그리고 웜업하는데 약 2시간이 걸렸습니다.
Fig.4 Cache warmer in action
우리가 웜업해야 했던 가장 큰 캐시는 약 700테라바이트로 약 460억개의 아이템을 가지고 있었습니다. 이 캐시는 380개의 노드 레플리카들을 가지고 있었고, 캐시를 카피하는데만 24시간이 걸리고 570개의 populator 인스턴스가 필요했습니다.
instance warmer는 실 환경에서도 사용중이고 매일 몇개의 인스턴스들을 웜업합니다. 아래 차트는 instance warming의 예시인데 5시 27분쯤 인스턴스 한개가 교체되었고 웜업은 15분 내로 걸렸으며 약 2.2GB, 1.5억개의 아이템들이 웜업되었습니다. 평균 데이터 사이즈와 아이템 수는 아래 차트를 참고해주세요.
Fig.5 Instance warmer in action
더 개선할 수 있는 점
Elastic scaling: 위에 설명했던 자동 instance warm-up은 EVCache의 scale out 이나 scale in을 편리하게 합니다. 이는 새로운 레플리카 생성해서 웜업하지 않아도 되기 때문에 EVCache배포 비용을 줄여줍니다. 다만 해싱이 변경되어야 하고, orphaned keys들을 지워야 한다는 점에서 클라이언트들에게 영향을 최소화 하기가 어렵습니다.
EBS storage: 현재 방식에서 가장 큰 병목은 데이터청크들을 S3로 업로드하고 다운로드 하는 부분입니다. S3네트웍이 어떤 순간에는 쓰로틀링이 되는 것을 발견했습니다. S3 대체로 data-chunks를 저장하고 다운받을 때 사용될 수 있는 것이 EBS 스토리지 입니다. 이 아이디어는 각 노드들의 Dumper에 EBS에 직접 붙어 데이터를 이미 알고 있는 공간에 덤프하는 것입니다. Populator도 같은 EBS에 붙어 데이터를 다운받습니다. 빠른 웜업을 위해 Dumper와 Populator가 동시에 작업을 해서 같은 EBS에 붙으려면 많은 인스턴스들이 더 필요할 겁니다.
Adapting to other data stores: 우리 팀은 Dynomite와 같은 다른 고성능 메모리 시스템도 지원합니다. 우리는 우리 컴포넌트들이 Dynomite의 수평확장에도 적용될 수 있는지 확인해볼 것입니다.
결론
이번에는 cache warming 인프라 구조에대해 다루어 보았습니다. 유연한 캐시 워밍 구조는 기 존재하는 레플리카에서 하나 이상의 새로운 레플리카로 데이터를 카피하여 하드웨어 이슈나 각종 이슈들로 인해 종료되거나 대체된 노드들을 웜업할 수 있었습니다. 우리의 구조는 캐시 웜업의 속도를 조절할 수 있는데 이는 Dumper와 Populator가 서로 디펜던시를 느슨하게 가지고 있기 때문입니다. 우리는 이 캐시 웝업시스템을 수개월째 사용중이고 이 시스템이 더 많은 시스템들을 지원할 수 있도록 확장시킬 계획입니다.
출처 넷플릭스 테크 블로그 https://netflixtechblog.com/cache-warming-agility-for-a-stateful-service-2d3b1da82642
from http://limago.tistory.com/5 by ccl(A) rewrite - 2021-09-24 19:00:51