리더 선출과 복제
- 6.1 Raft leader election
- 6.2 Practical considerations
- 6.3 State machine replication
- 6.4 Consensus
- 6.5 Consistency models
- 6.6 Chain replication
분산 시스템에서 데이터를 복제하려면 "누가 진본을 관리하지?"라는 질문부터 답해야 해. 리더를 선출하고, 그 리더가 복제를 주도하는 거야. 리더 선출이 복제를 가능하게 하고, 복제가 가용성과 확장성을 만들어. 이 두 개념은 떼어놓을 수 없지.
Raft의 리더 선출부터 보자. 세 가지 상태의 상태 머신으로 돌아가 — Follower, Candidate, Leader. 시간은 election term(임기)이라는 연속 정수로 나뉘어. 시작하면 다 follower야. 리더의 하트비트를 타임아웃 안에 못 받으면 follower가 term을 올리고 candidate로 전환해. 자신에게 투표한 뒤 다른 프로세스에 투표를 요청하지. 결과는 세 가지 중 하나야 — 과반수가 투표하면 리더 당선, 다른 프로세스가 먼저 당선됨(더 높은 term의 하트비트를 받으면 follower로 복귀), 분할 투표(여러 follower가 동시에 candidate가 돼서 아무도 과반수를 못 얻음). 분할 투표를 줄이려고 랜덤 타임아웃을 써 — 각 프로세스의 타임아웃이 다르면 동시에 candidate가 될 확률이 줄어들거든.
실무에서 Raft를 직접 구현할 일은 거의 없어. 대신 etcd, ZooKeeper 같은 조정 서비스가 제공하는 linearizable compare-and-swap + TTL을 써. 프로세스가 **lease(임차)**를 획득하면 리더가 되고, 갱신을 멈추면 lease가 만료돼서 다른 프로세스가 이어받는 방식이지. 근데 lease만으로는 **상호 배제(mutual exclusion)**가 완벽하지 않아. 프로세스가 GC 정지(stop-the-world)로 멈춰있는 사이에 lease가 만료될 수 있거든. 그래서 버전 번호 + compare-and-swap으로 보완해 — 리소스를 업데이트할 때 버전이 안 바뀌었는지 원자적으로 확인하는 거야. 리더의 약점도 알아야 해. 모든 작업이 리더를 통과하니까 확장성 병목이 되고 **단일 장애점(SPOF)**이야.
리더를 세웠으니 이제 복제를 하자. Raft의 복제는 **상태 머신 복제(state machine replication)**에 기반해. 리더가 상태를 변경하는 연산을 로그에 기록하고, 이 로그를 follower들에게 복제해. 모든 follower가 같은 순서로 같은 연산을 실행하면 같은 상태에 도달하지. 구체적으로 리더가 연산을 로컬 로그에 추가하고, AppendEntries 요청으로 follower들에게 보내고, 과반수가 ACK하면 커밋돼서 리더가 실행해. 2N+1 follower가 있으면 최대 N개 장애를 견딜 수 있어.
상태 머신 복제를 풀면 사실 합의(consensus) 문제를 푼 거야. 프로세스 그룹이 하나의 값에 동의하는 것 — 모든 정상 프로세스가 결국 동의하고, 결정은 모든 곳에서 동일하고, 합의된 값은 누군가가 제안한 것이어야 해.
복제된 데이터에 읽기를 누가 처리하냐에 따라 일관성이 달라져. **Linearizability(strong consistency)**는 모든 읽기/쓰기가 리더를 통과해서 단일 복사본에 있는 것처럼 보이지. 가장 강력하지만 느려. Sequential consistency는 follower도 읽기를 처리하되 클라이언트를 특정 follower에 고정해. 처리량은 높지만 그 follower가 죽으면 해당 클라이언트가 접근 불가야. Eventual consistency는 아무 follower나 읽기 처리. 쓰기가 멈추면 결국 수렴하지. 여기서 CAP 정리가 나와 — 네트워크 파티션 시 강한 일관성과 가용성 중 택1. 근데 실제로는 이진이 아니라 스펙트럼이야. PACELC 정리는 파티션 없이도 지연 시간과 일관성의 트레이드오프가 있다고 확장하지.
마지막으로 Raft와 다른 토폴로지인 Chain replication. 프로세스가 체인으로 배열돼서 쓰기는 head에서 받아 체인을 따라 전파하고, tail에 도달하면 커밋. 읽기는 tail에서 처리해 — 추가 조정 없이 즉시 응답할 수 있지. 장애 처리는 별도 컨트롤 플레인에 위임하고, 데이터 플레인에서는 리더가 필요 없어서 Raft보다 처리량이 높아. 단점은 쓰기 지연이 체인 전체를 통과해야 해서 길고, 한 노드가 느려지면 전체가 느려진다는 거야.
정리
6장 읽고 기억할 거 세 가지:
- Raft는 리더 선출 + 상태 머신 복제를 하나로 묶어 — 과반수 투표로 리더를 세우고, 과반수 ACK으로 로그를 커밋해. 실무에서는 etcd/ZooKeeper의 lease를 쓰지.
- CAP/PACELC — 강한 일관성은 가용성과 지연 시간의 대가를 치러. 일관성 모델은 이진이 아니라 linearizability → sequential → eventual의 스펙트럼이야.
- Chain replication은 쓰기/읽기 경로를 head/tail로 분리해서 리더 병목을 제거해 — 컨트롤/데이터 플레인 분리의 첫 등장이지.