[번역] Redis Cluster Specification: (4) Fault Tolerance

ma2sql·2021년 11월 25일
0

원문: https://redis.io/topics/cluster-spec

Heartbeat and gossip messages

레디스 클러스터 노드는 계속해서 핑/퐁 패킷을 교환한다. 두 종류의 패킷은 동일한 구조를 가지며, 둘다 중요한 구성 정보를 전달한다. 실제 차이가 나는 것은 메시지 타입의 필드 뿐이다. 핑/퐁 패킷을 합한 것을 하트 비트 패킷(heartbeat packets)이라고 한다.

일반적으로 노드는 핑 패킷을 보내고, 수신자가 퐁 패킷을 응답하도록 트리거한다. 그러나 이것이 꼭 사실만은 아니다. 노드가 응답을 트리거링하지 않고, 자신의 구성에 관한 정보를 다른 노드에게 보내기 위해서 퐁 패킷을 보낼 수 있다. 이것은 예를 들어, 새로운 구성 정보를 가능한한 빠르게 브로드캐스트 위한 상황에서 유용하다.

일반적으로 노드는 몇 개의 랜덤한 노드에게 매초 핑을 보내고, 각 노드마다 발송되고 핑 패킷과 수신된 퐁 패킷의 전체 수는 클러스터 내의 노드 수와 관계없이 일정한 양이다.

그러나 모든 노드는 NODE_TIMEOUT시간의 절반 이상의 시간 동안, 핑을 보내지 않았거나, 퐁을 수신한 적이 없는 다른 모든 노드에게 핑을 하도록 한다. NODE_TMEOUT이 경과되기 전에, 현재 TCP 커넥션에서 문제가 있어서, 접근할 수 없는 노드로 간주하지 않도록 하기 위해서, 다른 노드와 TCP 링크를 재연결하려고 한다.

만약 NODE_TIMEOUT가 작은 수치로 설정이 되었고, 노드의 수 (N)이 매우 클 때, 전역적으로 교환되는 메시지의 수는 상당할 수 있는데, 이것은 모든 노드가 NODE_TIMEOUT시간의 절반마다 새로운 정보가 없는 다른 모든 노드에게 핑을 보내려고 하기 때문이다.

예를 들어, 100개 노드의 클러스터가 있고, 노드 타임아웃이 60초로 설정이 되었을 때, 각 노드는 매 30초마다 99개의 핑을 보내려고 할 것이고, 전체 핑의 수는 초당 3.3개일 것이다. 100개의 노드를 곱하면, 전체 클러스터에서 초당 330개의 핑이 발생한다.

메시지의 수를 줄일 수 있는 방법이 있지만, 현재 레디스 클러스터 장애 감지에서 사용되는 대역폭에 대해서 보고된 이슈는 없고, 그래서 지금은 명확하고 직접적인 디자인이 사용된다. 심지어 위의 예에서 초당 330개의 교환되는 패킷은 100개의 서로 다른 노드에서 균등하게 나누어지며, 그래서 각 노드가 수신하는 트래픽은 허용 가능하다.

Heartbeat packet content

핑/퐁 패킷은 모든 타입의 패킷(예를 들어, 페일오버의 투표를 요청하는 등)의 공통적인 헤더와, 핑/퐁 패킷에서 지정하는 특별한 가십 프로토콜 섹션을 포함한다.

공통 헤더는 다음과 같은 정보를 가진다:
참고: https://github.com/redis/redis/blob/f07dedf73facfed5044efaf2a7a780581bf73ffa/src/cluster.h#L272

  • 노드 ID, 160비트의 의사난수(pseudorandom) 문자열. 노드가 생성될 때 처음 할당되고, 레디스 클러스터 노드의 운영되는 동안 동일하게 유지된다.
  • 전송하는 노드의 currentEpochconfigEpoch 필드. 이것은 레디스 클러스터가 분산 알고리즘을 갖추기 위해서 사용된다(이것은 다음 섹션에서 상세하게 설명한다). 만약 노드가 리플리카이면, configEpoch는 자신의 마스터 노드의 가장 최근의 configEpoch이다.
  • 노드 플래그. 노드가 리플리카인지, 마스터인지, 그리고 기타 단일 비트(single-bit)의 노드 정보를 표시한다.
  • 전송하는 노드가 담당하는 해시 슬롯의 비트맵. 리플리카인 경우에는 자신의 마스터가 담당하는 슬롯의 비트맵.
  • 전송하는 노드의 TCP 기반의 포트. 이것은 레디스가 클라이언트의 커맨드를 반아들이기 위해서 사용하는 포트이다.
  • 클러스터 포트. 레디스가 노드간의 커뮤니케이션을 위해서 사용하는 포트이다.
  • 전송하는 노드 관점에서의 클러스터의 상태. down 또는 ok.
  • 전송하는 노드가 리플리카일 때의 마스터 노드 ID.

