본문 바로가기
DataBase

2. Redis, Sentinel 고가용성을 위한 마스터/슬레이브 구성!

by 맑은안개 2023. 5. 26.

관련글

2023.04.17 - [DB] - 1. Redis란? Docker 설치와 함께 알아보기

Redis 마스터 / 슬레이브의 필요성

전 블로그에서는 Redis Standalone(Single Node)구성을 살펴봤습니다. 단일노드로 구성된 레디스는 장애 발생시 서비스가 중단되는 단점이 있습니다. 이를 해결하기 위해 마스터 / 슬레이브구성이 필요합니다.

출처: https://rtfm.co.ua/

마스터 / 슬레이브 장점

1. 고가용성(Failover)

마스터 노드가 장애로 중단되는 경우에도 계속해서 서비스를 제공할 수 있다.(무중단서비스)

2. 확장성

다중 슬레이브 노드에 데이터를 분산하고, 읽기 작업에 대한 처리를 위임함으로써 빠른 응답속도를 확보할 수 있다.

3. 데이터 복제 및 복구

실시간으로 데이터가 각 슬레이브 노드에 복제되므로 마스터 노드의 데이터 손실에 대응할 수 있다.

고려사항

  1. 다중 노드 특성상 충분한 네트워크 대역폭이 확보되어야 합니다. 단일 서버 구성이 아닌 경우, 각 서버의 하드웨어 성능등을 충분히 고려해야 합니다.
  2. 각 노드를 효과적으로 모니터링 할 수 있는 방법을 모색해야 합니다.
  3. 자동장애복구, 수동장애복구 기능의 마스터 승격 기능에 대한 기능검토와 테스트가 충분히 이루어져야 합니다.

Redis Sentinel

마스터/슬레이브 구성에서 고가용성을 위해 Redis Sentinel을 사용합니다.
( 클러스터와 Sentinel은 Failover를 관리하는 구성체계가 다르므로 같이 사용될 수 없음 )

마스터 노드 장애시, 자동으로 복구(Automastic failover)하는 기능을 Sentinel이 제공합니다.

Sentinel 주요특징

1. 모니터링(Monitoring)

마스터, 슬레이브 노드의 상태를 감시합니다.

2. 알림(Notification)

노드의 상태를 특정 시스템, 프로그램 혹은 API로 전달할 수 있습니다.

3. Automatic failover(자동 복구)

마스터 노드 장애 시 Sentinel은 이를 감지하고, 일련의 승격 프로세스를 통해 특정 슬레이브 노드를 마스터 노드로 승격합니다.

4. Configuration Provider

클라이언트는 Sentinel을 통해 Failover상황에도 동일한 엔드포인트로 Redis를 사용할 수 있습니다.

docker-compose-sentinel.yaml

version: '3'

x-redis-base: &redis-base
  image: bitnami/redis:6.2.10
  networks:
    - redis-net

x-redis-sentinel-base: &redis-sentinel
  image: bitnami/redis-sentinel:6.2.10
  environment:
    - REDIS_SENTINEL_DOWN_AFTER_MILLISECONDS=3000
    - REDIS_MASTER_HOST=redis
    - REDIS_MASTER_PORT_NUMBER=6379
    - REDIS_MASTER_SET=mymaster
    - REDIS_SENTINEL_QUORUM=2
  networks:
    - redis-net

services:
  redis:
    <<: *redis-base
    environment:
      - REDIS_REPLICATION_MODE=master
      - ALLOW_EMPTY_PASSWORD=yes
    ports:
      - '6379:6379'

  redis-slave1:
    <<: *redis-base
    environment: &slave_env
      - REDIS_REPLICATION_MODE=slave
      - REDIS_MASTER_HOST=redis
      - ALLOW_EMPTY_PASSWORD=yes
    ports:
      - '6379'

  redis-slave2:
    <<: *redis-base
    environment: *slave_env
    ports:
      - '6379'

  # For Sentinel
  redis-sentinel1:
    <<: *redis-sentinel
    ports:
      - "26379:26379"
    depends_on:
      - redis
      - redis-slave1
      - redis-slave2

  redis-sentinel2:
    <<: *redis-sentinel
    ports:
      - "26380:26379"
    depends_on:
      - redis-sentinel1

  redis-sentinel3:
    <<: *redis-sentinel
    ports:
      - "26381:26379"
    depends_on:
      - redis-sentinel2

