
Redis의 고가용성(HA: High Availability) 설계를 위한 위한 Redis Sentinel에 대해 알아보자.
주니어 개발자의 Nest + BullMQ 기반 실시간 채팅의 성능/구조 개선기내가 어떤 조직에 속하게 되었을 때, 조직에서 관리하는 애플리케이션을 한 번씩 사용자 관점에서 돌아보고, 개발자 관점에서
mag1c.tistory.com
이전 포스팅에 이어서, Sentinel 구성해보자.
Redis Master + Replica 구성
먼저, Master노드와 Replica 노드를 구성해보자.
# docker-compose.yml
redis-master:
image: redis:latest
command: redis-server
container_name: "redis-master"
networks:
- redis-net
redis-replica-1:
image: redis:latest
command: redis-server --replicaof redis-master 6379
links:
- redis-master
container_name: "redis-replica-1"
networks:
- redis-net
redis-replica-2:
image: redis:latest
command: redis-server --replicaof redis-master 6379
links:
- redis-master
container_name: "redis-replica-2"
networks:
- redis-net
Sentinel 구성
Sentinel은 Redis와는 별도의 구성으로, Redis Sentinel 문서에서 권장하는 최소 Sentinel인 3대의 Sentinel을 띄워 quorum을 만족시키도록 구성했다. 위 포스팅에서도 언급한 바 있지만, Sentinel은 failover 시 quorum을 만족해야 마스터를 전환할 수 있는데, 2개 이상의 Sentinel이 동의해야 객관적 장애(odown)로 판단할 수 있기 때문이다.
# Dockerfile
FROM redis:latest
EXPOSE 26379
ADD sentinel.conf /etc/redis/sentinel.conf
RUN mkdir -p /var/lib/redis && \
chmod 777 /var/lib/redis && \
chown redis:redis /etc/redis/sentinel.conf
COPY sentinel-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/sentinel-entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
#entrypoint.sh
#!/bin/sh
sed -i "s/\$SENTINEL_QUORUM/$SENTINEL_QUORUM/g" /etc/redis/sentinel.conf
sed -i "s/\$SENTINEL_DOWN_AFTER/$SENTINEL_DOWN_AFTER_MS/g" /etc/redis/sentinel.conf
sed -i "s/\$SENTINEL_FAILOVER/$SENTINEL_FAILOVER_TIMEOUT/g" /etc/redis/sentinel.conf
exec docker-entrypoint.sh redis-server /etc/redis/sentinel.conf --sentinel
# sentinel.conf
# Example sentinel.conf can be downloaded from http://download.redis.io/redis-stable/sentinel.conf
port 26379
dir /tmp
# 도커 서비스명을 hostname으로 인식,
# 해당 설정이 없으면 `Can't resolve master instance hostname` 오류 발생.
sentinel resolve-hostnames yes
# master redis를 감시.
sentinel monitor mymaster redis-master 6379 $SENTINEL_QUORUM
# 장애 간주 시간 설정 (MS)
sentinel down-after-milliseconds mymaster $SENTINEL_DOWN_AFTER_MS
# 새로운 master가 된 redis에 동기화할 수 있는 slave(repl) 제한
sentinel parallel-syncs mymaster 1
# failover과정 전체의 timeout
sentinel failover-timeout mymaster $SENTINEL_FAILOVER_TIMEOUT
bind 0.0.0.0
SENTINEL_DOWN_AFTER_MS=5000
SENTINEL_FAILOVER_TIMEOUT=500
SENTINEL_QUORUM=2
sentinel.conf를 컨테이너로 복사하고, entrypoint.sh에서 환경변수를 가지고 실시간 구성 파일을 만들어, Sentinel을 실행시킨다.
이 때, reslove-hostnames yes 는 도커 환경에서 서비스명을 호스트로 인식하게 하는 명령어이니, 서비스명을 사용하기 위해 반드시 우선적으로 입력해야한다.
위 코들을 바탕으로 전체 docker-compose 파일은 다음과 같이 구성된다.
version: "3.8"
services:
redis-master:
image: redis:latest
command: redis-server
container_name: "redis-master"
networks:
- redis-net
redis-replica-1:
image: redis:latest
command: redis-server --replicaof redis-master 6379
links:
- redis-master
container_name: "redis-replica-1"
networks:
- redis-net
redis-replica-2:
image: redis:latest
command: redis-server --replicaof redis-master 6379
links:
- redis-master
container_name: "redis-replica-2"
networks:
- redis-net
sentinel-1:
build: sentinel
ports:
- "26379:26379"
env_file:
- .env
depends_on:
- redis-master
- redis-replica-1
- redis-replica-2
container_name: "sentinel1"
networks:
- redis-net
sentinel-2:
build: sentinel
ports:
- "26380:26379"
env_file:
- .env
depends_on:
- redis-master
- redis-replica-1
- redis-replica-2
container_name: "sentinel2"
networks:
- redis-net
sentinel-3:
build: sentinel
ports:
- "26381:26379"
env_file:
- .env
depends_on:
- redis-master
- redis-replica-1
- redis-replica-2
container_name: "sentinel3"
networks:
- redis-net
networks:
redis-net:
driver: bridge
장애 유도 및 Failover 확인하기
위의 도커 구성대로 컨테이너를 구성한 후, 실제 장애를 발생시켜보았다.
$ docker stop redis-master
1. SDOWN
Sentinel들은 주기적으로 Master 노드에 PING을 보낸다. 일정 시간 응답이 없으면 Master을 SDOWN으로 판단한다. 여기서 일정 시간은, 위에서 설정한 DOWN_AFTER_MS 값이다.
2. ODOWN
여러 Sentinel이 SDOWN 상태를 보고하면, Quorum 수 이상의 Sentinel이 동의했을 때, ODOWN으로 승격된다.
3. 리더 Sentinel 선출
Failover을 수행할 리더 Sentinel을 선출한다. 이 과정은 투표 기반으로 진행되며, Sentinel은 자신이 리더가 되겠다는 요청을 다른 Sentinel에게 보내고 과반 투표를 받는다.
4. 새로운 Master 선택
리더 Sentinel은 Replica 중 하나를 Master로 승격시킨다.
선택된 Replica는 MASTER MODE로 전환된다.
새로운 Master가 선출됨에 따라 나머지 Replica를 재구성하고, 새로운 Master의 정보를 다른 Sentinel과 클라이언트에 전파한다.
기타
장애 복구 중간중간에 아래와 같은 에러 로그가 반복적으로 출력되는 것을 볼 수 있다.
Failed to resolve hostname 'redis-master'
이는 Sentinel 설정에서 호스트명을 컨테이너명을 사용했기 때문인데, redis-master 컨테이너가 완전히 종료되는 상황을 가정했기 때문에, 도커 내부 DNS - Hostname의 해석이 불가능해서이다. 실제 내부 IP로 바인딩하는 등의 처리로 해결할 수 있다.
마무리
Sentinel의 Failover의 과정을 따라 HA를 보장하는 것을 확인했다. 실제 장애를 유도하고 Failover로그를 추적하며 이전 포스팅에서 개념적으로 정리했던 Sentinel의 동작 원리를 간단하게 살펴보고 이해할 수 있었다.
다음 포스팅에서는 Sentinel Notificiations을 적용해보고, 장애 발생 시 정상적으로 알림을 발생시킬 수 있는지 확인해보려고 한다. 이 포스팅들을 기반으로, 실제 업무에 Sentinel을 좀 더 견고하게 적용시킬 수 있을 것으로 기대한다.
diehreo@gmail.com
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!