
Redis HA란?
Redis HA(High Availability)란 시스템에 장애가 발생하는 상황에서도 Redis 서비스의 지속적인 가용성을 보장하는 구성입니다. Redis는 일반적인 DBMS와 다르게 디스크가 아닌 메모리에 데이터를 저장하기 때문에 조회 성능에서 매우 빠른 성능을 보이지만, 메모리에 데이터를 저장되기에 데이터가 휘발될 수 있는 단점이 있습니다. 이 단점을 보완하고자 Redis HA는 다수의 Redis 서버들을 배포하여 장애 상황을 극복합니다.
사내에서 빠른 데이터 송수신 구조와 안정성을 확보한 데이터 처리를 목표로 하는 요구 사항이 있었습니다. 빠른 데이터 송수신을 위해 Redis의 Pub/Sub 기능을 사용하고 안정성을 위해서 Redis HA를 구성했습니다. 이번 블로그에서는 Redis HA를 도입한 과정에 대해서 설명하겠습니다.
Redis HA의 구성 요소
Redis HA는 크게 다음 3가지 구성 요소로 구성됩니다: Replication, Failover, Persistence 이 3가지 요소로 Redis HA는 고가용성을 확보합니다.
1. 복제 (Replication)
- Master-Slave 구조: 하나의 Master 노드와 하나 이상의 Slave(Replica) 노드로 구성
- 데이터 동기화: Master에서 발생한 쓰기 작업이 모든 Slave에 실시간으로 비동기적으로 복제됨
- 읽기 부하 분산: Slave 노드들을 통해 읽기 작업을 분산 처리하여 성능 향상
2. 자동 장애 복구 (Failover): 장애 복구를 지원하는 매케니즘
- Sentinel: Redis의 장애 감지 및 자동 복구 시스템
- Master 노드의 상태를 지속적으로 모니터링
- Master 장애 감지 시 자동으로 Slave 중 하나를 새로운 Master로 승격
- 클라이언트에게 새로운 Master 정보 제공
- 보통 최소 3개의 Sentinel 인스턴스를 권장 -> 쿼럼 기반 의사결정을 하기 위함
- Redis Cluster: 샤딩과 HA를 동시에 제공하는 솔루션
- 데이터를 여러 노드에 분산 저장(샤딩)
- 각 샤드마다 Master-Slave 구조로 복제 제공
- 자체적인 장애 감지 및 자동 복구 메커니즘 내장
3. 데이터 지속성(Persistence)
- RDB: 특정 시점의 스냅샷을 저장 (더 빠르지만 일부 데이터 손실 가능)
- AOF: 모든 쓰기 명령을 로그에 기록 (더 안전하지만 파일 크기가 크고 느림)
- 혼합 모드: RDB와 AOF를 함께 사용하여 장점 결합
Redis HA 작동 원리
Redis HA를 구성하는 방식은 크게 두 가지가 있습니다: Redis Sentinel, Redis Cluster
제가 사용한 방식은 Redis Sentinel을 이용한 방식이기에 Redis Sentinel을 기준으로 설명드리겠습니다. Redis Sentinel(보초)은 마스터 노드와 슬레이브 노드를 실시간으로 모니터링하며 장애 상황 발생 시 Slave 노드를 실시간으로 모니터링하며, 슬레이브 노드를 마스터로 승격시키는 자동 failover 작업을 수행하는 역할을 합니다. 따라서, 아래와 같이 애플리케이션은 마스터 노드를 직접 바라보는 것이 아닌, Sentinel을 통해 Redis 클러스터를 바라봅니다. 애플리케이션은 Sentinel을 통해 마스터 노드의 IP와 Port를 획득해 마스터 노드를 접근하는 형태입니다. 즉, Sentinel이 마스터 노드로 접근하기 위한 일종의 프록시 역할을 합니다.

