Redis Cluster란
Redis Cluster는 데이터를 여러 노드에 자동 샤딩(Sharding)하고, 노드 장애 시 자동 페일오버를 수행하는 Redis의 내장 분산 모드입니다. Sentinel이 고가용성만 제공하는 반면, Cluster는 고가용성 + 수평 확장을 동시에 해결합니다. 16384개의 해시 슬롯을 마스터 노드에 분배하여 키를 자동으로 분산 저장합니다.
아키텍처 이해
Redis Cluster의 핵심 구성 요소:
| 구성 요소 | 역할 | 최소 수량 |
|---|---|---|
| Master 노드 | 해시 슬롯 담당, 읽기/쓰기 처리 | 3개 |
| Replica 노드 | Master 복제, 페일오버 시 승격 | 3개 (Master당 1개) |
| 해시 슬롯 | 키 분배 단위 (0~16383) | 16384개 고정 |
| Gossip 프로토콜 | 노드 간 상태 교환 (클러스터 버스) | 포트+10000 |
키의 해시 슬롯은 CRC16(key) % 16384로 결정됩니다. 예를 들어 3개 마스터라면:
- Master A: 슬롯 0~5460
- Master B: 슬롯 5461~10922
- Master C: 슬롯 10923~16383
클러스터 구축
Docker Compose로 6노드(3 Master + 3 Replica) 클러스터를 구성합니다:
# docker-compose.yml
services:
redis-1:
image: redis:7.2-alpine
command: redis-server --port 6379 --cluster-enabled yes
--cluster-config-file nodes.conf --cluster-node-timeout 5000
--appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
ports: ["6371:6379"]
volumes: ["redis-1-data:/data"]
redis-2:
image: redis:7.2-alpine
command: redis-server --port 6379 --cluster-enabled yes
--cluster-config-file nodes.conf --cluster-node-timeout 5000
--appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
ports: ["6372:6379"]
volumes: ["redis-2-data:/data"]
# redis-3 ~ redis-6 동일 패턴...
cluster-init:
image: redis:7.2-alpine
depends_on: [redis-1, redis-2, redis-3, redis-4, redis-5, redis-6]
command: >
sh -c "sleep 5 && redis-cli --cluster create
redis-1:6379 redis-2:6379 redis-3:6379
redis-4:6379 redis-5:6379 redis-6:6379
--cluster-replicas 1 --cluster-yes"
volumes:
redis-1-data:
redis-2-data:
# 클러스터 상태 확인
redis-cli -c -h redis-1 cluster info
redis-cli -c -h redis-1 cluster nodes
redis-cli -c -h redis-1 cluster slots
해시 태그로 멀티 키 연산
Redis Cluster에서 가장 중요한 제약은 멀티 키 명령은 같은 슬롯의 키만 가능하다는 점입니다. MGET, MSET, 트랜잭션(MULTI/EXEC), Lua 스크립트 모두 해당됩니다.
해시 태그(Hash Tag)로 관련 키를 같은 슬롯에 배치합니다:
# 중괄호 {} 안의 문자열로 슬롯 결정
# 아래 키들은 모두 "user:1001" 기준으로 같은 슬롯에 배치
SET {user:1001}:profile "..."
SET {user:1001}:cart "..."
SET {user:1001}:session "..."
# 같은 슬롯이므로 멀티 키 연산 가능
MGET {user:1001}:profile {user:1001}:cart {user:1001}:session
# 트랜잭션도 가능
MULTI
SET {order:5001}:status "confirmed"
SET {order:5001}:payment "paid"
EXEC
주의: 해시 태그를 과도하게 사용하면 특정 슬롯에 키가 몰리는 핫스팟(Hot Spot)이 발생합니다. 사용자별 키 그룹핑 정도가 적절합니다.
Spring Boot 연동
Spring Data Redis는 Lettuce 클라이언트를 통해 Cluster를 네이티브 지원합니다:
# application.yml
spring:
data:
redis:
cluster:
nodes:
- redis-1:6379
- redis-2:6379
- redis-3:6379
max-redirects: 3 # MOVED/ASK 리다이렉트 최대 횟수
lettuce:
cluster:
refresh:
adaptive: true # 토폴로지 변경 자동 감지
period: 30s # 주기적 토폴로지 갱신
pool:
max-active: 16
max-idle: 8
min-idle: 4
@Configuration
public class RedisClusterConfig {
@Bean
public LettuceClientConfiguration lettuceConfig() {
return LettuceClientConfiguration.builder()
.clientOptions(ClusterClientOptions.builder()
.topologyRefreshOptions(
ClusterTopologyRefreshOptions.builder()
.enablePeriodicRefresh(Duration.ofSeconds(30))
.enableAllAdaptiveRefreshTriggers()
.build()
)
.autoReconnect(true)
.build()
)
.commandTimeout(Duration.ofSeconds(5))
.build();
}
@Bean
public RedisConnectionFactory redisConnectionFactory(
RedisClusterConfiguration clusterConfig) {
return new LettuceConnectionFactory(clusterConfig, lettuceConfig());
}
}
adaptive: true는 매우 중요합니다. 페일오버 발생 시 클라이언트가 즉시 새 토폴로지를 감지하여 MOVED 에러를 최소화합니다.
NestJS(ioredis) 연동
NestJS에서는 ioredis의 Cluster 클래스를 사용합니다:
import { Module } from '@nestjs/common';
import Redis from 'ioredis';
@Module({
providers: [
{
provide: 'REDIS_CLUSTER',
useFactory: () => {
return new Redis.Cluster(
[
{ host: 'redis-1', port: 6379 },
{ host: 'redis-2', port: 6379 },
{ host: 'redis-3', port: 6379 },
],
{
scaleReads: 'slave', // 읽기를 Replica로 분산
redisOptions: {
password: process.env.REDIS_PASSWORD,
maxRetriesPerRequest: 3,
},
clusterRetryStrategy: (times) => {
if (times > 3) return null; // 3회 실패 시 포기
return Math.min(times * 200, 2000);
},
natMap: { // Docker/K8s NAT 환경
'172.17.0.2:6379': { host: 'redis-1', port: 6371 },
'172.17.0.3:6379': { host: 'redis-2', port: 6372 },
},
},
);
},
},
],
exports: ['REDIS_CLUSTER'],
})
export class RedisClusterModule {}
노드 추가·제거 (리샤딩)
클러스터에 노드를 동적으로 추가하고 슬롯을 재분배합니다:
# 새 마스터 노드 추가
redis-cli --cluster add-node new-redis:6379 redis-1:6379
# 슬롯 리샤딩 (기존 3개 → 4개 마스터로 균등 분배)
redis-cli --cluster reshard redis-1:6379
--cluster-from all
--cluster-to <new-node-id>
--cluster-slots 4096 # 16384/4 = 4096 슬롯 이동
--cluster-yes
# 새 마스터에 Replica 추가
redis-cli --cluster add-node new-replica:6379 redis-1:6379
--cluster-slave --cluster-master-id <new-node-id>
# 노드 제거 (슬롯을 먼저 비워야 함)
redis-cli --cluster reshard redis-1:6379
--cluster-from <removing-node-id>
--cluster-to <target-node-id>
--cluster-slots 5461
--cluster-yes
redis-cli --cluster del-node redis-1:6379 <removing-node-id>
리샤딩은 온라인으로 수행됩니다. 슬롯 이동 중에도 해당 키에 대한 읽기/쓰기가 가능하며, 클라이언트는 ASK 리다이렉트를 통해 자동으로 올바른 노드에 접근합니다.
페일오버 동작 원리
마스터 장애 시 자동 페일오버 프로세스:
| 단계 | 동작 | 소요 시간 |
|---|---|---|
| 1. 장애 감지 | 다른 노드들이 PFAIL 상태 표시 | cluster-node-timeout |
| 2. FAIL 합의 | 과반 노드가 PFAIL → FAIL 합의 | ~1초 |
| 3. 선거 시작 | Replica가 마스터 선출 요청 | ~0.5초 |
| 4. 투표 | 다른 마스터들이 투표 | ~0.5초 |
| 5. 승격 | Replica → Master 승격, 슬롯 인수 | 즉시 |
전체 페일오버 시간은 약 cluster-node-timeout + 2~3초입니다. 기본값 5초 기준 약 7~8초 내에 복구됩니다.
# 수동 페일오버 (유지보수 시)
redis-cli -h replica-node CLUSTER FAILOVER
# 강제 페일오버 (마스터가 응답 불가 시)
redis-cli -h replica-node CLUSTER FAILOVER FORCE
모니터링 필수 메트릭
구조화 로깅과 함께 아래 메트릭을 Prometheus로 수집합니다:
# redis_exporter로 수집하는 핵심 메트릭
redis_cluster_state # 1=ok, 0=fail
redis_cluster_slots_assigned # 할당된 슬롯 수 (16384여야 정상)
redis_cluster_slots_ok # 정상 슬롯 수
redis_cluster_known_nodes # 인식된 노드 수
redis_connected_slaves # 연결된 Replica 수
redis_memory_used_bytes # 메모리 사용량
redis_keyspace_hits_total # 캐시 히트
redis_keyspace_misses_total # 캐시 미스
# Grafana 알림 규칙
# cluster_state != 1 → 즉시 알림
# slots_ok < 16384 → 슬롯 미할당 경고
# connected_slaves < expected → Replica 이탈 경고
# memory_used > maxmemory * 0.9 → 메모리 경고
운영 체크리스트
- ✅ 최소 6노드(3 Master + 3 Replica) 구성
- ✅
cluster-node-timeout5~15초 설정 (네트워크 상황에 따라) - ✅ Lettuce/ioredis 토폴로지 자동 갱신 활성화
- ✅ 해시 태그로 관련 키 그룹핑 (멀티 키 연산 필요 시)
- ✅
maxmemory+maxmemory-policy설정 - ✅ AOF 또는 RDB 백업 활성화
- ✅ PDB 설정 (K8s 배포 시)
- ✅ cluster_state, slots_ok 메트릭 알림 설정
정리
Redis Cluster는 해시 슬롯 기반 자동 샤딩과 Gossip 프로토콜 기반 자동 페일오버를 결합하여, 단일 Redis 인스턴스의 메모리·처리량 한계를 넘어서는 유일한 공식 솔루션입니다. 핵심은 해시 태그로 멀티 키 제약을 관리하고, 클라이언트의 토폴로지 자동 갱신을 반드시 활성화하며, 리샤딩을 온라인으로 수행할 수 있다는 점입니다. Sentinel 대비 운영 복잡도는 높지만, 수평 확장이 필요한 환경에서는 필수 선택입니다.