Redis SENTINEL ELECTION OF LEADER

<< Sentinel Data Structure INFO sentinel >>

Redis SENTINEL ELECTION OF LEADER

여기서는 장애조치(failover)를 주관할 센티널 리더를 선출과정과 장애조치 과정을 설명한다.

센티널 리더 선출과 장애조치 과정

  1. 센티널은 1초에 한 번씩 PING을 보내서 레디스 마스터, 슬레이브, 그리고 다른 센티널들을 체크하는데, 30초 동안 응답이 없으면 해당 서버가 다운된 것으로 인지한다.   여기서 30초는 디폴트 값으로 이것은 설정 파일(sentinel.conf)에 down-after-milliseconds 파라미터로 변경할 수 있다.
    이렇게 확인된 다운을 주관적 다운(subjectively down)이라고 하고 줄여서 sdown이라고 한다.   주관적 다운이라고 하는 이유는 자신은 해당 서버가 다운되었다고 인지했지만 다른 센티널도 인지했는지 모르게 때문이다.   주관적 다운은 세 종류 서버(마스터, 슬레이브, 센티널)에 대해 모두 판정한다.   반면 다음에 설명할 객관적 다운(objectively down)은 마스터 서버에 대해서만 한다.
    로그: "+sdown"

  2. 다운된(sdown) 서버가 마스터일 경우 센티널은 그 서버를 모니터링하는 다른 센티널에게 센티널 명령("SENTINEL is-master-down-by-addr <master-ip> <master-port> <current-epoch> <*>")을 보내서 그 서버가 다운되었는지 묻는다.   이때 마지막 인수에 "*"을 보낸다.   객관적 다운을 판정한 이후에 센티널 리더 선출과정에서도 같은 명령을 다른 센티널들에게 보내는데, 그때는 마지막 인수에 센티널 자신의 runid를 넣는다.

  3. 명령(질의)을 받은 다른 센티널들은 해당 마스터 서버가 다운되었는지 여부를 응답한다.   센티널는 받은 응답에 따라 그 마스터의 다운 여부를 기록한다.  자신을 포함해서 마스터가 다운되었다고 응답한 센티널 수가 쿼럼값 이상이면 센티널은 해당 마스터가 실제로 다운되었다고 판정한다.   이것을 객관적 다운(objectively down)이라고 하고 줄여서 odown이라고 한다.
    로그: "+odown"

  4. 객관적 다운을 판정한 센티널은 장애조치 단계로 진입한다.   센티널은 장애조치 관련해서 failover_state 플레그를 가지고 있다.   평상시 failover_state는 SENTINEL_FAILOVER_STATE_NONE (0)이다.
    장애조치 단계에 진입하면
    • failover_state를 SENTINEL_FAILOVER_STATE_WAIT_START (1)로 변경한다.
    • current_epoch을 1 증가시킨다.
    • failover_start_time = 현재 시각 + 랜덤시간(0~999 milliseconds)
      랜덤시간을 더하는 이유는 시간초과로 리더 선출에 실패하여 재 선출할때 선출이 쉽게 하기 위해서이다.   리더 선출은 시간에 매우 의존적이다.
      #define SENTINEL_MAX_DESYNC 1000
      master->failover_start_time = mstime()+rand()%SENTINEL_MAX_DESYNC;
    로그: "+new-epoch", "+try-failover"

  5. 이번에는 센티널 리더를 선출하기 위해서 다른 센티널들에게 명령("SENTINEL is-master-down-by-addr <master-ip> <master-port> <new-epoch> <runid>")을 보낸다.   이번에는 1 증가시킨 new-epoch과 센티널 자신의 runid를 보낸다.

  6. 질문을 받은 센티널은 자신의 current_epoch보다 받은 req_epoch이 높으면 다음 사항을 진행한다.
    • 자신의 current_epoch을 받은 req_epoch으로 대치하고, 로그("+new-epoch")를 남긴다.
    • 마스터의 리더를 받은 runid로 대치하고, 로그("+vote-for-leader")를 남긴다.   이것은 자신은 받은 센티널을 리더로 투표했다는 것을 의미한다.   failover_start_time에 현재 시각 + 랜덤 시간을 넣는다.   이것은 다른 센티널이 리더가 되어 장애조치를 시작한다는 것을 의미한다.
    • 새로운 epoch이 시작되었으므로 sentinel.conf에 new-epoch을 기록한다.
    그리고 다음 세 가지를 응답한다.
    • 마스터 다운 여부
    • 투표(리더 runid): 문의한 센티널 runid를 리턴한다.
    • leader_epoch: 받은 epoch
    로그: "+new-epoch", "+vote-for-leader"

    자신의 current-epoch이 req_epoch과 같으면 이미 리더로 정한 runid를 리턴한다.   한 epoch에서 리더를 한 번 선정(투표)하면 바꾸지 않는다.

  7. 응답을 받은 센티널은 이제 개표를 한다.   다음 조건이 만족되면 자신이 리더로 선출된다.
    1) 투표에 참여한 센티널 수가 그 마스터를 모니터링하고 있는 전체 센티널의 과반수 이상이어야 한다.
    2) 자신의 표를 포함해서 자신을 리더로 투표한 수가 쿼럼값 이상이어야 한다.
    만약 리더가 선정되지 않으면 선출과정(묻고->응답->개표)을 반복하는데, 투표에 참여하지 못했던 센티널이 참여하면 결과가 바뀔 수 있지만, 그렇지 않으면 결과는 같다.   시간초과가 발생하면 새로운(new-epoch) 선출과정이 시작된다.
    로그: "+elected-leader"

  8. 리더로 선출된 센티널은 새 마스터가 될 슬레이브를 선정한다.
    failover_state를 SENTINEL_FAILOVER_STATE_SELECT_SLAVE (2)로 변경한다.
    선정 조건
    • 첫 번째: slave-priority 값이 적은 것
    • 두 번째: slave_repl_offset 값이 큰것
    • 세 번째: runid가 작은 것
    로그: "+failover-state-select-slave", "+selected-slave"

  9. 선정된 슬레이브를 새 마스터로 만든다.
    failover_state를 SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE (3)로 변경한다.
    선정된 슬레이브에 "slaveof no one" 명령을 실행한다.
    로그: "+failover-state-send-slaveof-noone"

  10. 슬레이브가 마스터로 승격되기를 기다린다.
    failover_state를 SENTINEL_FAILOVER_STATE_WAIT_PROMOTION (4)로 변경한다.
    로그: "+failover-state-wait-promotion", "+promoted-slave"

  11. 다른 슬레이브들이 새 마스터를 바라보도록 슬레이브마다 slaveof new-ip new-port 명령을 실행한다.
    failover_state를 SENTINEL_FAILOVER_STATE_RECONF_SLAVES (5)로 변경한다.
    로그: "+failover-state-reconf-slaves"
    슬레이브마다 세 개의 로그("slave-reconf-sent/inprog/done")가 남는다.

  12. 모든 슬레이브가 새 마스터를 바라보는 작업(slaveof)이 완료되면 센티널의 마스터, 슬레이브, 다른 센티널 정보를 갱신한다.
    failover_state를 SENTINEL_FAILOVER_STATE_UPDATE_CONFIG (6)로 변경한다.
    로그: "+failover-end"

  13. 장애조치 완료: 센티널은 새로운 마스터를 모니터링하기 시작한다.
    로그: "+switch-master"