networks:
  redis-net:
    driver: bridge
  • ALLOW_EMPTY_PASSWORD=yes 옵션으로 패스워드 사용을 해제할 수 있음
  • 위에서 사용한 bitnami이미지는 사용 편의성, 호환성, 보안성, 확장성 및 커뮤니티 지원과 같은 다양한 장점을 제공함
  • 3개(홀수)의 Sentinel을 구성한 이유는 Failover시 마스터 승격 투표의 과반수 이상 결정을 위함
  • --scale 옵션으로 sentinel을 원하는 수 만큼 실행할 수 있으나 포트 매핑과정에서 충돌이 나는 버그 존재함 (compose 버전 문제로 확인 됨)

compose 실행

$ docker-compose -f docker-compose-sentinel.yaml up

정상 실행 출력 로그

redis-sentinel3_1  | 1:X 16 Jun 2023 07:50:04.128 * +sentinel sentinel 5e1324a023d38ac018c4048e1279cfd2c802da4c 192.168.176.5 26379 @ mymaster 192.168.176.4 6379
redis-sentinel1_1  | 1:X 16 Jun 2023 07:50:04.874 * +sentinel sentinel 406025aa9e4615b5fca802041deeac9c9939fc0e 192.168.176.7 26379 @ mymaster 192.168.176.4 6379
redis-sentinel2_1  | 1:X 16 Jun 2023 07:50:04.874 * +sentinel sentinel 406025aa9e4615b5fca802041deeac9c9939fc0e 192.168.176.7 26379 @ mymaster 192.168.176.4 6379

Sentinel 접속 및 마스터/슬레이브 정보 확인

$ redis-cli -p 26379
127.0.0.1:26379> info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=192.168.160.2:6379,slaves=2,sentinels=3
  • Master정보와 Slave, Sentinel의 활성화 갯수를 확인
127.0.0.1:26379> sentinel masters
1)  1) "name"
    2) "mymaster"
    3) "ip"
    4) "192.168.160.2"
    5) "port"
    6) "6379"
    7) "runid"
    8) "0aa1c22564e1fe33018f6fc2eb7df137710e8ba8"
...생략...
  • default 마스터노드명은 mymaster로 설정 됨
  • compose environment에 REDIS_MASTER_SET 옵션으로 노드명 설정 가능

master 확인

127.0.0.1:26379> sentinel master mymaster
 1) "name"
 2) "mymaster"
 3) "ip"
 4) "172.26.0.4"
 5) "port"
 6) "6379"

...중략...

31) "num-slaves"
32) "2"
33) "num-other-sentinels"
34) "2"
  • 마스터노드의 ip / port 및 슬레이브 정보등을 확인

Failover Test

Failover 테스트를 하기 위해 간단한 파이썬 프로그램을 실행합니다.
테스트용 프로그램은 1초마다 Sentinel을 통해 얻은 마스터 노드 정보를 출력합니다.

docker-compose-sentinel.yaml 파일에 다음을 추가 합니다.

  python-app:
    build:
      context: .
      dockerfile: Dockerfile
    networks:
      - redis-net
    depends_on:
      - redis-sentinel3

Dockerfile

도커파일을 도커를 실행하는 위치에 생성합니다.

FROM python:3.9

WORKDIR /app

COPY requirements.txt .

RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD [ "python", "-u", "client4sentinel.py" ]
  • -u 옵션을 주어야만 파이썬에서 출력하는 Print로그를 실시간으로 확인할 수 있습니다.

