Redis Sentinel, failover과정 정리

Posted by Geuni's Blog on August 24, 2023

Redis Sentinel은 Redis HA솔루션이다. Redis는 Redis Cluster라고 하는 Cluster제품이 따로 있으며 Sentinel은 Redis Cluster와 관련이 없다. Sentinel은 cluster가 필요없는 사용자들에게 간단한 failover기능을 제공하는 제품이다.

Sentinel은 기능을 알아보자.

  • Monitoring : Sentinel은 failover 자동화를 목적으로 master/slave 상태를 지속적으로 모니터링한다.
  • Notification: redis instance down 혹은 failover 발생시 Pub/Sub 기능으로 client에 알리거나 shell script실행을 통한 notify기능을 할수있다.
  • Automatic failover: master가 장애발생시 failover process가 시작되며 sentinel은 slave중 적합한 대상을 찾아 master로 승격시키고 기타 slave들이 새 master를 바라보도록 재구성작업을 하게된다.
  • Configuration provider: Sentinel은 client에 service discovery역할을 해준다. client에 master,slave등의 구성정보를 제공하여 read/write 분리기능 등이 가능하도록 하게해준다. sentinel은 client에 구정정보를 제공할뿐 proxy서버가 아니다.

Sentinel에 대하여 알아야할 기본지식

  • 안정적인 HA구성을 위하여 최소 3개의 sentinel instance가 필요함. 투표 효율성을 위하여 홀수 instance 추천(짝수case에서 50%투표율일때 재투표로 이어짐 )

  • 각 instance를 상대적으로 독립적인 VM, 물리서법 혹은 가용영역(AZ)에 설치하여 장애영향을 가능한 줄일것

  • Sentinel + Redis구조에서 Redis는 비동기화방식으로 데이터 sync를 한다. 따라 failover 발생시 부분 데이터유실 기능성이 존재한다.

  • 충분한 테스트 검증을 거치지 않은 HA구성은 안전하다고 말할수 없다. 기본적인 failover테스트는 해보자.

알아야할 S_DOWN & O_DOWN

  • S_DOWN(Subjectively Down) - 주관적 다운, 로컬 sentinel(자신)기준에서 PING응답을 받지 못하는 경우
  • O_DOWN(Objectively Down) - 객관적 다운, 설정한 quorum 이상의 sentinel에서 SDOWN 피드백을 받았을떄 다운상태가 SDOWN에서 -> ODOWN으로 승격된다.

ODOWN상태는 master에만 적용되는 상태이며 failover액션으로 이어진다. replica 다운시 sentinel이 취하는 액션이 없으며 따라 replica는 ODOWN상태가 없다.

failover과정

Sentinel은 주기적으로 master, slave 및 기타 sentinel들에 대하여 health체크를 한다.

이때 master에 대하여 ODOWN판정(src@sentinelCheckSubjectivelyDown)을 하게되면 failover process가 시작된다.

우선 failover작업을 수행할 sentinel leader를 선거하게 되며 선거된 sentinel leader는 살아있는 slave중 master를 선출하게된다.

failover과정에 아래와 같은 failover상태변화를 거치된다. (sentinel.c참고)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 +-------------------------------------------+
 | SENTINEL_FAILOVER_STATE_NONE              |               
 |  - No failover in progress                | 			
 +-------------------------------------------+
                      ↓
 +-------------------------------------------+
 | SENTINEL_FAILOVER_STATE_WAIT_START        |		 
 | - Wait for failover_start_time            | 			 
 +-------------------------------------------+
                       ↓
 +-------------------------------------------+
 | SENTINEL_FAILOVER_STATE_SELECT_SLAVE      | 	 
 | - Select slave to promote                 |				 
 +-------------------------------------------+
                       ↓
 +-------------------------------------------+
 | SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOON | 
 | - Slave -> Master                         |						 
 +-------------------------------------------+
                       ↓
 +-------------------------------------------+
 | SENTINEL_FAILOVER_STATE_WAIT_PROMOTION    |
 | - Wait slave to change role               | 				 
 +-------------------------------------------+
                       ↓
 +-------------------------------------------+
 | SENTINEL_FAILOVER_STATE_RECONF_SLAVES     |     
 | - SLAVEOF newmaster                       |						 
 +-------------------------------------------+
                        ↓
 +-------------------------------------------+
 | SENTINEL_FAILOVER_STATE_UPDATE_CONFIG     |  	 
 | - Monitor promoted slave.                 |				 
 +-------------------------------------------+
 