센티널 리더 선출 케이스별 설명과 해당 로그

센티널은 5대이고 쿼럼은 3일 경우

  • Case 1) 주관적 다운(sdown)을 인지하지 못한 상태에서 다른 센티널에게 투표한 경우
    여기서 new-epoch은 이미 odown으로 진입한 다른 센티널의 질의(Ask)에 투표한 것이다.   new-epoch과 vote-for-leader는 연속되어 진행되는데 약 20ms 차이가 있는 것은 new-epoch이 발생하면 sentinel.conf에 new-epoch을 기록하기 때문이다.   기록은 파일 전체를 fsync 모드로 다시 쓴다.
    01:47:04.801 # +new-epoch 35
    01:47:04.833 # +vote-for-leader a3d44d79c3b8433fc82af3b9c4f2e04d435c24b8 35
    01:47:05.490 # +config-update-from sentinel a3d44d79c3b8433fc82af3b9c4f2e04d435c24b8 127.0.0.1 7113 @ Xmaster 127.0.0.1 7121
    01:47:05.490 # +switch-master Xmaster 127.0.0.1 7121 127.0.0.1 7124
  • Case 2) 주관적 다운(sdown)을 인지하고 다른 센티널에게 투표하고 자신은 객관적 다운(odown)을 판정하지 못한 경우
    주관적 다운 인지후 다른 센티널들에게 마스터 다운여부에 대해서 물었으나, 2개 이상의 센티널에 다운되었다고 응답받지 못해서 객관적 다운(odown) 판정을 하지 못한다.
    01:10:10.414 # +sdown master Xmaster 127.0.0.1 7124
    01:10:10.524 # +new-epoch 34
    01:10:10.545 # +vote-for-leader 5b9f0bcc19e2828e17ead56de51ac3879ebc9af9 34
    01:10:10.803 # +config-update-from sentinel 5b9f0bcc19e2828e17ead56de51ac3879ebc9af9 127.0.0.1 7114 @ Xmaster 7124
    01:10:10.803 # +switch-master Xmaster 127.0.0.1 7124 127.0.0.1 7121
  • Case 3) 주관적 다운(sdown)을 인지하고 다른 센티널에게 투표하고 객관적 다운(odown)을 판정한 경우
    객관적 다운 판정 이전에 이미 다른 센티널에 투표했으므로 자신은 리더로 선출되지 못한다.   다른 센티널에게 투표한 다음 odown을 판정했을 경우 Next failover delay가 표시된다.   이 경우 일정 시간 동안 리더가 선출되지 않으면 이 센티널은 표시된 시간에 epoch을 1증가시키고 리더 선출을 다시 시작할 수 있다.   일정 시간은 failover_timeout * 2 (디폴트 6분)이다.   여기서는 failover_timeout을 10초로 설정했으므로 20초 후로 정해졌다.
    01:10:10.445 # +sdown master Xmaster 127.0.0.1 7124
    01:10:10.524 # +new-epoch 34
    01:10:10.545 # +vote-for-leader 5b9f0bcc19e2828e17ead56de51ac3879ebc9af9 34
    01:10:10.545 # +odown master Xmaster 127.0.0.1 7124 #quorum 5/3
    01:10:10.545 # Next failover delay: I will not start a failover before Fri Sep 9 01:10:30 2016
    01:10:10.803 # +config-update-from sentinel 5b9f0bcc19e2828e17ead56de51ac3879ebc9af9 127.0.0.1 7114 @ Xmaster 7124
    01:10:10.803 # +switch-master Xmaster 127.0.0.1 7124 127.0.0.1 7121
  • Case 4) 주관적 다운(sdown)을 인지하고 객관적 다운(odown)을 판정하고 자신에게 투표했으나 자신이 리더로 선출되지 못한 경우
    01:10:10.426 # +sdown master Xmaster 127.0.0.1 7124
    01:10:10.492 # +odown master Xmaster 127.0.0.1 7124 #quorum 4/3
    01:10:10.492 # +new-epoch 34
    01:10:10.492 # +try-failover master Xmaster 127.0.0.1 7124
    01:10:10.524 # +vote-for-leader 41d43f6a6b7b852eda2311b2e66494419170caa6 34
    01:10:10.803 # +config-update-from sentinel 5b9f0bcc19e2828e17ead56de51ac3879ebc9af9 127.0.0.1 7114 @ Xmaster 7124
    01:10:10.803 # +switch-master Xmaster 127.0.0.1 7124 127.0.0.1 7121
  • Case 5) 자신이 리더로 선출된 경우
    주관적 다운(sdown)을 인지하고 객관적 다운(odown)을 판정하고 자신에게 투표했고 자신이 리더로 선출된 경우 장애조치를 진행한다.
    01:10:10.417 # +sdown master Xmaster 127.0.0.1 7124
    01:10:10.476 # +odown master Xmaster 127.0.0.1 7124 #quorum 3/3
    01:10:10.476 # +new-epoch 34
    01:10:10.476 # +try-failover master Xmaster 127.0.0.1 7124
    01:10:10.587 # +elected-leader master Xmaster 127.0.0.1 7124
    01:10:10.587 # +failover-state-select-slave master Xmaster 127.0.0.1 7124
    01:10:10.642 # +selected-slave slave 127.0.0.1:7121 127.0.0.1 7121 @ Xmaster 127.0.0.1 7124
    01:10:10.642 * +failover-state-send-slaveof-noone slave 127.0.0.1:7121 127.0.0.1 7121 @ Xmaster 127.0.0.1 712
    01:10:10.732 * +failover-state-wait-promotion slave 127.0.0.1:7121 127.0.0.1 7121 @ Xmaster 127.0.0.1 7124
    01:10:10.751 # +promoted-slave slave 127.0.0.1:7121 127.0.0.1 7121 @ Xmaster 127.0.0.1 7124
    01:10:10.751 # +failover-state-reconf-slaves master Xmaster 127.0.0.1 7124
    01:10:10.803 * +slave-reconf-sent slave 127.0.0.1:7125 127.0.0.1 7125 @ Xmaster 127.0.0.1 7124
    01:10:11.512 * +slave-reconf-inprog slave 127.0.0.1:7125 127.0.0.1 7125 @ Xmaster 127.0.0.1 7124
    01:10:12.526 * +slave-reconf-done slave 127.0.0.1:7125 127.0.0.1 7125 @ Xmaster 127.0.0.1 7124
    01:10:12.627 * +slave-reconf-sent slave 127.0.0.1:7122 127.0.0.1 7122 @ Xmaster 127.0.0.1 7124
    01:10:13.567 * +slave-reconf-inprog slave 127.0.0.1:7122 127.0.0.1 7122 @ Xmaster 127.0.0.1 7124
    01:10:13.567 * +slave-reconf-done slave 127.0.0.1:7122 127.0.0.1 7122 @ Xmaster 127.0.0.1 7124
    01:10:13.642 * +slave-reconf-sent slave 127.0.0.1:7123 127.0.0.1 7123 @ Xmaster 127.0.0.1 7124
    01:10:14.575 * +slave-reconf-inprog slave 127.0.0.1:7123 127.0.0.1 7123 @ Xmaster 127.0.0.1 7124
    01:10:15.632 * +slave-reconf-done slave 127.0.0.1:7123 127.0.0.1 7123 @ Xmaster 127.0.0.1 7124
    01:10:15.715 # +failover-end master Xmaster 127.0.0.1 7124
    01:10:15.716 # +switch-master Xmaster 127.0.0.1 7124 127.0.0.1 7121

