Foundry
분산 시스템
심화

합의 알고리즘과 리더 선출

과반수가 정답을 만든다. Raft로 보는 리더 선출

합의 알고리즘과 리더 선출

무엇에 합의하는가

  • 여러 노드가 같은 명령을 같은 순서로 적용하도록 로그의 순서에 합의한다
  • 그 결과 모든 노드가 동일한 상태에 도달한다(복제 상태 기계)

Raft의 세 조각

요소내용
리더 선출하트비트가 끊기면 후보가 term을 올려 투표를 요청하고, 과반 득표로 리더가 된다
로그 복제쓰기는 리더만 받아 팔로워에 복제하고, 과반에 복제되면 커밋으로 확정한다
안전성최신 로그를 가진 노드만 리더가 될 수 있어 커밋된 항목이 사라지지 않는다

쿼럼과 노드 수

다섯 대에서 과반 셋을 두 번 고르면 반드시 한 대가 겹친다 노드 5대에서 과반은 3대 먼저 정한 쪽 나중에 정하려는 쪽 가운데 한 대가 양쪽에 다 들어간다 겹치는 그 한 대가 앞의 결정을 알고 있어 뒤집지 못하게 한다 그래서 과반이면 두 결정이 동시에 성립할 수 없다 4대에서 2대씩 고르면 겹치지 않을 수 있어 과반이 아니다
  • 과반수는 N/2 + 1. 3노드는 1대, 5노드는 2대 장애까지 견딘다
  • 짝수 노드는 이득이 없다. 4노드도 허용 장애는 1대뿐이므로 홀수로 구성한다
  • 과반을 모으지 못하면 쓰기를 멈춘다. CAP 관점에서 CP 성향이다

왜 직접 만들지 않나

합의는 정상 경로가 아니라 이상 경로에서 무너집니다. 그 경로가 많습니다.

리더가 살아 있는데 네트워크만 끊겨 둘이 리더가 되는 경우
투표가 매번 갈려 리더가 안 정해지는 경우
옛 리더가 돌아와 옛 결정을 밀어 넣는 경우

셋 다 평소에는 안 보입니다. 그래서 직접 만든 합의는 사고가 난 뒤에 틀렸다는 것을 압니다. 검증된 구현을 쓰는 쪽이 옳습니다.

노드 수를 어떻게 정하나

과반이 필요하므로 홀수가 낭비가 없습니다.

노드견딜 수 있는 고장과반
31대2
41대3
52대3
73대4

3과 4가 같은 것이 요점입니다. 한 대를 더 넣어도 견디는 고장 수는 그대로인데 과반이 늘어 쓰기가 느려집니다. 그리고 노드를 늘릴수록 결정마다 기다릴 상대가 많아집니다.

실무 포인트

  • 직접 구현할 일은 드물다. etcd, ZooKeeper(ZAB), Kafka KRaft가 이미 사용 중이다
  • 리더가 교체되는 수백 ms 동안 쓰기가 실패한다. 클라이언트 재시도가 전제다
  • 분단이 생기면 소수 쪽은 리더가 될 수 없다. term과 과반 규칙이 split-brain을 막는 장치다
  • 멀티 리전 배치에서는 노드 간 왕복 지연이 커밋 지연으로 그대로 드러난다
면접에서 이렇게 나옵니다

Q.리더 선출이 필요한 이유와 과정을 설명해주세요

여러 노드가 같은 데이터를 들고 있을 때, 누구 말이 맞는지 정하는 기준이 필요합니다. 쓰기를 한 곳으로 모으면 순서가 하나로 정해집니다. 그 한 곳이 리더입니다.

리더가 없으면 두 노드가 같은 자리에 다른 값을 쓰고, 나중에 어느 것이 맞는지 알 수 없게 됩니다. 시계로 판단하려 해도 서버마다 시계가 다릅니다.

선출 과정은 세 조각입니다.

단계내용
임기선거마다 번호가 하나씩 오른다. 옛 임기의 말은 무시된다
후보리더 소식이 일정 시간 없으면 스스로 후보가 되어 표를 청한다
과반과반의 표를 얻으면 리더가 된다. 못 얻으면 다음 임기로 넘어간다

임기 번호가 옛 리더 문제를 해결합니다. 네트워크가 끊겼다 돌아온 옛 리더가 명령을 내려도, 임기가 낮으므로 받는 쪽이 거절합니다.

대기 시간을 노드마다 다르게(무작위로) 두는 것도 중요합니다. 전부 동시에 후보가 되면 표가 갈려 아무도 과반을 못 얻고, 그게 반복되면 리더가 영영 안 정해집니다.

흔한 실수: 리더 선출을 직접 구현하는 것. 정상 경로는 쉬워 보이지만 무너지는 곳은 이상 경로입니다. 옛 리더의 복귀, 표 분산, 반쪽 네트워크. 세 가지 모두 평소에는 안 보이고 사고가 난 뒤에 틀렸다는 것을 알게 됩니다.

Q.노드를 5개로 구성하면 몇 대 장애까지 견디나요? 짝수는 왜 피하나요?