- Sentinel을 통한 failover 처리단계
- Sentinel 중 하나가 Master 노드의 장애를 감지
- 다른 Sentinel들에게 failover 진행 여부를 투표
- 정족수 이상의 Sentinel이 failover에 동의한 경우, failover를 진행 -> 3개 이상 홀수 개의 Sentinel이 필요
- Master 노드에 대한 연결을 끊음
- Slave 중 하나를 Master로 승격
- 승격된 Slave 노드 외의 다른 모든 Slave 노드를 새로 승격된 Master 노드에 연결
- 애플리케이션의 요청에 대해 새로 승격된 마스터 노드의 IP, PORT 를 반환
- 이후 (전)Master 노드가 복구가 되면 Slave로 Redis HA에 접속함
이전 블로그에서 데이터베이스를 다중화했을 때, 트래픽은 더 받을 수 있지만, 하나의 데이터베이스 장애 상황에 대해서 failover 기능이 없어서 오히려 장애 상황에 더 취약한 구조가 아닌가에 대한 고민이 있었습니다.
하지만, Redis HA에서는 다중화된 Redis 서버에 대한 failover를 기본적으로 지원하기 때문에, 하나의 노드에 장애가 발생하더라도 자동으로 마스터를 전환하고 클라이언트가 새로운 마스터에 연결할 수 있도록 해줍니다. 따라서 별도의 failover 로직을 애플리케이션 차원에서 구현하지 않아도 고가용성을 확보할 수 있다는 점에서, Redis는 장애 상황에 더 유연하게 대응할 수 있는 구조를 제공합니다. Failover가 진행되는 실제 과정은 아래에서 살펴보겠습니다.
Redis HA 도입기
우선 Redis HA의 전체적인 구성은 다음과 같습니다. 일반적으로 하나의 노드에 장애가 발생할 경우를 대비해, 최소 3개의 노드를 구성하고 각 노드에 Redis Server 1개와 Redis Sentinel 1개씩을 배치하는 것이 권장됩니다. 하지만 현재는 사내에서 단일 노드만 제공받을 수 있었기 때문에, Docker Swarm 클러스터가 구축된 하나의 노드 위에 Redis HA를 다음과 같은 방식으로 구성해보았습니다.

Redis HA를 구축하는 흐름은 다음과 같습니다. 이중에서 3번, 6번, 7번 과정에 대해서 설명드리겠습니다.
- Redis Master Server 6379 포트에 배포
- Redis Slave Server 2개를 각각 6380 포트와 6381 포트에 배포함
- Redis Master <-> Redis Slave 간의 복제 여부를 확인
- Redis Sentinel Server 3개를 각각 26379, 26380, 26381 포트에 배포
- Redis Sentinel Server들끼리 서로를 잘 인식하는지 확인
- Redis Sentinel이 Redis Server를 잘 모니터링하는지 확인
- Failover가 잘 되는지 확인
<3. Redis Master <-> Redis Slave 간의 복제 여부를 확인>

우선, Sentinels를 구성하기에 앞서 Redis Master 1개와 Redis Slave 2개를 구성해보았습니다.

위에서 배포된 Redis Server들을 확인해볼 수 있습니다. 제가 Redis Server들을 배포하면서 들었던 의문점은 Redis Slave 배포 스크립트에, 6380 포트와 6381 포트만 열어줬지만, 6379/tcp가 추가적으로 열려 있다는 사실이었습니다. 그 이유에 대해서 찾아본 결과 다음과 같았습니다.
Redis Master-Slave 복제 환경에서는 모든 Redis 인스턴스(Master와 Slave들)가 컨테이너 내부에서 기본 포트인 6379를 사용합니다.
컨테이너들은 Overlay 네트워크 상에서 각각 고유한 IP 주소(예: 172.20.20.89, 172.20.20.93)를 가지기 때문에, 동일한 포트를 사용하더라도 충돌이 발생하지 않습니다. 따라서, Slave들은 각자의 IP 주소를 통해 6379 포트로 Master에 연결되어 복제를 수행합니다.
위에서 명시된 6380 포트와 6381 포트는 외부에서 각 Redis 인스턴스에 접근할 때 사용되는 포트 매핑입니다. Master는 6379 포트를 그대로 사용하고, Slave 인스턴스들은 각각 6380과 6381 포트로 매핑되어 외부에서 독립적으로 접근할 수 있습니다. 이 포트 매핑은 Docker Swarm의 포트 퍼블리싱(port publishing) 기능을 통해 설정되며, 내부 통신은 모두 6379 포트를 기반으로 하고 외부 통신은 서로 다른 포트를 통해 이루어지는 구조입니다.