Case 2~5는 같은 테스트의 로그이고, Case 1은 다른 테스트의 로그이다.   다섯 케이스가 한 번의 테스트로 모두 나왔으면 거의 같은 시간대가 표시되는데, Case 1은 다른 시간대가 표시되었다.


마스터 다운 인지 순서별 처리과정

여기서는 마스터 서버 주관적 다운을 인지한 순서별로 센티널의 처리과정이 어떻게 되는지 알아보자.   이해를 쉽게 하기위해 센티널이 3대, 쿼럼(quorum)은 2인 상황을 가정하고, 센티널은 각각을 A,B,C라고 하자.

  • 마스터 다운을 처음 인지한 센티널 A는 다른 센티널들에게 마스터가 다운되었는지 묻는다.   다른 센티널들은 아직 마스터가 다운되었다는 것을 알지못하므로 살아있다고 응답한다.   센티널 A는 마스터가 다운되었다고 인지한 센티널 수가 쿼럼값에 미치지 못하므로 객관적 다운을 판정하지 못한다.   위 Case 2)에 해당한다.

  • 마스터 다운을 두 번째로 인지한 센티널 B도 다른 센티널에게 묻는다.   이때는 센티널 A는 이미 마스터 다운을 알고있어 다운되었다고 응답한다.   센티널 B는 자신까지 포함하면 다운을 인지한 센티널 수가 2이므로 쿼럼값에 도달했다.   이제 센티널 B는 객관적 다운으로 판정하고 장애조치 단계에 진입하여 리더 선출을 시작한다.   이때는 epoch을 1증가시키고 센티널 자신의 runid를 인수로 넣어서 다른 센티널에게 묻는다.   다른 센티널은 새로운 epoch이므로 센티널 B에게 투표한다.   위의 Case 5)에 해당한다.

  • 다스터 다운을 세 번째로 인지한 센티널C도 다른 센티널에게 물어서 객관적 다운으로 판정하고, 장애조치 단계에 진입하여 리더 선출을 시작한다.   객관적 다운 이전에 다른 센티널에게 투표했으면 Case 3)에 해당하고, 객관적 다운 이후 자신에게 투표했으나 리더로 선출되지 못했으면 Case 4)에 해당한다.

  • 마스터 다운을 인지하지 전에 센티널 B에게 투표했으면 Case 1)에 해당한다.

