← 학습 카테고리

Learn

Kafka

42개 모듈 · 현재 12번째

Kafka 모듈 12/42 kafka-learn-12

신뢰성 보장과 브로커 설정 (복제 팩터, unclean leader election, min.insync.replicas)

Kafka: The Definitive Guide (O'Reilly, 2017, 1st Edition) — Neha Narkhede, Gwen Shapira, Todd Palino Chapter 6: Reliable Data Delivery — Reliability Guarantees / Replication / Broker Configuration (pp.115-121)

신뢰성은 나중에 덧붙일 수 없고 설계 첫 단계부터 넣어야 하는 성질이며, 특정 컴포넌트가 아니라 시스템 전체의 속성이다. 이 모듈에서는 Kafka가 명시적으로 약속하는 네 가지 보장, in-sync 판정 기준, 그리고 신뢰성을 좌우하는 세 가지 브로커 설정 — replication.factor, unclean.leader.election.enable, min.insync.replicas — 의 트레이드오프를 다룬다.

Kafka가 약속하는 네 가지 보장

신뢰성은 보통 '보장(guarantee)'의 형태로 이야기한다. 여러 상황에서 시스템이 반드시 지키는 동작이라는 뜻이다. 관계형 데이터베이스의 ACID가 대표적이다. 사용자들이 관계형 DB에 가장 중요한 애플리케이션을 맡기는 이유는, 시스템이 무엇을 약속하고 어떤 조건에서 어떻게 동작하는지 정확히 알기 때문이다.

Apache Kafka는 다음을 보장한다.

- 파티션 내 메시지 순서를 보장한다. 같은 프로듀서가 같은 파티션에 메시지 A 다음 B를 썼다면, B의 오프셋이 A보다 크고 컨슈머는 A 다음에 B를 읽는다. - 생산된 메시지는 그 파티션의 모든 in-sync 복제본에 기록되었을 때 '커밋된' 것으로 간주된다(디스크에 flush되었다는 뜻은 아니다). 프로듀서는 완전히 커밋되었을 때, 리더에 기록되었을 때, 또는 네트워크로 보내기만 했을 때 중 어느 시점에 확인 응답을 받을지 선택할 수 있다. - 커밋된 메시지는 복제본이 최소 하나만 살아 있어도 유실되지 않는다. - 컨슈머는 커밋된 메시지만 읽을 수 있다.

이 기본 보장들은 신뢰성 있는 시스템을 만드는 재료이지만 그 자체로 시스템을 신뢰성 있게 만들어 주지는 않는다. 신뢰성에는 언제나 가용성, 높은 처리량, 낮은 지연, 하드웨어 비용과의 트레이드오프가 따르고, Kafka는 그 균형점을 설정으로 고르게 해 둔 것이다.

핵심 포인트

  • 파티션 내 순서 보장 (같은 프로듀서·같은 파티션 기준)
  • '커밋됨'의 정의는 모든 ISR에 기록됨이며 디스크 flush를 의미하지 않는다
  • 커밋된 메시지는 복제본 하나만 살아 있어도 유실되지 않는다
  • 컨슈머는 커밋된 메시지만 읽는다

in-sync 판정의 세 가지 조건

복제본은 다음 중 하나에 해당할 때 in-sync로 간주된다. 파티션의 리더이거나, 또는 다음을 모두 만족하는 팔로워다(괄호 안 수치는 책이 다루는 버전의 기본값이며 모두 설정 가능하다).

- ZooKeeper와 활성 세션을 유지하고 있다 — 최근 6초 안에 ZooKeeper에 하트비트를 보냈다 - 최근 10초 안에 리더로부터 메시지를 fetch했다 - 최근 10초 안에 리더의 가장 최신 메시지까지 fetch했다. 즉 리더로부터 메시지를 받고 있는 것만으로는 부족하고 지연이 거의 없어야 한다

복제본이 ZooKeeper 연결을 잃거나, 새 메시지를 가져오기를 멈추거나, 뒤처진 뒤 10초 안에 따라잡지 못하면 out-of-sync가 된다. out-of-sync 복제본은 ZooKeeper에 다시 연결되고 리더에 기록된 최신 메시지까지 따라잡으면 다시 in-sync가 된다. 일시적인 네트워크 문제였다면 금방 회복되지만, 복제본이 있던 브로커가 오래 내려가 있었다면 시간이 걸릴 수 있다.

복제본 하나가 in-sync와 out-of-sync 사이를 빠르게 오간다면 클러스터에 문제가 있다는 확실한 신호다. 흔한 원인은 브로커의 Java 가비지 컬렉션 설정 오류다. GC가 잘못 설정되면 브로커가 몇 초간 멈추고 그동안 ZooKeeper 연결을 잃어 out-of-sync로 판정되면서 이런 깜빡임이 나타난다.

참고로 약간 뒤처진 in-sync 복제본은 프로듀서와 컨슈머를 함께 느리게 만든다. 커밋되기 전에 모든 in-sync 복제본이 메시지를 받기를 기다리기 때문이다. 그런데 그 복제본이 아예 out-of-sync가 되면 더 이상 기다리지 않으므로 성능 영향은 사라진다. 함정은 in-sync 복제본 수가 줄어든 만큼 파티션의 실효 복제 팩터가 낮아져 다운타임과 데이터 유실 위험이 올라간다는 점이다.

핵심 포인트

  • in-sync 조건: ZooKeeper 세션 유지(6초) + 최근 10초 내 fetch + 최근 10초 내 최신 메시지까지 따라잡음
  • in-sync/out-of-sync 깜빡임은 대개 브로커 GC 설정 문제의 신호다
  • 느린 복제본이 out-of-sync가 되면 성능은 회복되지만 실효 복제 팩터가 낮아져 위험은 커진다

복제 팩터: 몇 개가 적당한가

토픽 레벨 설정은 `replication.factor`이고, 브로커 레벨에서 자동 생성 토픽의 기본값은 `default.replication.factor`다. 토픽이 만들어진 뒤에도 복제본을 추가하거나 제거해 복제 팩터를 바꿀 수 있다.

복제 팩터가 N이면 브로커 N-1대를 잃어도 토픽을 안정적으로 읽고 쓸 수 있다. 대신 브로커가 최소 N대 필요하고 데이터를 N벌 저장하므로 디스크도 N배가 든다. 결국 가용성을 하드웨어와 맞바꾸는 것이다.

적정값은 토픽이 얼마나 중요한지, 가용성에 얼마를 지불할 의사가 있는지에 달렸다. 브로커 한 대를 재시작하는 정상적인 운영 중에 특정 토픽이 잠시 불가용해도 괜찮다면 복제 팩터 1로도 충분할 수 있다. 다만 디스크·서버를 아끼는 대신 고가용성을 잃는 선택임을 관리자와 사용자도 함께 납득해야 한다.

복제 팩터 2면 브로커 한 대를 잃어도 괜찮다는 뜻이라 충분해 보이지만, 브로커 한 대를 잃는 것이 (특히 구버전 Kafka에서) 클러스터를 불안정한 상태로 만들어 컨트롤러 브로커를 한 대 더 재시작해야 하는 상황을 부를 수 있다. 그러면 운영 문제를 해결하기 위해 불가용을 감수해야 하는 곤란한 선택이 된다. 그래서 가용성이 중요한 토픽에는 복제 팩터 3을 권장하며, 드물게는 은행이 핵심 토픽을 복제본 5개로 운영하는 사례도 있다.

복제본의 배치도 매우 중요하다. Kafka는 기본적으로 파티션의 각 복제본을 서로 다른 브로커에 두지만, 그 브로커들이 모두 같은 랙에 있고 top-of-rack 스위치가 오작동하면 복제 팩터와 무관하게 파티션 가용성을 잃는다. 랙 단위 사고에 대비하려면 브로커를 여러 랙에 배치하고 `broker.rack` 설정으로 각 브로커의 랙 이름을 지정한다. 랙 이름이 설정되어 있으면 Kafka는 파티션의 복제본이 여러 랙에 흩어지도록 배치한다.

핵심 포인트

  • 복제 팩터 N이면 브로커 N-1대 손실까지 견딘다(대신 디스크도 N배)
  • 가용성이 중요한 토픽에는 3을 권장
  • 복제 팩터 2는 컨트롤러 재시작 등으로 인해 실질적으로 불안할 수 있다
  • broker.rack을 설정해 복제본을 여러 랙에 분산시켜야 랙 장애를 견딘다

unclean leader election: 데이터 일관성 대 가용성

이 설정은 브로커(사실상 클러스터 전역) 레벨에서만 제공되며 이름은 `unclean.leader.election.enable`이다. 책이 다루는 버전에서 기본값은 true였다(참고: 이후 버전에서 브로커 기본값은 false로 바뀌었으므로, 실제 운영 시에는 사용 중인 버전의 기본값을 반드시 확인해야 한다).

리더가 불가용해지면 in-sync 복제본 중 하나가 새 리더로 선택된다. 커밋된 데이터는 정의상 모든 in-sync 복제본에 존재하므로 이 선출은 데이터 유실이 없다는 의미에서 'clean'하다. 문제는 방금 사라진 리더 말고는 in-sync 복제본이 하나도 없을 때다. 이런 상황은 두 가지 시나리오로 발생한다.

- 복제본이 3개인 파티션에서 팔로워 2개가 불가용해진다(브로커 2대 크래시). 프로듀서는 계속 리더에 쓰고, 리더가 유일한 in-sync 복제본이므로 모든 메시지가 ack되고 커밋된다. 이제 리더까지 불가용해지고, out-of-sync 팔로워 중 하나가 먼저 올라오면 그 파티션의 유일한 가용 복제본이 out-of-sync 상태다. - 복제본이 3개인 파티션에서 네트워크 문제로 팔로워 2개가 뒤처져, 살아서 복제는 하고 있지만 더 이상 in-sync가 아니다. 리더가 유일한 in-sync 복제본으로 계속 메시지를 받다가 불가용해지면, 남은 두 복제본은 out-of-sync다.

어느 쪽이든 어려운 결정을 해야 한다. out-of-sync 복제본을 리더로 만들지 않으면, 옛 리더(=마지막 in-sync 복제본)가 돌아올 때까지 파티션은 오프라인으로 남는다. 메모리 칩 교체 같은 경우 이는 수 시간이 걸릴 수 있다. 반대로 out-of-sync 복제본을 리더로 만들면, 그 복제본이 out of sync였던 동안 옛 리더에 쓰인 메시지를 모두 잃고 컨슈머 쪽에도 불일치가 생긴다. 복제본 0과 1이 불가용한 동안 리더였던 복제본 2에 오프셋 100~200이 쓰였다고 하자. 이제 복제본 2가 사라지고 복제본 0이 돌아오면 복제본 0에는 0~100까지만 있다. 복제본 0을 리더로 만들면 프로듀서가 새 메시지를 쓰고 컨슈머가 읽을 수 있게 되면서 새 리더에는 전혀 다른 100~200이 생긴다. 어떤 컨슈머는 옛 100~200을, 어떤 컨슈머는 새 100~200을, 어떤 컨슈머는 둘이 섞인 것을 읽게 되어 다운스트림 리포트 같은 곳에서 심각한 결과를 낳는다. 게다가 복제본 2가 돌아와 새 리더의 팔로워가 되면, 현재 리더보다 앞선 자신의 메시지들을 삭제한다. 그 메시지들은 앞으로 어떤 컨슈머에게도 제공되지 않는다.

요약하면, out-of-sync 복제본의 리더 승격을 허용하면 데이터 유실과 불일치를 감수하는 것이고, 허용하지 않으면 원래 리더가 돌아올 때까지 가용성을 잃는 것이다. 데이터 품질과 일관성이 결정적인 시스템 — 은행이 대표적이다 — 에서는 보통 false로 꺼 둔다(대부분의 은행은 몇 분에서 몇 시간 결제를 처리하지 못하는 편이 결제를 잘못 처리하는 것보다 낫다고 본다). 반대로 실시간 클릭스트림 분석처럼 가용성이 더 중요한 시스템에서는 켜 두는 일이 흔하다.

핵심 포인트

  • unclean.leader.election.enable은 브로커/클러스터 전역 설정 (책 기준 기본 true, 이후 버전은 false)
  • 발생 시나리오: 팔로워들이 죽거나 뒤처져 리더가 유일한 ISR인 상태에서 리더까지 사라질 때
  • 허용하면 데이터 유실과 컨슈머 간 불일치, 금지하면 파티션이 오프라인으로 남는다
  • 돌아온 옛 리더는 새 리더보다 앞선 자기 메시지를 삭제한다

min.insync.replicas: 단일 복제본을 읽기 전용으로 만들기

토픽 레벨과 브로커 레벨 설정 모두 이름이 `min.insync.replicas`다. 앞에서 봤듯이 복제본 3개로 설정한 토픽에서도 in-sync 복제본이 하나만 남는 상황이 생길 수 있다. 그 복제본까지 불가용해지면 가용성과 일관성 중 하나를 골라야 한다. 문제의 근원 중 하나는 Kafka의 신뢰성 보장상 '커밋됨'이 모든 in-sync 복제본에 기록됨을 뜻하는데, 그 '모든'이 사실상 한 개일 수 있고 그 한 개가 사라지면 데이터가 없어진다는 점이다.

커밋된 데이터가 반드시 둘 이상의 복제본에 기록되도록 하고 싶다면 최소 in-sync 복제본 수를 더 높게 설정해야 한다. 복제본이 3개인 토픽에서 min.insync.replicas를 2로 두면, 3개 중 최소 2개가 in-sync일 때만 파티션에 쓸 수 있다.

세 복제본이 모두 in-sync면 정상 동작하고, 하나가 불가용해져도 마찬가지다. 그러나 셋 중 둘이 불가용해지면 브로커는 produce 요청을 더 이상 받지 않고, 데이터를 보내려는 프로듀서는 NotEnoughReplicasException을 받는다. 컨슈머는 기존 데이터를 계속 읽을 수 있다. 즉 이 설정에서 in-sync 복제본이 하나만 남으면 그 파티션은 사실상 읽기 전용이 된다. 이는 데이터를 생산하고 소비했는데 unclean 선출이 일어나면서 그 데이터가 사라지는 바람직하지 않은 상황을 막아준다. 이 읽기 전용 상태에서 벗어나려면 불가용한 두 복제본 중 하나를 다시 살리고(브로커 재시작 등) 따라잡아 in-sync가 될 때까지 기다려야 한다.

핵심 포인트

  • min.insync.replicas는 브로커/토픽 레벨 설정이며 쓰기 경로에만 적용된다
  • RF=3, min.insync.replicas=2에서 ISR이 1이 되면 프로듀서는 NotEnoughReplicasException을 받는다
  • 이때 컨슈머는 계속 읽을 수 있어 파티션이 사실상 읽기 전용이 된다
  • 복구는 불가용 복제본을 살려 따라잡게 하는 것뿐이다

이 모듈과 연관된 문항 4개가 문제 은행에 있습니다.

이 내용으로 문제 풀어보기