client4sentinel.py

import time
from datetime import datetime

from redis import Sentinel
from redis.sentinel import MasterNotFoundError

s = Sentinel([('redis-sentinel1', 26379)], socket_timeout=1)
master = s.master_for('mymaster', socket_timeout=1)

while True:
    try:
        ip, port = s.discover_master('mymaster')
        print(datetime.now(), ', Current master info:', ip, port)
    except MasterNotFoundError as e:
        print(datetime.now(), ', Not Found master!!')
    finally:
        time.sleep(1)
  • redis-sentinel1은 컴포즈에 기술한 센티넬 서비스명입니다.

Compose 재실행

docker-compose -f docker-compose-sentinel.yaml down

docker-compose -f docker-compose-sentinel.yaml up --build

로그

python-app_1       | 2023-06-16 07:51:56.638240 , Current master info: 192.168.208.4 6379
redis-sentinel2_1  | 1:X 16 Jun 2023 07:51:57.398 * +sentinel sentinel b5627dded55437d05a5486db1cbc636e25e699c1 192.168.208.7 26379 @ mymaster 192.168.208.4 6379
redis-sentinel1_1  | 1:X 16 Jun 2023 07:51:57.398 * +sentinel sentinel b5627dded55437d05a5486db1cbc636e25e699c1 192.168.208.7 26379 @ mymaster 192.168.208.4 6379
python-app_1       | 2023-06-16 07:51:57.641038 , Current master info: 192.168.208.4 6379
python-app_1       | 2023-06-16 07:51:58.642896 , Current master info: 192.168.208.4 6379
python-app_1       | 2023-06-16 07:51:59.645808 , Current master info: 192.168.208.4 6379
python-app_1       | 2023-06-16 07:52:00.647456 , Current master info: 192.168.208.4 6379

매초마다 마스터 정보를 출력합니다. 이제 master node에 redis-cli로 접근해서 잠시 블로킹 상태로 만들겠습니다.

redis-cli DEBUG sleep 30

Failover 수행 로그