핑/퐁 패킷은 또한 가십 섹션을 포함한다. 이 섹션은 수신자에게 전송하는 노드가 클러스터내의 다른 노드들에 대해서 어떻게 판단하고 있는지에 대한 일람을 전달한다. 가십 섹션은 전송하는 노드가 알고 있는 노드 집합 중에서 몇 개의 랜덤한 노드에 대한 정보만을 포함한다. 가십 섹션에서 언급되는 노드의 수는 클러스터 사이즈에 비례한다.

가십 섹션에 추가되는 모든 노드는 다음의 필드가 보고된다.

  • 노드 아이디
  • 노드의 IP와 포트
  • 노드의 플래그

가십 섹션은 수신하는 노드는 송신하는 노드의 관점에서의 다른 노드들의 상태에 관한 정보를 얻을 수 있다. 이것은 장애를 탐지하고, 클러스터 내의 다른 노드를 발견하는 것에 모두 유용하다.

Failure detection

레디스 클러스터 실패 탐지(Redis Cluster failure detection)는 과반 수의 노드로부터 어떤 마스터나 리플리카 노드가 더 이상 접근할 수 없는 것을 인식하기 위해서, 그 다음 리플리카를 마스터로 승격시킴으로써 대응한다. 리플리카 프로모션이 불가능하면, 클러스터는 클라이언트로부터의 쿼리 수신을 중지하기 위해서 에러 상태로 전환된다.

이미 언급한대로, 모든 노드는 이미 알고 있는 다른 노드들과 관련된 플래그의 목록을 가진다. 장애 탐지를 위해서 사용되는 PFAILFAIL이라는 2개의 플래그가 있다. PFAIL장애 가능성(Possible failure)을 의미하며, 승인되지 않은 실패의 타입이다. FAIL은 노드가 실패하고 있고, 고정된 시간 내에 과반수 이상의 마스터에 의해서 이 상태가 확인이 되었다는 것을 의미한다.

PFAIL flag:

NODE_TIMEOUT시간 보다 더 오랜 시간동안 접속할 수 없으면, 어떤 한 노드는 또 다른 노드가 NODE_TIMEOUT시간 보다 더 오랜 시간동안 접속할 수 없으면 PFAIL로 플래그를 지정한다. 타입에 관계없이 마스터와 리플리카 노드 모두 PFAIL로 플래그가 지정될 수 있다.

레디스 클러스터 노드의 접근 불가(non-reachability)의 개념은 NODE_TIMEOUT보다 더 긴 시간동안 보류중인 (보냈지만 아직 응답을 아직 받지 못한) 액티브 핑 (active ping)이 있다라는 것이다. 이 메커니즘이 동작하기 위해서 NODE_TIMEOUT은 네트워크 왕복 시간(round trip time)과 비교해서 반드시 더 큰 값이 되어야 한다. 일반적인 오퍼레이션동안 신뢰성을 더하기 위해서, 핑에 대한 응답없이 NODE_TIMEOUT의 절반의 시간이 경과하자마자 클러스터 내의 노드는 다른 노드와 다시 연결하려고 시도할 것이다. 이 메커니즘은 커넥션이 살아있는 상태로 유지하려고 하고, 연결이 끊긴 커넥션에 대해서 노드 간에 잘못된 오류를 보고하지 않도록 한다.

FAIL flag:

PFAIL플래그만으로는 각 노드가 다른 노드들에 대해서 가지는 로컬 정보일 뿐, 리플리카의 승격을 발생시키기에는 충분하지 않다. 어느 한 노드가 다운된 것으로 간주되려면 PFAIL 조건은 FAIL 조건으로 에스컬레이션되어야 할 필요가 있다.

이 문서의 노드 하트 비트 섹션에서 설명한대로, 모든 노드는 몇 개의 랜덤한 알려진 노드에 대한 상태를 포함해서 가십 메시지를 다른 모든 노드에게 전송한다. 모든 노드는 결국 다른 모든 노드에 대한 노드 플래그의 집합을 받게 된다. 이렇게 모든 노드는 발견한 장애 상태에 대해서 다른 노드로 신호를 보내는 메커니즘을 가진다.

다음의 조건들의 집합을 충족하면, PFAIL 상태는 FAIL로 에스컬레이션된다.

  • 임의의 노드 A는 PFAIL 플래그가 설정된 또 다른 노드 B에 대한 정보를 가지고 있다.
  • 노드 A는 가십 섹션을 통해서 클러스터 내의 과반수의 마스터의 관점에서의 B의 상태에 관한 정보를 수집했다.
  • 과반수의 마스터는 NODE_TIMEOUT * FAIL_REPORT_VALIDITY_MULT 시간 내에 PFAIL이나 FAIL상태를 신호했다. (유효성 계수(validity factor)는 현재의 구현에서 2로 설정되어 있고, 그래서 이것은 단지 NODE_TIMEOUT시간의 2배이다.)

위의 조건 모두 참일 때, 노드 A는 아래와 같이 동작할 것이다.

  • 노드를 FAIL로 표시해둔다.
  • (하트 비트 메시지 내에서 FAIL 상태가 아닌) FAIL 메시지를 접속 가능한 모든 노드에게 전송한다.