Sentinel leader 선거과정

  1. Sentinel이 master에 대하여 O_DOWN판정후 failover process 시작.
    • failover_state를 SENTINEL_FAILOVER_STATE_NONE에서 -> SENTINEL_FAILOVER_STATE_WAIT_START로 변경(src@sentinelStartFailover)
  2. current-epoch +1후 다른 sentinel에 SENTINEL IS-MASTER-DOWN-BY-ADDR <ip> <port> <current-epoch> <runid>명령으로 자신의 epoch, runid 보내 투표요청을 한다.(src@sentinelAskMasterStateToOtherSentinels)
  3. 투표(is-master-down-by-addr)요청을 받은 sentinel은 current-epoch과 요청받은 req_epoch을 비교한다.req_epoch이 current-epoch값보다 크면 master구조체의 leader,leader_epoch을 각각 요청받은 req_runid, req_epoch으로 업데이트 하고 받은 req_epoch을 응답해주며 Follower역할로 전환된다. 값이 같으면 이미 다른 Candidate에게 투표를 했다는 의미므로 이미 표를 준 sentinel의 runid를 리턴한다. (src@sentinelVoteLeader)
  4. 선거기간내 과반수이상이 투표를 했거나 최소 quorum만큼의 득표를 받으면 leader로 선거되며 failover_state가SENTINEL_FAILOVER_STATE_SELECT_SLAVE로 변경된다.src@sentinelFailoverWaitStart
    • 최소 50% + 1의 득표를 얻어야 leader가 될수 있으며 이 또한 투표효울성을 위하여 홀수의 instance를 띄워야 하는 이유다.
  5. 선거기간내 선출된 leader가 없으면 (failover_timeout * 2)시간후 재선거가 시작된다.
  6. 선거된 leader는 master선출작업을 시작한다.

master 선출

master 선출과정이 시작되면 master후보를 선정후 우선순위 확인을 통해 master를 선출하게된다.

아래와 같은 조건으로 master로 만들기에 적합한 slave를 선택한다. (src@sentinelSelectSlave)

  1. S_DOWN, O_DOWN, DISCONNECTED 상태의 instance제외
  2. 5초내(sentinel_ping_period*5) ping응답을 받지 못한 instance제외
  3. slave_priority(우선순위)가 0인 instance제외 (priority=0인 instance는 폐기로 인식)
  4. info_validity_time이 3초이전 혹은 5초이전(master가 S_DOWN상태일때)인 instance제외
  5. master_link_down_time이 (now - master->s_down_since_time) + (master->down_after_period * 10) 보다 큰 instance제외

master후보선정이 끝나면 아래와 같은 우선순위로 master를 선택하게된다.(src@compareSlavesForPromotion )

  1. slave_priority값이 작으면 우선
  2. replication offset(slave_repl_offset)가 크면 우선
  3. runid가 작으면우선

master대체하기에 적합한 slave를 찾은후 failover_state는 SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE로 변경된다.(src@sentinelFailoverSelectSlave)

slave to master작업

master를 대체할 slave를 찾은후 실질적인 slave를 master로 승격시키는 작업이 시작된다.

  1. SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE상태

    failover_state가 SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE인 상태에 sentinel은 선택받은 slave에 SLAVEOF NO ONE 명령을 실행하여 master로 role변경을 요청후 failover_state를 SENTINEL_FAILOVER_STATE_WAIT_PROMOTION으로 변경한다.(src@sentinelFailoverSendSlaveOfNoOne)

  2. SENTINEL_FAILOVER_STATE_WAIT_PROMOTION상태

    현재 상태에서 SLAVEOF NO ONE 명령을 받은 slave에 INFO명령을 날려 role을 확인한다. failover_timeout시간내 role이 slave에서 master로 변경된것을 확인하면 failover_state를 SENTINEL_FAILOVER_STATE_RECONF_SLAVES로 변경한다.(src@sentinelGetCurrentMasterAddress)

  3. SENTINEL_FAILOVER_STATE_RECONF_SLAVES로상태

    다른 slave들에게 SLAVEOF <new master> 명령을 보내고 작업이 완료되면 failover_state를 SENTINEL_FAILOVER_STATE_UPDATE_CONFIG로 변경한다.(src@sentinelFailoverDetectEnd)

  4. SENTINEL_FAILOVER_STATE_UPDATE_CONFIG상태

    메모리중의 master정보를 새master로 리셋후 redis엔진의 redis.conf파일을 rewriting한다.src@sentinelResetMasterAndChangeAddress

    failover_state는 다시 정상상태인 SENTINEL_FAILOVER_STATE_NONE 돌아가며 failover process는 종료된다.

Sentinel의 통신방식

모든 Sentinel은 아래와 같이 master, slave, 다른 sentinel들과 연결을 맺고있다.

  • 모니터링중인 master
  • 모든 관련 slave들
  • 모니터링 대상 master에 연결된 기타 sentinel들

sentinel.conf에는 master정보만 설정돼있다. 그렇다면 Sentinel은 slave와 다른 sentinel과 어떻게 통신하나 ?

Sentinel은 Slave와 어떻게 통신하나?

Sentinel은 master에 INFO명령을 날려 slave정보를 조회한다.(src@sentinelRefreshInstanceInfo)

Sentinel간에는 어떻게 통신하나?

Sentinel간의 통신은 주로 Redis의 Pub/Sub기능을 통하여 이루어진다. master의 __sentinel__:hello sentinel전용채널을 통하여 자신의 ip, port등 정보를 배포한다.(src@sentinelSendHello)

  • 메시지 포멧: sentinel_ip,sentinel_port,sentinel_runid,current_epoch, master_name,master_ip,master_port,master_config_epoch

sentinel은 구독중인 __sentinel__:hello채널에서 메시지를 받은후 runid확인을 한다. runid가 자신의 runid와 같을때 자신의 정보라 판단하여 메시지는 폐기되며 runid가 다르면 타 sentinel정보를 dict에 기록을 한다. (src@sentinelReceiveHelloMessages)