Redis cluster, no longer vaporware.

Salvatore Sanfilippo

Redis 클러스터, 더 이상 베이퍼웨어가 아니다

원문은 Salvatore Sanfilippo님이 에 게재했습니다. 이 블로그 구독하기

내 git 히스토리에서 찾을 수 있는 Redis Cluster에 관한 첫 커밋은 2011년 3월 29일자이지만, 이는 “copy and commit” 방식의 머지였다. 클러스터 브랜치의 히스토리는 API와 시스템 나머지 부분과의 상호작용에 대한 초기 아이디어를 다듬기 위한 진행 중인 커밋들로 엉망이었기 때문에 삭제되었다.

기본적으로 4년 가까이 된 프로젝트다. 이는 Redis 프로젝트 전체 역사의 3분의 2에 해당한다. 그런데도 클러스터를 지원하는 첫 번째 버전인 Redis 3.0.0의 첫 릴리스 후보(Release Candidate)를 오늘에서야 공개한다.

우여곡절의 연속

왜 이렇게 오래 걸렸는지 이해하는 건 어렵지 않다. 나는 클러스터 프로젝트를 상당히 성급하게 시작했다. 자동 확장 수단 없이는 Redis가 완전히 쓸모없어질 것처럼 보이던 시기였다. 클러스터 프로젝트를 시작하기에 적절한 시기가 아니었다. 단순히 Redis 자체가 너무 미성숙했기 때문에, 아직 탄탄한 “단일 인스턴스” 이야기를 들려줄 단계가 아니었다.

잘못된 시기에 프로젝트를 시작한 것은 내 실수였지만, 적어도 커뮤니티에서 들어오는 요청들을 무시하는 함정에는 빠지지 않았다. 그래서 다른 핵심 기능들에 더 많은 리소스를 할애하기 위해 프로젝트는 수없이 중단되곤 했다. 영속성(persistence), 복제(replication), 지연 시간(latency), 인트로스펙션(introspection)은 클러스터보다 훨씬 더 많은 관심을 받았다. 단순히 사용자들에게 더 중요했기 때문이다.

이 프로젝트의 또 다른 한계는, 내가 시작했을 당시 분산 프로그래밍에 대해 전혀 아는 바가 없었다는 점이다. 첫 설계는 형편없었다. “제품” 요구사항, 즉 낮은 지연 시간, 선형 확장성, 작은 클러스터에서의 적은 오버헤드만을 잘 담아냈을 뿐이었다. 하지만 세부 사항은 모두 잘못되어 있었고, 필요 이상으로 훨씬 복잡했으며, 사용된 알고리즘은 안전하지 않았다.

조금씩 진전을 이루면서 나는 분산 프로그래밍의 기초를 공부하기 시작했고, Redis Cluster를 재설계했으며, 같은 아이디어를 Sentinel의 새 버전에 적용했다. 두 시스템이 사용하는 분산 프로그래밍 알고리즘은 여전히 원시적이다. 비동기 복제(asynchronous replicated) 방식의 결과적 일관성(eventually consistent) 시스템이기 때문에 합의(consensus)나 다른 까다로운 문제들을 다룰 필요가 없었기 때문이다. 하지만 적어도 CP 스토어를 만드는 것에 비하면 단순한 문제를 다루는 경우에도, 자신이 무엇을 하고 있는지 이해하지 못하면 결과 시스템은 완전히 잘못될 수 있다.

이런 모든 문제에도 불구하고, 나는 프로젝트를 계속 붙잡고 고치고 구현을 수정하며 성숙시키려 노력했다. 작은 방 안의 코끼리처럼 Redis 커뮤니티 전체에 퍼져 있는 단순한 사실이 있었기 때문이다. 사람들은 계속해서, 각자의 노력으로, 그리고 여러 번 완전히 잘못된 방식으로 두 가지 일을 반복하고 있었다.

  1. 데이터셋을 N개의 노드에 샤딩하는 일.
  2. 특정 장애에서 살아남기 위한 신속한 페일오버 절차.

문제 “2”는 너무 심각해서, 나는 클러스터가 완성되기 전에 Redis Sentinel 프로젝트를 시작하기로 했다. 가능한 한 빨리 고가용성(HA) 시스템을 제공하고, “1”이 아닌 “2”만 필요한 대다수의 사용 사례에는 Redis Cluster보다 더 적합한 시스템을 제공하기 위해서였다.

마침내 이 노력의 첫 번째 현실적인 결과를 보기 시작했고, 이제 우리는 채택을 이끌고 남은 버그를 수정하며 시스템을 보다 점진적인 방식으로 개선하는 데 필요한 근본적인 이정표인 릴리스 후보를 갖게 되었다.

실제로 무엇을 하는가?

Redis Cluster는 기본적으로 데이터 샤딩 전략이며, 클러스터가 실행되는 동안 키를 한 노드에서 다른 노드로 리샤딩할 수 있는 기능과, 시스템이 특정 종류의 장애에서 살아남을 수 있도록 보장하는 페일오버 절차를 함께 제공한다.

분산 데이터베이스 관점에서 보면, Redis Cluster는 파티션 상황에서 제한적인 가용성과 약한 형태의 일관성을 제공한다. 기본적으로 CP 시스템도 AP 시스템도 아니다. 다시 말해, Redis Cluster는 특정한 현실 세계의 특성을 얻기 위해 분산 시스템으로 가능한 이론적 한계까지 달성하지는 않는다.

