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 선거과정
- Sentinel이 master에 대하여 O_DOWN판정후 failover process 시작.
- failover_state를
SENTINEL_FAILOVER_STATE_NONE
에서 ->SENTINEL_FAILOVER_STATE_WAIT_START
로 변경(src@sentinelStartFailover)
- failover_state를
- current-epoch +1후 다른 sentinel에
SENTINEL IS-MASTER-DOWN-BY-ADDR <ip> <port> <current-epoch> <runid>
명령으로 자신의 epoch, runid 보내 투표요청을 한다.(src@sentinelAskMasterStateToOtherSentinels) - 투표(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)
- 선거기간내 과반수이상이 투표를 했거나 최소 quorum만큼의 득표를 받으면 leader로 선거되며 failover_state가
SENTINEL_FAILOVER_STATE_SELECT_SLAVE
로 변경된다.src@sentinelFailoverWaitStart- 최소
50% + 1
의 득표를 얻어야 leader가 될수 있으며 이 또한 투표효울성을 위하여 홀수의 instance를 띄워야 하는 이유다.
- 최소
- 선거기간내 선출된 leader가 없으면 (failover_timeout * 2)시간후 재선거가 시작된다.
- 선거된 leader는 master선출작업을 시작한다.
master 선출
master 선출과정이 시작되면 master후보를 선정후 우선순위 확인을 통해 master를 선출하게된다.
아래와 같은 조건으로 master로 만들기에 적합한 slave를 선택한다. (src@sentinelSelectSlave)
- S_DOWN, O_DOWN, DISCONNECTED 상태의 instance제외
- 5초내(sentinel_ping_period*5) ping응답을 받지 못한 instance제외
- slave_priority(우선순위)가 0인 instance제외 (priority=0인 instance는 폐기로 인식)
- info_validity_time이 3초이전 혹은 5초이전(master가 S_DOWN상태일때)인 instance제외
- master_link_down_time이 (now - master->s_down_since_time) + (master->down_after_period * 10) 보다 큰 instance제외
master후보선정이 끝나면 아래와 같은 우선순위로 master를 선택하게된다.(src@compareSlavesForPromotion )
- slave_priority값이 작으면 우선
- replication offset(slave_repl_offset)가 크면 우선
- runid가 작으면우선
master대체하기에 적합한 slave를 찾은후 failover_state는 SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE로 변경된다.(src@sentinelFailoverSelectSlave)
slave to master작업
master를 대체할 slave를 찾은후 실질적인 slave를 master로 승격시키는 작업이 시작된다.
-
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) -
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) -
SENTINEL_FAILOVER_STATE_RECONF_SLAVES로
상태다른 slave들에게 SLAVEOF <new master> 명령을 보내고 작업이 완료되면 failover_state를
SENTINEL_FAILOVER_STATE_UPDATE_CONFIG
로 변경한다.(src@sentinelFailoverDetectEnd) -
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)