redis-sentinel3_1  | 1:X 16 Jun 2023 07:57:20.110 # +sdown master mymaster 192.168.208.4 6379
python-app_1       | 2023-06-16 07:57:20.313846 , Current master info: 192.168.208.4 6379
redis-sentinel2_1  | 1:X 16 Jun 2023 07:57:20.983 # +sdown master mymaster 192.168.208.4 6379
redis-sentinel2_1  | 1:X 16 Jun 2023 07:57:21.046 # +odown master mymaster 192.168.208.4 6379 #quorum 2/2
redis-sentinel2_1  | 1:X 16 Jun 2023 07:57:21.046 # +new-epoch 1
redis-sentinel2_1  | 1:X 16 Jun 2023 07:57:21.046 # +try-failover master mymaster 192.168.208.4 6379
redis-sentinel2_1  | 1:X 16 Jun 2023 07:57:21.063 # +vote-for-leader 02252d44eb04103d53788ab64014a04a6a66a7ee 1
redis-sentinel1_1  | 1:X 16 Jun 2023 07:57:21.081 # +new-epoch 1
redis-sentinel3_1  | 1:X 16 Jun 2023 07:57:21.081 # +new-epoch 1
redis-sentinel3_1  | 1:X 16 Jun 2023 07:57:21.098 # +vote-for-leader 02252d44eb04103d53788ab64014a04a6a66a7ee 1
redis-sentinel2_1  | 1:X 16 Jun 2023 07:57:21.098 # b5627dded55437d05a5486db1cbc636e25e699c1 voted for 02252d44eb04103d53788ab64014a04a6a66a7ee 1
redis-sentinel2_1  | 1:X 16 Jun 2023 07:57:21.098 # 94bc4b0f836a77c23a52d0929bcd350600177cd7 voted for 02252d44eb04103d53788ab64014a04a6a66a7ee 1
redis-sentinel1_1  | 1:X 16 Jun 2023 07:57:21.098 # +vote-for-leader 02252d44eb04103d53788ab64014a04a6a66a7ee 1
redis-sentinel1_1  | 1:X 16 Jun 2023 07:57:21.098 # +sdown master mymaster 192.168.208.4 6379
redis-sentinel2_1  | 1:X 16 Jun 2023 07:57:21.126 # +elected-leader master mymaster 192.168.208.4 6379
redis-sentinel2_1  | 1:X 16 Jun 2023 07:57:21.126 # +failover-state-select-slave master mymaster 192.168.208.4 6379
redis-sentinel1_1  | 1:X 16 Jun 2023 07:57:21.164 # +odown master mymaster 192.168.208.4 6379 #quorum 3/2
redis-sentinel1_1  | 1:X 16 Jun 2023 07:57:21.164 # Next failover delay: I will not start a failover before Fri Jun 16 08:03:21 2023
redis-sentinel2_1  | 1:X 16 Jun 2023 07:57:21.179 # +selected-slave slave 192.168.208.2:6379 192.168.208.2 6379 @ mymaster 192.168.208.4 6379
redis-sentinel2_1  | 1:X 16 Jun 2023 07:57:21.179 * +failover-state-send-slaveof-noone slave 192.168.208.2:6379 192.168.208.2 6379 @ mymaster 192.168.208.4 6379
redis-sentinel2_1  | 1:X 16 Jun 2023 07:57:21.242 * +failover-state-wait-promotion slave 192.168.208.2:6379 192.168.208.2 6379 @ mymaster 192.168.208.4 6379
redis-slave2_1     | 1:M 16 Jun 2023 07:57:21.242 # Connection with master lost.
redis-slave2_1     | 1:M 16 Jun 2023 07:57:21.242 * Caching the disconnected master state.
redis-slave2_1     | 1:M 16 Jun 2023 07:57:21.242 * Discarding previously cached master state.
redis-slave2_1     | 1:M 16 Jun 2023 07:57:21.242 # Setting secondary replication ID to b64d226b674f04565dc73d303c6a7b8c0512c393, valid up to offset: 67306. New replication ID is 0e9f083abd841a3e2ff0bf8043e153ad9e85688c
redis-slave2_1     | 1:M 16 Jun 2023 07:57:21.242 * MASTER MODE enabled (user request from 'id=6 addr=192.168.208.6:54903 laddr=192.168.208.2:6379 fd=11 name=sentinel-02252d44-cmd age=328 idle=0 flags=x db=0 sub=0 psub=0 multi=4 qbuf=188 qbuf-free=40766 argv-mem=4 o
bl=45 oll=0 omem=0 tot-mem=61468 events=r cmd=exec user=default redir=-1')
redis-slave2_1     | 1:M 16 Jun 2023 07:57:21.251 # CONFIG REWRITE executed with success.
redis-sentinel3_1  | 1:X 16 Jun 2023 07:57:21.260 # +odown master mymaster 192.168.208.4 6379 #quorum 3/2
redis-sentinel3_1  | 1:X 16 Jun 2023 07:57:21.260 # Next failover delay: I will not start a failover before Fri Jun 16 08:03:21 2023
python-app_1       | 2023-06-16 07:57:21.315819 , Not Found master!!
redis-sentinel2_1  | 1:X 16 Jun 2023 07:57:22.165 # +promoted-slave slave 192.168.208.2:6379 192.168.208.2 6379 @ mymaster 192.168.208.4 6379
redis-sentinel2_1  | 1:X 16 Jun 2023 07:57:22.165 # +failover-state-reconf-slaves master mymaster 192.168.208.4 6379
redis-sentinel2_1  | 1:X 16 Jun 2023 07:57:22.204 * +slave-reconf-sent slave 192.168.208.3:6379 192.168.208.3 6379 @ mymaster 192.168.208.4 6379
redis-slave1_1     | 1:M 16 Jun 2023 07:57:22.204 # Connection with master lost.
redis-slave1_1     | 1:M 16 Jun 2023 07:57:22.205 * Caching the disconnected master state.
redis-slave1_1     | 1:S 16 Jun 2023 07:57:22.205 * Connecting to MASTER 192.168.208.2:6379
redis-slave1_1     | 1:S 16 Jun 2023 07:57:22.205 * MASTER <-> REPLICA sync started
redis-slave1_1     | 1:S 16 Jun 2023 07:57:22.205 * REPLICAOF 192.168.208.2:6379 enabled (user request from 'id=6 addr=192.168.208.6:51869 laddr=192.168.208.3:6379 fd=11 name=sentinel-02252d44-cmd age=328 idle=0 flags=x db=0 sub=0 psub=0 multi=4 qbuf=342 qbuf-free=4
0612 argv-mem=4 obl=45 oll=0 omem=0 tot-mem=61468 events=r cmd=exec user=default redir=-1')
redis-sentinel3_1  | 1:X 16 Jun 2023 07:57:22.205 # +config-update-from sentinel 02252d44eb04103d53788ab64014a04a6a66a7ee 192.168.208.6 26379 @ mymaster 192.168.208.4 6379
redis-sentinel3_1  | 1:X 16 Jun 2023 07:57:22.205 # +switch-master mymaster 192.168.208.4 6379 192.168.208.2 6379
redis-sentinel3_1  | 1:X 16 Jun 2023 07:57:22.205 * +slave slave 192.168.208.3:6379 192.168.208.3 6379 @ mymaster 192.168.208.2 6379
redis-sentinel1_1  | 1:X 16 Jun 2023 07:57:22.205 # +config-update-from sentinel 02252d44eb04103d53788ab64014a04a6a66a7ee 192.168.208.6 26379 @ mymaster 192.168.208.4 6379
redis-sentinel1_1  | 1:X 16 Jun 2023 07:57:22.205 # +switch-master mymaster 192.168.208.4 6379 192.168.208.2 6379
redis-sentinel1_1  | 1:X 16 Jun 2023 07:57:22.205 * +slave slave 192.168.208.3:6379 192.168.208.3 6379 @ mymaster 192.168.208.2 6379
redis-sentinel1_1  | 1:X 16 Jun 2023 07:57:22.205 * +slave slave 192.168.208.4:6379 192.168.208.4 6379 @ mymaster 192.168.208.2 6379
redis-sentinel3_1  | 1:X 16 Jun 2023 07:57:22.205 * +slave slave 192.168.208.4:6379 192.168.208.4 6379 @ mymaster 192.168.208.2 6379
redis-slave1_1     | 1:S 16 Jun 2023 07:57:22.222 # CONFIG REWRITE executed with success.
redis-slave1_1     | 1:S 16 Jun 2023 07:57:22.222 * Non blocking connect for SYNC fired the event.
redis-slave1_1     | 1:S 16 Jun 2023 07:57:22.222 * Master replied to PING, replication can continue...
redis-slave1_1     | 1:S 16 Jun 2023 07:57:22.222 * Trying a partial resynchronization (request b64d226b674f04565dc73d303c6a7b8c0512c393:67306).
redis-slave2_1     | 1:M 16 Jun 2023 07:57:22.222 * Replica 192.168.208.3:6379 asks for synchronization
redis-slave2_1     | 1:M 16 Jun 2023 07:57:22.222 * Partial resynchronization request from 192.168.208.3:6379 accepted. Sending 446 bytes of backlog starting from offset 67306.
redis-slave1_1     | 1:S 16 Jun 2023 07:57:22.222 * Successful partial resynchronization with master.
redis-slave1_1     | 1:S 16 Jun 2023 07:57:22.222 # Master replication ID changed to 0e9f083abd841a3e2ff0bf8043e153ad9e85688c
redis-slave1_1     | 1:S 16 Jun 2023 07:57:22.222 * MASTER <-> REPLICA sync: Master accepted a Partial Resynchronization.
python-app_1       | 2023-06-16 07:57:22.318237 , Current master info: 192.168.208.2 6379
redis-sentinel2_1  | 1:X 16 Jun 2023 07:57:23.162 * +slave-reconf-inprog slave 192.168.208.3:6379 192.168.208.3 6379 @ mymaster 192.168.208.4 6379
redis-sentinel2_1  | 1:X 16 Jun 2023 07:57:23.162 * +slave-reconf-done slave 192.168.208.3:6379 192.168.208.3 6379 @ mymaster 192.168.208.4 6379
redis-sentinel2_1  | 1:X 16 Jun 2023 07:57:23.262 # -odown master mymaster 192.168.208.4 6379
redis-sentinel2_1  | 1:X 16 Jun 2023 07:57:23.262 # +failover-end master mymaster 192.168.208.4 6379
redis-sentinel2_1  | 1:X 16 Jun 2023 07:57:23.263 # +switch-master mymaster 192.168.208.4 6379 192.168.208.2 6379
redis-sentinel2_1  | 1:X 16 Jun 2023 07:57:23.263 * +slave slave 192.168.208.3:6379 192.168.208.3 6379 @ mymaster 192.168.208.2 6379
redis-sentinel2_1  | 1:X 16 Jun 2023 07:57:23.263 * +slave slave 192.168.208.4:6379 192.168.208.4 6379 @ mymaster 192.168.208.2 6379
python-app_1       | 2023-06-16 07:57:23.319664 , Current master info: 192.168.208.2 6379
python-app_1       | 2023-06-16 07:57:24.321228 , Current master info: 192.168.208.2 6379
redis-sentinel3_1  | 1:X 16 Jun 2023 07:57:25.211 # +sdown slave 192.168.208.4:6379 192.168.208.4 6379 @ mymaster 192.168.208.2 6379
redis-sentinel1_1  | 1:X 16 Jun 2023 07:57:25.249 # +sdown slave 192.168.208.4:6379 192.168.208.4 6379 @ mymaster 192.168.208.2 6379
python-app_1       | 2023-06-16 07:57:25.323027 , Current master info: 192.168.208.2 6379
redis-sentinel2_1  | 1:X 16 Jun 2023 07:57:26.280 # +sdown slave 192.168.208.4:6379 192.168.208.4 6379 @ mymaster 192.168.208.2 6379
python-app_1       | 2023-06-16 07:57:26.324887 , Current master info: 192.168.208.2 6379
python-app_1       | 2023-06-16 07:57:27.326593 , Current master info: 192.168.208.2 6379
  • 각 Sentinel은 마스터의 서비스가 동작하지 않는것을 감지, failover를 수행합니다.
  • 각 Sentinel은 투표를 통해 어떤 슬레이브가 마스터로 승격할지를 결정합니다. (과반수 투표)
  • 새로운 마스터 노드 선출까지 약 2초 이내에 처리되었으며, 그 동안 python-app에서 마스터노드 접근에 오류가 발생함을 확인할 수 있습니다.

마치며..

고가용 레디스 서비스를 위한 마스터/슬레이브 구성을 도커컴포즈를 사용하여 간단하게 구성해봤습니다.
여러개의 노드를 사용하는 마스터/슬레이브, 클러스터링 구성은 각 노드 상태를 모니터링하고 제어할 수 있어야 합니다. 다음장에서는 레디스 클러스터링 환경을 도커컴포즈를 사용해서 구축해보고, 모니터링 프레임워크를 사용해서 노드를 모니터링, 제어하는 기능을 소개합니다.

Reference

Docker-Hub Bitnami Redis (https://hub.docker.com/r/bitnami/redis-sentinel)

tags

레디스 redis master slave 도커 컴포즈 센티넬 구성 python 파이썬 장단점 마스터/슬레이브

반응형