특정 센티널을 리더로 선출되게 하는 방법

장애조치 시 새로운 마스터가 될 슬레이브의 선정은 slave-priority 값으로 조정할 수 있다.   그럼 리더 선출에 우리가 관여할 수 있을까?   slave-priority 같은 직접적인 파라미터는 없지만,   위에서 설명한 것처럼 마스터 다운을 발견한 순서에 따라 리더가 될 가능성이 달라진다.   쿼럼값이 2이면 두 번째로 마스터 다운을 인지한 센티널이, 쿼럼값이 3이면 세 번째로 마스터 다운을 인지한 센티널이 리더가 될 가능성이 높다.   이러한 특성을 이용하면 특정 센티널을 리더로 선출될 가능성을 높이는 방법이 있다. 주관적 다운(sdown)을 인지하는 기준인 down-after-milliseconds 값을 이용하는 것이다.

  • 센티널이 3대이고 쿼럼이 2일때 각 센티널에 down-after-milliseconds 값을 A는 10000, B는 11000, C는 12000 이렇게 부여하면 11000을 가진 센티널 B가 리더가 될 가능성이 높다.
  • 센티널이 5대이고 쿼럼이 3일때 각 센티널에 down-after-milliseconds 값을 10000, 11000, 12000, 13000, 14000 이렇게 부여하면 12000을 가진 센티널이 리더가 될 가능성이 높다.
  • 만약 리더가 failover-timeout 동안 선출되지 않으면 시간초과로 리더 선출을 다시하게 된다.   시간초과까지 생각해서 특정 센티널이 리더가 되는것을 조정하고 싶으면 그 센티널에 가장 적은 failover-timeout 시간을 주고, 다른 센티널들에게는 1초씩 크게 부여한다.