이미 PFAIL 상태로 노드가 플래그로 지정되어 있는지 아닌지와 관계없이, FAIL메시지는 수신하는 모든 노드가 해당 노드를 FAIL 상태로 표시해두도록 한다.

FAIL플래그는 대부분 단방향이다. 이것은 어떤 노드가 PFAIL에서 FAIL로는 바뀔 수 있지만, FAIL 플래그는 오직 다음과 같은 상황에서만 해제된다.

  • 노드는 이미 접근이 가능하고, 리플리카 노드이다. 이러한 경우 리플리카는 페일오버되지 않기 때문에, FAIL 플래그는 해제될 수 있다.
  • 노드는 이미 접근이 가능하고, 어떤 슬롯도 처리하지 않는 마스터 노드이다. 이러한 경우 슬롯이 없는 마스터는 클러스터에 실제 참여하고 있지 않고, 클러스터에 참여하기 위해서 구성이 변경되기(configured)를 기다리고 있기 때문에, FAIL플래그는 해제될 수 있다.
  • 노드는 이미 접근이 가능하고, 마스터 노드이지만, 감지가 가능한 어떤 리플리카의 승격도 없이, 오랜 시간(NODE_TIMEOUT을 N번)이 경과되었다.

PFAIL -> FAIL 변경은 합의의 형태를 사용하지만, 사용되는 합의는 약하다는 점을 알아두면 좋다.

  1. 노드들은 일정 시간 동안 다른 노드들의 뷰를 수집하고, 마스터 노드의 과반수가 동의를 할 필요가 있다고 하더라도, 실제로 이것은 다른 노드들로부터 각각 다른 시간에 수집된 상태일 뿐이고, 주어진 시간동안에 마스터의 과반수가 동의한 것을 보장하지도, 요구하지도 않는다. 하지만 오래된 장애 보고는 폐기하므로, 마스터의 과반수에 의해서 시간의 구간 내에 장애가 신호된다.
  2. FAIL상태를 감지하는 모든 노드는 FAIL 메시지를 이용해서 클러스터내의 다른 노드에게 상태를 강제로 적용하지만, 메시지가 모든 노드에 도달할 것이라는 것을 보장할 방법은 없다. 예를 들어, 노드가 FAIL상태를 발견할 수 있지만, 네트워크 파티션 때문에 다른 어떤 노드에도 도달할 수 없을 것이다.

그러나 레디스 클러스터 장애 감지는 라이브니스(liveness) 요구사항이 있다. 결국 모든 노드는 주어진 노드의 상태에 관해서 동의해야 한다. 스플릿 브레인으로부터 비롯될 수 있는 2가지의 케이스가 있다. 일부 소수의 노드가 어떤 노드를 FAIL 상태로 인지하거나, 또 다른 소수의 노드는 FAIL 상태로 인지하지 않는다. 두 가지 경우 모두 결국 클러스터는 주어진 노드에 대해서 한 가지의 관점을 가질 것이다.

Case 1: 만약 과반수의 마스터가 어떤 노드를 FAIL로 플래그를 지정했다면, 장애 탐지와 그것이 발생시키는 연쇄 효과(chain effect) 때문에, 지정된 시간 내에 충분히 장애가 보고될 것이므로, 결국 모든 다른 노드는 그 마스터를 FAIL로 플래그를 기록할 것이다.

Case 2: 오직 소수의 마스터만 어떤 노드를 FAIL로 플래그를 지정했다면, (모든 노드가 결국 승격에 대해서 알게 하기 위해서 정규적인 알고리즘을 사용하기 때문에) 리플리카 승격은 일어나지 않고, 모든 노드는 위에서 서술한 FAIL상태를 해제하는 규칙에 따라 FAIL 상태를 해제할 것이다. (예를 들어 NODE_TIMEOUT이 N번 경과한 이후에도 승격이 없었던 경우)

FAIL플래그는 리플리카의 승격을 위해 알고리즘의 안전한 부분을 실행하기 위한 트리거로만 사용된다. 이론적으로 리플리카는 독립적으로 동작하고, 마스터가 접근할 수 없게 되면 리플리카 승격을 시작하고, 만약 마스터가 과반수의 의해서 접근이 가능하다면 다른 마스터들이 승인을 제공하기를 거부할 때까지 기다린다. 그러나 PFAIL -> FAIL 상태의 추가적인 복잡성, 약한 합의, 그리고 FAIL 메시지가 클러스터의 접근 가능한 부분에 가장 짧은 시간내에 상태를 전파시키는 것은 실질적인 이점이 있다. 이러한 메커니즘 때문에, 만약 클러스터가 에러 상태라면, 일반적으로 모든 노드는 거의 동시에 쓰기를 멈출 것이다. 이것은 레디스 클러스터를 사용하는 어플리케이션의 관점에서 매력적인 기능이다. 또한, 로컬 시스템의 문제(마스터는 다른 마스터 노드의 과반수에 의해 접근이 가능한 것과 달리)때문에 자신의 마스터에 접근할 수가 없는 리플리카가 시작하는 잘못된 투표는 방지된다.

0개의 댓글