다음과 같이 Redis Master를 조회했을 때, Redis Slave가 잘 연결되어 있으면 6379 포트를 통해 복제가 이뤄지고 있음을 볼 수 있습니다. 추후에 이 설정 때문에 Redis Sentinel의 모니터링 과정에서 문제가 생깁니다.

위에서 보이는 것처럼 Master에 입력한 값 (key: test_key, value: success) 이 Slave에도 잘 나타나 복제가 성공적으로 이뤄지고 있는 것을 볼 수 있습니다.
<6. Redis Sentinel이 Redis Server를 잘 모니터링하는지 확인>

Redis Master와 Redis Slave가 성공적으로 배포된 후에 Redis Sentinel 3개를 각각 26379 포트, 26380 포트, 26381 포트에 각각 배포했습니다. 하지만, 아래와 같이 Redis Sentinel이 Redis Master는 잘 모니터링하고 있지만, Redis Slave와 연결이 되고 1분 후에 Redis Slave와 연결이 끊기는 것(+sdown)을 확인할 수 있습니다.

이 문제의 원인은 Sentinel이 Slave 정보를 직접 확인하는 것이 아니라 Master를 통해 가져오기 때문입니다.

현재 설정에서는 Redis Slave 서버에 별도의 포트 설정을 하지 않았기 때문에, Master는 Slave들이 기본 포트인 6379로 운영되고 있다고 인식합니다. 따라서, Sentinel도 Slave 서버들이 172.20.20.127:6379, 172.20.20.129:6379에 있다고 잘못 판단하게 됩니다. 하지만, Redis Slave가 실제로는 각각 6380, 6381 포트를 사용해 외부와 통신하고 있었기 때문에, Sentinel이 올바르게 Slave 상태를 확인하지 못하고 다운된 것으로 판단한 것입니다.
이 문제를 해결하기 위해, Slave 인스턴스가 Master에게 자신이 사용하는 외부 통신 포트를 명시적으로 알리도록 설정을 추가했습니다. 각각의 Docker Stack 파일(docker-stack.server2.yml, docker-stack.server3.yml)에 다음과 같은 설정을 넣었습니다:
REDIS_EXTRA_FLAGS=--port 6380 --maxmemory 200mb --slave-announce-port 6380
REDIS_EXTRA_FLAGS=--port 6381 --maxmemory 200mb --slave-announce-port 6381

이를 통해 Slave가 Master에게 정확한 포트 정보를 전달할 수 있게 되었고, Sentinel도 올바르게 Slave를 모니터링할 수 있게 되었습니다. 저는 Redis Sentinel도 Redis Slave와 내부 포트인 6379 포트를 사용하여 통신한다고 생각했지만, 실제로는 외부 포트 매핑을 통해 통신한다는 사실을 알게 되었습니다.
<7. Failover가 잘 되는지 확인>
Redis HA를 Sentinels를 사용하여 구성한 후에 실제로 Redis Sentinel이 장애 상황을 감지하고 Slave를 Master로 승격시키는 Failover이 동작하는지 확인하기 위해 Redis Master가 실행되고 있는 컨테이너를 다운시켰습니다. Master를 다운시킨 후에 확인한 Sentinel들의 로그는 다음과 같습니다.