일관성 모델은 유명한 “결과적 일관성(eventual consistency)” 모델이다. 기본적으로 파티션으로 인해 노드들이 비동기화되더라도, 파티션이 복구되면 특정 키를 담당하는 모든 노드가 그 값에 대해 합의한다는 것이 보장된다.

하지만 병합 전략은 “마지막 페일오버가 승리(last failover wins)” 방식이므로, 네트워크 파티션 중에 수신된 쓰기는 유실될 수 있다. 흔한 예는 마스터가 클라이언트들의 쓰기 요청과 함께 소수 파티션 쪽에 고립되는 경우다. 파티션이 복구될 때, 다수 파티션 쪽에서 슬레이브가 이 마스터를 대체하도록 승격되었다면, 이전 마스터가 수신한 쓰기는 유실된다.

이는 다시 말해 Redis Cluster가 값 병합을 시도하기 위해 자료구조에 메타데이터를 담을 필요가 없으며, Redis가 지원하는 다양한 명령어와 자료구조가 Redis Cluster에서도 그대로 지원된다는 뜻이다. 따라서 추가적인 메모리 오버헤드도, API 제한도, 값이 포함할 수 있는 요소 수에 대한 제한도 없지만, 파티션 상황에서의 안전성은 떨어진다.

Redis Cluster처럼 설계된 시스템에서 노드들이 분기되는 것은 좋지 않다는 것을 이해하는 건 어렵지 않다. 그래서 시스템은 두 노드가 분기할 확률(그리고 분기의 정도)을 제한함으로써 단점을 완화하려 한다. 이는 몇 가지 방법으로 달성된다.

  1. 파티션의 소수 쪽은 사용 불가능한 상태가 된다.
  2. 복제는 일반적으로 클라이언트에 대한 응답과 슬레이브로의 복제 스트림이 동시에 전송되도록 설계되었다.
  3. 마스터를 페일오버할 수 있는 슬레이브가 여러 개 있을 때, 시스템은 실패한 마스터로부터 덜 분기된 것으로 보이는 슬레이브를 선택하려고 시도한다.

이러한 전략들은 시스템의 이론적 특성을 바꾸지는 않지만, 흔한 Redis Cluster 장애 모드에 대해 현실 세계에서의 보호를 조금 더 추가한다.

Redis API와 사용 사례에 대해서는 이 설계가 타당하다고 믿지만, 과거에는 많은 이들이 동의하지 않았다. 하지만 내 생각에 각 설계자는 원하는 대로 시스템을 설계할 자유가 있다. 단 하나의 규칙이 있을 뿐이다. 진실을 말하라는 것이다. 그래서 Redis Cluster는 공식 문서에 그 한계와 장애 모드를 명확하게 문서화한다.

시스템이 유용한지 아닌지를 결정하는 것은 사용자와 당면한 사용 사례다. 내 느낌으로는 6년이 지난 후에도 사용자들이 클러스터링 지원이 전혀 없는 상태에서도 계속 Redis를 사용해 온 이유는, 사용 사례가 그것을 가능하게 했고 Redis가 특정 문제를 해결하는 데 매우 적합하게 만드는 특유의 기능과 성능을 제공했기 때문이다. Redis Cluster가 많은 사용자들의 삶을 개선해주길 바란다.

앞으로의 길

마침내 우리는 출시할 수 있는 최소 기능 제품(MVP)을 갖게 되었다. 사용자가 본격적으로 테스트를 시작하고, 경우에 따라서는 이미 도입할 수 있을 만큼 충분히 안정적이다. 채택이 많아질수록 더 많이 개선할 수 있다. 나는 이를 Redis와 Sentinel을 통해 알고 있다. 이제 소프트웨어를 쓸 만한 수준에서 성숙한 수준으로 끌어올리는 점진적인 과정이 시작된다. 사용자들의 목소리를 듣고, 버그를 수정하고, 더 많은 코드를 테스트로 커버하는 식으로 말이다.

동시에 나는 Redis Cluster의 다음 버전에 대해서도 생각하기 시작했다. 지금은 추가할 수 없었던 많은 유용한 기능들로 v1을 개선하는 것이다. 예를 들면 멀티 데이터센터 지원, 명령어 리플레이를 이용한 소수 파티션에서의 더 높은 쓰기 안전성, 자동 노드 밸런싱(현재는 일부 노드가 너무 비어 있고 다른 노드가 너무 가득 차면 수동으로 리샤딩해야 한다) 등 많은 것들이 있다.

게다가 Redis Cluster는 캐싱을 위해 특별히 설계된 실행 모드의 혜택을 받을 수 있다고 믿는다. 소수 파티션에서도 가용성을 유지하기 위해 노드가 자신이 담당하지 않는 해시 슬롯에 대한 쓰기를 허용하는 모드다.

우리의 구현과 설계를 개선하고 수정할 시간은 항상 있다. 하지만 어떤 소프트웨어가 어떻게 되었으면 좋겠는지에 너무 집착하면, 필요 이상으로 오랫동안 베이퍼웨어 범주에 머물게 할 위험이 있다. 이제 놓아줄 때다. Redis Cluster를 즐기시길!

Redis Cluster RC1은 GitHub에서 '3.0.0-rc1' 태그로, 또는 Redis.io 다운로드 페이지인 http://redis.io/download에서 tarball 형태로 제공된다.

이 글은 muse-spark-1.2-contributor 모델을 사용해 번역했습니다.

댓글