Redis 단일 인스턴스의 한계: 왜 고가용성이 필요한가
Redis를 단일 인스턴스로 운영하면 해당 노드가 다운되는 순간 전체 서비스가 캐시 없이 동작하거나, 세션/큐 데이터가 유실됩니다. 이를 해결하기 위해 Redis는 두 가지 고가용성 솔루션을 제공합니다:
| 솔루션 | 목적 | 데이터 분산 | 자동 페일오버 |
|---|---|---|---|
| Redis Sentinel | 고가용성 (HA) | 없음 (단일 마스터) | ✅ |
| Redis Cluster | 수평 확장 + HA | 있음 (16384 해시 슬롯) | ✅ |
이 아티클에서는 Redis Sentinel에 집중합니다. 데이터가 단일 마스터에 수용 가능한 규모(수십 GB 이하)이고, 수평 분산보다 자동 페일오버와 운영 단순성이 중요한 환경에서 Sentinel이 적합합니다.
Sentinel의 세 가지 역할
Redis Sentinel은 독립 프로세스로 실행되며 세 가지 핵심 기능을 수행합니다:
- 모니터링(Monitoring): 마스터와 레플리카가 정상 동작하는지 주기적으로 확인합니다.
- 알림(Notification): 모니터링 중 이상이 감지되면 관리자에게 알리거나 스크립트를 실행합니다.
- 자동 페일오버(Automatic Failover): 마스터가 다운되면 레플리카 중 하나를 새 마스터로 승격시키고, 나머지 레플리카와 클라이언트에게 새 마스터를 알립니다.
┌──────────┐ ┌──────────┐ ┌──────────┐
│Sentinel 1│ │Sentinel 2│ │Sentinel 3│
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
┌──────────┴─────────────┴──────────────┴───────┐
│ │
┌────▼────┐ ┌──────────┐ ┌──────────┐
│ Master │──복제────▶│Replica 1 │ │Replica 2 │
│ :6379 │──복제────▶│ :6380 │ │ :6381 │
└─────────┘ └──────────┘ └──────────┘
최소 구성: Sentinel 3대가 필요한 이유
Sentinel은 과반수 합의(quorum)로 페일오버를 결정합니다. Sentinel 2대로 운영하면 네트워크 파티션 시 과반수를 확보할 수 없어 페일오버가 불가능합니다.
| Sentinel 수 | quorum (최소) | 허용 장애 수 | 권장 여부 |
|---|---|---|---|
| 1 | 1 | 0 (SPOF) | ❌ 사용 금지 |
| 2 | 2 | 0 (과반수 불가) | ❌ 부족 |
| 3 | 2 | 1 | ✅ 최소 권장 |
| 5 | 3 | 2 | ✅ 높은 안정성 |
핵심 규칙: Sentinel 수는 항상 홀수로 설정합니다. quorum은 ceil(N/2)로 설정하는 것이 일반적입니다.
Sentinel 설정 파일
# sentinel.conf
# Sentinel 자체 포트
port 26379
# 모니터링할 마스터 정의
# sentinel monitor <master-name> <ip> <port> <quorum>
sentinel monitor mymaster 10.0.0.1 6379 2
# 마스터가 응답하지 않으면 SDOWN 판정 (밀리초)
sentinel down-after-milliseconds mymaster 5000
# 페일오버 후 새 마스터에 동시 동기화할 레플리카 수
sentinel parallel-syncs mymaster 1
# 페일오버 타임아웃 (밀리초)
sentinel failover-timeout mymaster 60000
# Redis 인증 (마스터/레플리카에 requirepass 설정 시)
# sentinel auth-pass mymaster your-redis-password
# Sentinel 간 인증 (Sentinel도 보호할 때)
# requirepass sentinel-password
핵심 파라미터 설명
| 파라미터 | 기본값 | 설명 |
|---|---|---|
down-after-milliseconds |
30000 (30초) | 이 시간 동안 PING 응답이 없으면 SDOWN (주관적 다운) |
parallel-syncs |
1 | 페일오버 후 동시에 새 마스터와 동기화하는 레플리카 수. 높으면 빠르지만 동기화 중 레플리카 서비스 불가 |
failover-timeout |
180000 (3분) | 페일오버 전체 과정의 타임아웃. 이 시간 초과 시 다른 Sentinel이 재시도 |
페일오버 과정: SDOWN → ODOWN → 투표 → 승격
마스터 장애 시 Sentinel의 페일오버는 4단계로 진행됩니다:
1단계: SDOWN (Subjectively Down)
개별 Sentinel이 down-after-milliseconds 동안 마스터에서 응답을 받지 못하면, 해당 Sentinel은 마스터를 주관적으로 다운이라 판단합니다. 이 시점에서는 아직 페일오버가 시작되지 않습니다.
2단계: ODOWN (Objectively Down)
SDOWN을 감지한 Sentinel이 다른 Sentinel에게 확인을 요청합니다. quorum 이상의 Sentinel이 마스터가 다운되었다고 동의하면 ODOWN(객관적 다운) 상태로 전환됩니다.
3단계: 리더 선출
Sentinel들 중 하나가 페일오버를 주도할 리더로 선출됩니다. Raft 알고리즘 변형을 사용하며, 과반수의 투표가 필요합니다.
4단계: 레플리카 승격
리더 Sentinel이 최적의 레플리카를 선택하여 새 마스터로 승격합니다. 선택 기준:
- replica-priority: 낮을수록 우선 (0은 승격 불가)
- replication offset: 높을수록 우선 (데이터가 더 최신)
- Run ID: 사전순으로 작은 것 우선 (동점 처리)
# 페일오버 과정 모니터링
redis-cli -p 26379 SENTINEL get-master-addr-by-name mymaster
# 1) "10.0.0.1"
# 2) "6379"
# 마스터 다운 후 다시 확인
redis-cli -p 26379 SENTINEL get-master-addr-by-name mymaster
# 1) "10.0.0.2" ← 새 마스터로 변경됨
# 2) "6380"
클라이언트 연결: Sentinel을 통한 마스터 디스커버리
Sentinel 환경에서 클라이언트는 마스터의 IP:Port를 직접 지정하지 않고, Sentinel에게 현재 마스터를 질의하여 연결합니다. 이렇게 해야 페일오버 후 새 마스터로 자동 전환됩니다.
Node.js (ioredis)
import Redis from 'ioredis';
const redis = new Redis({
sentinels: [
{ host: '10.0.0.10', port: 26379 },
{ host: '10.0.0.11', port: 26379 },
{ host: '10.0.0.12', port: 26379 },
],
name: 'mymaster', // sentinel monitor에서 설정한 마스터 이름
// password: 'redis-password', // Redis 인증
// sentinelPassword: 'sentinel-pw', // Sentinel 인증
db: 0,
retryStrategy(times) {
return Math.min(times * 200, 5000); // 재연결 백오프
},
});
Spring Boot (Lettuce)
# application.yml
spring:
data:
redis:
sentinel:
master: mymaster
nodes:
- 10.0.0.10:26379
- 10.0.0.11:26379
- 10.0.0.12:26379
# password: redis-password
timeout: 5000ms
NestJS (BullMQ)
// BullMQ + Sentinel 연결
BullModule.forRoot({
connection: {
sentinels: [
{ host: '10.0.0.10', port: 26379 },
{ host: '10.0.0.11', port: 26379 },
{ host: '10.0.0.12', port: 26379 },
],
name: 'mymaster',
maxRetriesPerRequest: null, // BullMQ 필수
},
})
읽기 분산: 레플리카 읽기 설정
Sentinel 환경에서 쓰기는 반드시 마스터로 가지만, 읽기는 레플리카로 분산할 수 있습니다.
// ioredis: preferredSlaves로 읽기 분산
const redis = new Redis({
sentinels: [...],
name: 'mymaster',
role: 'slave', // 레플리카에 연결
preferredSlaves: [
{ ip: '10.0.0.2', port: '6380', prio: 1 },
],
});
주의: 레플리카 읽기는 비동기 복제 지연으로 인해 약간의 stale 데이터를 읽을 수 있습니다. 강한 일관성이 필요한 조회(결제 상태, 재고 확인)는 반드시 마스터에서 읽어야 합니다.
Sentinel 명령어 레퍼런스
# Sentinel 접속
redis-cli -p 26379
# 모니터링 중인 마스터 정보
SENTINEL masters
# 특정 마스터의 상세 정보
SENTINEL master mymaster
# 마스터의 레플리카 목록
SENTINEL replicas mymaster
# 다른 Sentinel 목록
SENTINEL sentinels mymaster
# 현재 마스터 주소
SENTINEL get-master-addr-by-name mymaster
# 수동 페일오버 트리거 (운영 점검 시)
SENTINEL failover mymaster
# 마스터가 정상인데 강제로 재설정
SENTINEL reset mymaster
주요 명령어 비교
| 명령어 | 용도 | 사용 시점 |
|---|---|---|
SENTINEL masters |
모든 마스터 상태 확인 | 일상 모니터링 |
SENTINEL replicas |
레플리카 상태·오프셋 확인 | 복제 지연 진단 |
SENTINEL failover |
수동 페일오버 | 계획된 마스터 점검 시 |
SENTINEL reset |
마스터 감시 재설정 | 레플리카 추가/제거 후 정리 |
Sentinel 이벤트와 알림 스크립트
Sentinel은 페일오버 등 주요 이벤트 발생 시 외부 스크립트를 실행할 수 있습니다.
# sentinel.conf
sentinel notification-script mymaster /opt/redis/notify.sh
sentinel client-reconfig-script mymaster /opt/redis/reconfig.sh
#!/bin/bash
# /opt/redis/notify.sh — 페일오버 알림
# 인수: <event-type> <event-description>
EVENT_TYPE=$1
EVENT_DESC=$2
curl -s -X POST "https://hooks.slack.com/services/xxx"
-H 'Content-Type: application/json'
-d "{"text":"[Redis Sentinel] ${EVENT_TYPE}: ${EVENT_DESC}"}"
#!/bin/bash
# /opt/redis/reconfig.sh — 페일오버 완료 후 실행
# 인수: <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>
MASTER_NAME=$1
ROLE=$2
NEW_IP=$6
NEW_PORT=$7
echo "$(date) Failover: ${MASTER_NAME} new master is ${NEW_IP}:${NEW_PORT}"
>> /var/log/redis/failover.log
Sentinel vs Cluster: 선택 기준
| 기준 | Redis Sentinel | Redis Cluster |
|---|---|---|
| 데이터 규모 | 단일 마스터에 수용 가능 (수십 GB) | 수백 GB 이상, 수평 분산 필요 |
| 쓰기 처리량 | 단일 마스터의 처리량 | 여러 마스터에 분산 |
| 운영 복잡성 | 낮음 | 높음 (해시 슬롯, 리샤딩) |
| MULTI/EXEC | 제한 없음 | 같은 슬롯 키만 가능 |
| Lua 스크립트 | 제한 없음 | 같은 슬롯 키만 접근 가능 |
| Pub/Sub | 정상 동작 | 모든 노드에 브로드캐스트 (비효율) |
| 최소 Redis 인스턴스 | 3 (1 master + 2 replica) | 6 (3 master + 3 replica) |
판단 기준: 데이터가 단일 노드 메모리에 수용 가능하고, MULTI/EXEC·Lua 스크립트를 자유롭게 사용해야 한다면 Sentinel. 데이터가 단일 노드를 초과하거나 쓰기 처리량 분산이 필요하면 Cluster를 선택합니다.
운영 함정과 Best Practice
- Sentinel을 Redis 서버와 같은 노드에 배치하지 마세요: Redis 서버가 죽으면 Sentinel도 함께 죽어 quorum을 확보할 수 없습니다. Sentinel은 별도 노드 또는 애플리케이션 서버에 배치합니다.
- down-after-milliseconds를 너무 낮게 설정하지 마세요: 네트워크 일시적 지연으로 불필요한 페일오버가 발생합니다. 프로덕션에서는 5000~10000ms가 적절합니다.
- parallel-syncs = 1 유지: 이 값을 높이면 여러 레플리카가 동시에 새 마스터와 전체 동기화를 시작하여, 동기화 중 읽기 서비스가 불가능한 레플리카가 늘어납니다.
- min-replicas-to-write 설정: 마스터에
min-replicas-to-write 1과min-replicas-max-lag 10을 설정하면, 레플리카가 모두 다운되었을 때 마스터가 쓰기를 거부하여 데이터 유실을 줄입니다. - NAT/Docker 환경 주의: Sentinel은 Redis INFO 명령으로 레플리카의 IP를 파악합니다. NAT 뒤에 있으면 내부 IP가 노출되어 클라이언트가 연결하지 못합니다.
replica-announce-ip와sentinel announce-ip를 명시적으로 설정하세요.
# redis.conf (마스터/레플리카)
min-replicas-to-write 1 # 최소 1개 레플리카 연결 시만 쓰기 허용
min-replicas-max-lag 10 # 레플리카 복제 지연 10초 이내
# Docker/NAT 환경
replica-announce-ip 203.0.113.10
replica-announce-port 6379
# sentinel.conf — Docker/NAT 환경
sentinel announce-ip 203.0.113.20
sentinel announce-port 26379
운영 모니터링 체크리스트
| 모니터링 항목 | 확인 방법 | 임계값 |
|---|---|---|
| Sentinel quorum 유지 | SENTINEL ckquorum mymaster |
OK 응답이어야 함 |
| 마스터 상태 | SENTINEL master mymaster의 flags |
“master”만 표시 |
| 레플리카 복제 지연 | INFO replication의 lag |
10초 이하 |
| 페일오버 횟수 | SENTINEL master mymaster의 num-other-sentinels, failover-count |
비정상적 증가 시 알림 |
| Sentinel 간 연결 | SENTINEL sentinels mymaster |
모든 Sentinel이 조회되어야 함 |
핵심 정리
- Sentinel은 3대 이상 홀수로 배포하고, quorum은 과반수로 설정합니다.
- 페일오버는 SDOWN → ODOWN → 리더 선출 → 레플리카 승격 4단계로 진행되며, 전체 과정은 보통 수 초 내 완료됩니다.
- 클라이언트는 Sentinel을 통해 마스터를 디스커버리해야 페일오버 시 자동 전환됩니다. IP를 직접 지정하면 안 됩니다.
- min-replicas-to-write를 설정하여 네트워크 파티션 시 데이터 유실을 줄이세요.
- 데이터 규모가 단일 마스터를 초과하거나 쓰기 분산이 필요하면 Redis Cluster로 전환을 검토합니다.
참고 자료