본문 바로가기
Backend2026년 8월 15일6분 읽기

리더 선출(Leader Election) — 분산 시스템에서 대장 하나를 안전하게 뽑기

YS
김영삼
조회 7
리더 선출(Leader Election) — 분산 시스템에서 대장 하나를 안전하게 뽑기

리더 선출(Leader Election)은 여러 대의 노드로 이뤄진 분산 시스템에서, 특정 작업을 책임질 단 하나의 노드(리더)를 합의로 뽑는 과정입니다. "누가 대장인지"를 모두가 동의하게 만드는 것이 핵심이고, 리더가 죽으면 남은 노드끼리 다시 뽑아야 합니다.

왜 굳이 대장이 필요할까요. 예를 들어 스케줄러가 여러 대 떠 있는데 배치 잡을 한 번만 돌려야 한다면, 아무 조율 없이는 세 대가 동시에 같은 잡을 세 번 실행합니다. 이럴 때 "지금은 네가 리더다"를 한 노드에만 부여하면, 나머지는 대기하고 리더만 일합니다. 단일 쓰기 지점, 순서 보장, 중복 실행 방지 — 이 세 가지가 리더를 두는 대표 이유입니다.

순진하게 접근하면 반드시 만나는 벽

"그냥 DB에 leader='node-1' 한 줄 넣으면 되지 않나?" 처음엔 저도 그렇게 생각했습니다. 문제는 리더가 죽었는지 어떻게 아느냐입니다. 네트워크가 잠깐 끊긴 건지, 진짜 죽은 건지 구분할 수 없죠. 이걸 잘못 판단하면 최악의 사고, 스플릿 브레인(split-brain)이 터집니다. 두 노드가 서로 자기가 리더라고 믿고 동시에 쓰기를 하는 상황입니다.

분산 시스템에서 "저 노드가 죽었다"와 "저 노드로 가는 네트워크가 느리다"는 원격에서 구분이 불가능합니다. 리더 선출의 모든 어려움이 사실 이 한 문장에서 나옵니다.

리스(lease)와 임기(term)라는 안전장치

실무의 해법은 리스(임대 시간)입니다. 리더 자격에 유효기간을 붙이는 겁니다. 리더는 주기적으로 하트비트를 보내 리스를 갱신하고, 갱신에 실패하면 일정 시간 뒤 자격이 자동 만료됩니다. 새 리더는 이전 리스가 확실히 만료된 뒤에만 일을 시작합니다. 여기에 임기 번호(term/fencing token)를 단조 증가시켜, 뒤늦게 깨어난 옛 리더의 명령을 "낡은 임기"라며 거부합니다.

# 리스 기반 리더의 뼈대 (의사코드)
LEASE = 15   # 초
while True:
    ok = kv.compare_and_set(
        key="leader", expect=my_id or None,
        value=my_id, ttl=LEASE)
    if ok:
        act_as_leader()          # 리더 일 수행
        sleep(LEASE / 3)         # 여유있게 갱신
    else:
        act_as_follower()        # 대기
        sleep(1)

여기서 compare_and_set과 TTL이 원자적으로 보장돼야 합니다. 이걸 직접 구현하려 들면 지옥이 열립니다. 그래서 대부분 이미 합의 알고리즘을 검증받은 도구에 위임합니다.

직접 만들지 말고 빌려 쓰자

리더 선출은 본질적으로 합의(consensus) 문제이고, 이건 Raft나 Paxos 같은 알고리즘이 다루는 영역입니다. 실무에서 직접 Raft를 구현할 일은 거의 없습니다. 아래 도구들이 검증된 선출 기능을 제공합니다.

도구기반쓰는 상황
etcd / ConsulRaftTTL 키 + 락 API로 선출
ZooKeeperZAB순차 ephemeral 노드
KubernetesLease 오브젝트컨트롤러 HA 배포
RedisSET NX PX가벼운 단일 리더(주의 요망)

쿠버네티스에서 컨트롤러를 여러 레플리카로 띄우면서 하나만 활성화하고 싶을 때, 흔히 쓰는 게 바로 Lease 오브젝트 기반의 leaderelection 패키지입니다. 저는 크론 잡 중복 실행을 막을 때 이걸로 여러 번 재미를 봤습니다.

여전히 조심할 것들

  • 펜싱 토큰을 꼭 검증하세요. 리더 자격만 얻고 실제 쓰기 시점에 토큰을 확인 안 하면, GC 스톱이나 긴 지연으로 잠깐 멈췄던 옛 리더가 깨어나 낡은 명령을 밀어넣을 수 있습니다.
  • 리스 시간과 하트비트 주기의 균형. 리스가 너무 짧으면 순간 지연에도 리더가 튕기고, 너무 길면 진짜 장애 시 새 리더 전환이 느립니다.
  • Redis 단일 인스턴스 락은 합의가 아닙니다. 장애 조치(failover) 순간에 두 노드가 동시에 락을 쥘 수 있으니, 강한 보장이 필요하면 Raft 기반 도구를 쓰세요.

자주 묻는 질문

스플릿 브레인을 완전히 막을 수 있나요?

선출 단계에서 다수결(과반수 정족수, quorum)을 요구하면 두 리더가 동시에 과반을 얻을 수 없어 논리적으로 막힙니다. 여기에 펜싱 토큰으로 실제 쓰기 시점까지 검증하면 실무적으로 안전합니다. 핵심은 "쓰기 직전에 자격을 재확인"하는 것입니다.

리더가 없는 설계는 불가능한가요?

가능합니다. Dynamo 계열의 리더리스(leaderless) 복제나 CRDT 기반 설계는 리더 없이 동작합니다. 다만 강한 순서 보장이나 단일 쓰기 지점이 필요하면 리더 방식이 단순하고 강력합니다. 문제 성격에 따라 고르세요.

Raft와 Paxos 중 뭘 배워야 하나요?

이해 목적이면 Raft를 추천합니다. 리더 선출·로그 복제·안전성을 명확한 상태로 나눠 설명해서 훨씬 읽기 쉽습니다. Paxos는 더 오래됐지만 직관적이지 않아 변형이 많습니다.

선출에 걸리는 시간은 얼마나 되나요?

대개 리스 만료 시간에 지배됩니다. 리더 장애 후 새 리더가 뜨기까지 리스 TTL 정도(수 초~십수 초)가 걸립니다. 이 공백 동안 리더 전용 작업이 잠깐 멈추는 것을 감안해 설계해야 합니다.

댓글 0

아직 댓글이 없습니다.
Ctrl+Enter로 등록