RedisRaft(현재 개발 중)는 오픈소스 Redis의 새로운 모듈로, 여러 대의 Redis 서버를 하나의 장애 허용(fault-tolerant)이면서 강력한 일관성(strongly consistent)을 갖춘 클러스터로 운영할 수 있게 해줍니다. 이름 그대로 Raft 합의 알고리즘과 이를 구현한 오픈소스 C 라이브러리에 기반하고 있습니다.
RedisRaft는 Redis와 Redis 생태계에 엄격한 직렬화(strict serialization)를 지원하는 새로운 강력한 일관성 배포 옵션을 제공합니다. 이 모듈을 활용하면 기존 Redis 클라이언트, 라이브러리, 데이터 타입을 그대로 사용하면서도 높은 수준의 신뢰성과 일관성이 요구되는 캐시 이상(beyond-cache) 시나리오에서 Redis를 활용할 수 있습니다.
RedisRaft의 시작
RedisRaft는 Redis 5 출시 직전 실험적인 '사이드 프로젝트'로 시작되었습니다. Redis 모듈 API는 Redis 4에서 도입되었으며, 주로 새로운 데이터 타입과 명령어를 구현하는 모듈을 지원하기 위해 설계되었습니다. 개발팀은 이 API를 얼마나 폭넓게 확장할 수 있는지 탐색하고, 모듈을 통해 Redis를 더욱 급진적인 방식으로 확장해 보고자 했습니다. 동시에 실질적으로 유용한 결과물, 즉 Redis를 위한 강력한 일관성 배포 옵션을 만드는 것을 목표로 삼았습니다.
RedisRaft 클러스터는 ZooKeeper나 Etcd처럼 검증된 신뢰성 높은 데이터 스토어에서 기대하는 수준의 일관성과 신뢰성을 제공합니다. 핵심만 요약하면 RedisRaft에서는 다음이 보장됩니다.
- 승인된 쓰기 작업은 커밋이 보장되며 절대 손실되지 않습니다.
- 읽기 작업은 항상 가장 최근에 커밋된 쓰기를 반환합니다.
이 수준의 일관성을 제공하는 데이터 스토어에서 예상할 수 있듯이, 이러한 보장에는 성능과 가용성 측면의 트레이드오프가 따릅니다. RedisRaft에서는 다음과 같은 특성이 있습니다.
- 클라이언트 작업은 클러스터 노드 간 메시지 교환에 의존하므로 네트워크 지연 시간의 영향을 받습니다.
- 쓰기 작업은 완료 전 디스크에 플러시(flush)되어야 하므로 디스크 I/O 지연 시간의 영향을 받습니다.
- 클러스터는 과반수 노드가 정상적으로 가동 중이고 서로 통신 가능할 때만 사용할 수 있습니다.
RedisRaft의 작동 방식
RedisRaft 모듈이 Redis에 로드되면 클러스터 노드 간 통신, Raft 로그 및 스냅샷 복제, 영속화 등을 전담합니다. Redis 코어는 이를 인식하지 못하며, Redis 입장에서는 클러스터링, 영속화, 복제 기능 없이 단독 서버로 동작하는 것과 같습니다.
3노드 RedisRaft 클러스터 구축은 3개의 Redis 서버를 실행하는 것만큼 간단합니다.
redis-server --loadmodule /path/to/redisraft.so
첫 번째 서버에 접속해 Raft 클러스터를 생성하는 방법은 다음과 같습니다.
10.0.0.1:6379> RAFT.CLUSTER INIT
OK 989645460313dd2ddb051f033c791222
이후 나머지 두 서버에 접속해 클러스터에 참여시킵니다.
10.0.0.2:6379> RAFT.CLUSTER JOIN 10.0.0.1:6379
OK
10.0.0.3:6379> RAFT.CLUSTER JOIN 10.0.0.1:6379
OK
클러스터 접근하기
RedisRaft 설정이 완료되면 클러스터에 데이터를 쓸 수 있습니다.
10.0.0.1:6379> INCR counter:1
(integer) 1
응답을 받았다는 것은 해당 쓰기가 클러스터 노드 과반수(이 경우 2개 노드)에 복제되었고, 영구 저장소에 커밋되었음을 의미합니다.
Raft는 강력한 리더(strong-leader) 개념에 기반하므로, 모든 클라이언트 작업은 리더 노드로 향해야 하며 리더에서 시작되어야 합니다. 위 예시에서 클라이언트는 실제로 리더에 연결되어 있었지만, 클러스터 리더십은 동적으로 변경될 수 있으므로 클라이언트가 항상 리더를 알고 있다고 보장할 수 없습니다.
팔로워(비리더) 노드에서 동일한 작업을 시도하면 다음과 같은 응답이 반환됩니다.
10.0.0.3:6379> INCR counter:1
(error) MOVED 10.0.0.1:6379
따라서 클라이언트는 이 오류를 감지해 지정된 리더 노드와 연결을 다시 맺고 명령을 재시도할 수 있습니다.
그렇다면 기존 애플리케이션을 수정하고 싶지 않다면 어떻게 해야 할까요? 다행히 RedisRaft는 이 과정을 자동으로 처리하도록 설정할 수도 있습니다. 팔로워 프록시 모드(follower proxy mode)를 활성화하면 클러스터 노드가 요청을 자동으로 리더에게 전달하고, 응답이 준비되면 이를 반환합니다.
10.0.0.3:6379> RAFT.CONFIG SET follower-proxy yes
OK
10.0.0.3:6379> INCR counter:1
(integer) 2
이 방식이 훨씬 간단하지만, 비리더 노드로 요청하면 추가 네트워크 홉(hop)이 발생하므로 지연 시간과 네트워크 부하에 영향을 줄 수 있습니다.
클러스터 구성 변경
앞서 클러스터를 구축하면서 사실상 세 가지 개별 작업을 수행했습니다.
- 단일 노드에 클러스터 생성
- 두 번째 노드 추가
- 세 번째 노드 추가
RedisRaft 클러스터 구성은 고정되어 있지 않으며, 클러스터가 생성되고 운영 중인 상태에서도 노드를 추가하거나 제거할 수 있습니다.
예를 들어 세 번째 노드를 교체해야 하는 상황을 생각해 볼 수 있습니다. 먼저 교체용 새 노드를 클러스터에 참여시킵니다. 전환 과정에서 클러스터와 데이터가 중복성 저하 상태로 남지 않도록 '제거 후 추가'가 아닌 '추가 후 제거' 순서로 진행하는 것이 중요합니다.
10.0.0.4:6379> RAFT.CLUSTER JOIN 10.0.0.1:6379
OK
다음으로 세 번째 노드에 무작위로 할당된 ID를 조회합니다.
redis-cli -h 10.0.0.1 --raw RAFT.INFO | grep 10.0.0.3
node2:id=1739451728,state=connected,voting=yes,addr=10.0.0.3,port=6379,
last_conn_secs=3537,conn_errors=0,conn_oks=1
그런 다음 해당 노드를 제거합니다.
10.0.0.1:6379> RAFT.NODE REMOVE 1739451728
OK
RedisRaft 출시 현황
RedisRaft 개발 과정에서 개발팀은 Kyle Kingsbury(Aphyr로도 알려짐)와 협력해 분산 시스템의 안전성과 정확성을 검증하는 데 널리 쓰이는 Jepsen 프레임워크로 RedisRaft를 분석하고 테스트해 왔습니다. 이 협업을 통해 현재까지 공개된 분석 결과가 산출되었습니다.
아직 개발 단계에 있지만 RedisRaft의 대부분 핵심 기능은 이미 구현되어 있습니다. 현재 첫 번째 프리뷰 버전을 준비 중이며, 몇 달 안에 공개될 것으로 예상됩니다. 정식 출시 시 RedisRaft는 GNU AGPLv3 또는 Redis Source Available License(RSAL)의 듀얼 라이선스로 배포될 예정입니다.