5대면 2대까지 견딥니다. 과반인 3대가 살아 있어야 결정을 내릴 수 있기 때문입니다.

노드과반견디는 고장
321
431
532
642
743

짝수를 피하는 이유가 이 표에 있습니다. 3과 4가 똑같이 1대만 견딥니다. 한 대를 더 넣었는데 내결함성은 그대로이고, 과반이 2에서 3으로 늘어 쓰기마다 기다릴 상대만 늘어납니다. 6과 7도 같은 관계입니다. 짝수는 돈과 지연을 더 쓰고 얻는 것이 없습니다.

무한정 늘리지 않는 이유도 같습니다. 노드가 늘수록 견디는 고장은 늘지만 결정마다 더 많은 응답을 기다려야 해서 쓰기가 느려집니다. 그래서 5가 흔한 타협점입니다.

투표에만 참여하고 데이터를 안 들고 있는 노드를 두는 방식도 있습니다. 짝수 구성에서 동수를 깨는 용도인데, 그 노드가 죽으면 다시 짝수가 되므로 근본 해법은 아닙니다.

흔한 실수: 가용성을 올리려고 노드를 짝수로 늘리는 것. 합의 클러스터에서 노드 수는 과반을 만들 수 있는가로 정해집니다. 대수를 늘리는 것과 견디는 고장 수가 비례하지 않는 유일한 구성입니다.

Q.리더 교체 순간 애플리케이션은 무엇을 겪나요?

교체는 순간이 아니라 구간입니다. 그 사이 쓰기가 거절되거나 멈춥니다.

시점애플리케이션이 겪는 것
리더가 죽음요청이 응답 없이 타임아웃까지 매달린다
선출 중쓰기가 거절된다. 읽기는 되기도 한다
새 리더 확정클라이언트가 새 주소를 알기까지 더 걸린다
직후옛 리더에 붙어 있던 연결이 끊어져 재연결이 몰린다

첫 줄이 가장 아픕니다. 죽은 리더는 거절하지 않고 그냥 대답하지 않습니다. 그래서 클라이언트 타임아웃이 길면 그 시간만큼 요청이 쌓입니다. 합의 계층이 몇 초에 복구돼도 애플리케이션은 훨씬 오래 느립니다. 타임아웃을 짧게 잡는 것이 복구 시간을 정합니다.

준비할 것은 셋입니다. 쓰기 실패를 재시도하되 멱등하게 만들 것, 재시도 간격을 벌려 새 리더에 몰리지 않게 할 것, 그리고 이 구간에 사용자에게 무엇을 보여줄지 정해 둘 것.

읽기는 설정에 따라 다릅니다. 복제본에서 읽도록 허용했다면 이 구간에도 읽기는 되지만 조금 옛 데이터입니다. 그 거래를 받아들일지 미리 정해야 합니다.

흔한 실수: 합의 계층의 복구 시간만 보고 "몇 초면 된다" 고 보는 것. 사용자가 겪는 시간은 거기에 클라이언트 타임아웃과 재연결 몰림이 더해진 값입니다.

Q.네트워크 분단에서 split-brain을 어떻게 막나요?

과반 규칙이 그 자체로 방어입니다. 양쪽이 동시에 과반이 될 수 없기 때문입니다.

5대가 3대와 2대로 갈리면, 3대 쪽만 리더를 뽑고 2대 쪽은 아무 결정도 못 합니다. 2대 쪽에 옛 리더가 있어도 과반의 응답을 못 받으므로 쓰기를 확정하지 못합니다.

쪽할 수 있는 것
과반이 있는 쪽새 리더를 뽑고 쓰기를 계속한다
과반이 없는 쪽쓰기 불가. 읽기는 옛 데이터일 수 있다

그래서 남는 위험은 소수 쪽에서 읽는 것입니다. 옛 리더가 자기가 아직 리더인 줄 알고 읽기를 처리하면 낡은 값을 줍니다. 리더 임대(일정 시간마다 리더 자격을 갱신)로 막는데, 그 시간 동안은 여전히 낡을 수 있습니다.

정말 위험한 구성은 따로 있습니다. 분단된 양쪽이 각자 별개 클러스터로 재구성되는 경우입니다. 사람이 장애 대응 중 소수 쪽 노드 수를 줄여 과반을 만들어 주면 이것이 일어납니다. 양쪽이 각자 리더를 갖고 각자 쓰기를 받습니다.

흔한 실수: 장애 중에 급해서 노드 수 설정을 손으로 바꾸는 것. 과반 규칙이 지켜 주던 유일한 보호막을 사람이 직접 걷어내는 행동이고, 복구된 뒤 두 갈래 데이터를 합칠 방법이 없습니다.

먼저 스스로 답해보고 아래 답변과 견줘보세요. 막히는 부분은 문제로 확인할 수 있어요.

읽었으면 문제로 확인해보세요

분산 시스템 문제를 풀면 틀린 문제가 자동으로 노트에 쌓입니다. 가입 없이 5문제를 먼저 풀어볼 수도 있어요.