센티널이 HZ(timer interrupt 주기)를 매번 바꾸는 이유

  • 레디스 서버를 포함해서 센티널 서버는 주기적인 작업을 위해 timer interrupt를 발생시키는데, 이는 hz 값에 의존한다.   hz가 5이면 1초에 5번(200ms 마다), 10이면 1초에 10번(100ms 마다), 20이면 1초에 20번(50ms 마다) 인터럽트가 발생한다.  디폴트 hz는 10이고, 레디스 서버는 hz를 redis.conf 또는 config set 명령으로 바꿀 수 있다.
  • 레디스 서버는 정해진 hz 값에 따라 일정한 주기로 필요한 작업을 하는 것이 맞다.   하지만 센티널이 주기적으로 하는 작업은 레디스 서버를 모니터링하는 것인데, 만약 여러 센티널에서는 동일한 hz 값을 사용하면다면, 여러 센티널에서 PING이나 INFO 명령을 동시에 보낼 것이고 레디스 서버들도 동시에 응답하게 될 것이다.   그럼 센티널들이 주관적 다운을 발견하는 시간이 거의 비슷할 것이고, 이후 센티널 리더를 선출은 객관적 다운 판정 시점에 매우 의존적인데, 거의 동시에 선거가 진행되어 리더가 선출되지 않아 리더 선출과정이 반복되어 시간초과가 발생할 가능성이 높다.
  • 이것에 대한 해결책으로 센티널은 매 timer interrupt마다 hz값을 랜덤하게 10에서 19사이의 값으로 변경시킨다.   그래서 센티널은 timer interrupt가 1초에 53ms(hz 19)에서 100ms(hz 10) 사이로 평균 14.5번 발생한다.   정리하면, 각 센티널은 매번 다른 시간에 인터럽트가 발생해서 레디스 서버에 PING이나 INFO 명령을 각기 다른 시간에 보내게 된다.   따라서 주관적 다운을 인지하는 시간이 각 센티널마다 달라 이후에 진행되는 센티널 리더 선출과정에서도 경합이 크게 줄어들어 대부분의 경우 한번에 선출이 완료된다.

  • 아래 sentinelTimer()은 센티널의 메인 함수이다.   이 함수가 매 timer interrupt 마다 실행된다.   마지막 server.hz 문이 hz를 랜덤하게 바꾸어주는 곳이다.   여기서 CONFIG_DEFAULT_HZ는 10이다.


  • server.c에 아래 소스와 같이 "run_with_period(100)" 코드가 있어서 센티널 메인 함수가 100ms마다 실행되는 것으로 오해할 수 있으나, 이것은 위에 설명한 것처럼 hz를 바꾸기 때문에 변경된 hz 값에 따라 실행 간격이 정해진다.
    serverCron() 함수 안에는 많은 코드가 있으나 여기서는 센티널 관련 코드만 표시했다.

문서 정보

  • 이 문서는 2016년 9월에 버전 3.2.2을 기준으로 만들었습니다.



<< Sentinel Data Structure ELECTION OF LEADER INFO sentinel >>

질문하거나 댓글을 보려면 클릭하세요.  댓글수 :    조회수 :

Email 답글이 올라오면 이메일로 알려드리겠습니다.