- Sentinel 1이 마스터(172.20.20.124:6379)가 응답하지 않는다고 판단해 +sdown(subjectively down) 상태로 전환
- Sentinel들이 새로운 에포크(epoch)를 시작 -> 이는 Failover 프로세스가 시작되었음을 나타냄
- Sentinel 1이 리더 선출을 위해 특정 Sentinel에게 투표 -> Failover 과정에서 리더 역할을 하는 Sentinel 선정
- 3개 Sentinel 중 2개 이상(quorum=2)이 마스터 다운에 동의해 +odown(objectively down) 상태로 전환
- 마스터가 172.20.20.124:6379에서 172.20.20.2:6380(기존 슬레이브)로 전환
- 기존 슬레이브(172.20.20.6:6381)가 새 마스터(172.20.20.2:6380)의 슬레이브로 연결.

- Sentinel 2가 마스터(172.20.20.124:6379)가 응답하지 않는다고 판단해 +sdown(subjectively down) 상태로 전환
- Sentinel 2가 리더 선출을 위해 특정 Sentinel에게 투표
- 마스터가 172.20.20.124:6379에서 172.20.20.2:6380(기존 슬레이브)로 전환
- 기존 슬레이브(172.20.20.6:6381)가 새 마스터(172.20.20.2:6380)의 슬레이브로 연결.

- Sentinel 3이 마스터(172.20.20.124:6379)가 응답하지 않는다고 판단해 +sdown(subjectively down) 상태로 전환
- 2개 이상의 Sentinel이 마스터 다운에 동의해 +odown(objectively down) 상태로 전환. 2/2는 quorum(2)을 충족했음을 나타냄
- Sentinel들이 새로운 에포크(epoch)를 시작. Failover 프로세스가 본격적으로 진행됨
- Sentinel 3이 Failover를 시도 시작.
- Sentinel 3이 자신을 리더로 투표. -> Sentinel 3이 리더로 선출됨(+elected-leader).
- 리더(Sentinel 3)가 새 마스터로 승격할 슬레이브를 선택 -> 172.20.20.2:6380 선택
- SLAVEOF NO ONE 명령을 172.20.20.2:6380에 전송해 마스터로 승격 요청.
- 나머지 슬레이브(172.20.20.6:6381)를 새 마스터(172.20.20.2:6380)에 연결하도록 재구성.
- Failover 프로세스 종료. 마스터가 172.20.20.124:6379에서 172.20.20.2:6380으로 전환.

Failover 진행 후에 6380 포트에서 실행 중인 Slave가 Master로 승격되었음을 확인 할 수 있습니다. 또한, 이전에 Master인 6379 포트에서 실행 중이던 Redis 컨테이너를 재기동 시키니 Slave로 다시 접속되었음을 확인할 수 있습니다.
Redis HA 개선점
현재는 하나의 노드에서 모든 Redis 컨테이너들이 실행되고 있습니다. 이는 컨테이너 실패 상황에 대해서 Failover가 실행될 수 있으나, 노드가 실패한 경우에는 Redis HA 전체가 다운되는 Single Point of Failure가 될 수 있습니다.

이를 개선하기 위해서 위와 같이 3개의 노드로 구성된 클러스터를 구축하여, 각각의 노드에 Redis Server 1개와 Redis Sentinel 1개를 배치하는 전략을 취할 수 있습니다.
참고자료
https://seongonion.tistory.com/168
Redis의 HA(High Availability) 전략 알아보기!
Redis(이하 레디스)는 Key-Value 형태의 데이터를 저장할 수 있는 일종의 NoSQL 데이터베이스 중 하나이다. 레디스는 특히 일반적인 DBMS 시스템과 다르게 디스크가 아닌 메모리에 데이터를 저장하기
seongonion.tistory.com
'Infra' 카테고리의 다른 글
| [Security] Wireguard VPN으로 내 EC2 지키기 (0) | 2025.11.08 |
|---|---|
| [Infra] 실무에서 사용해본 유용한 Docker Swarm 기능들 (6) | 2025.06.26 |
| [CI/CD] 배포에 관한 생각 (0) | 2025.04.05 |
| [Infra] 모니터링 서버 구축 (feat. Prometheus, Grafana) (3) | 2024.12.21 |
| [Infra] 데이터베이스 다중화 (1) | 2024.12.20 |