Kafka 복제 구성: 데이터 내구성 및 가용성 보장
내구성을 약화시키지 않고 Kafka 복제, ISR, 프로듀서 확인 및 랙 인식 구성을 설정합니다.
Kafka 복제 구성: 데이터 내구성 및 가용성 보장
Kafka 복제 구성은 클러스터가 단순한 브로커의 집합이 아니라 장애 상황에서 신뢰할 수 있는 시스템으로 동작하게 만드는 핵심입니다. 설정 자체는 복잡하지 않습니다: 복제 팩터, 동기화된 복제본, 프로듀서 확인, 리더 선출, 랙 배치입니다. 까다로운 점은 이 설정들이 함께 의미를 가진다는 것입니다.
세 개의 복제본을 가진 토픽이라도 프로듀서가 약한 확인을 사용하면 승인된 데이터가 손실될 수 있습니다. acks=all을 사용하는 프로듀서도 현재 활성화된 브로커 수에 비해 min.insync.replicas가 너무 엄격하면 쓰기가 실패할 수 있습니다. 가용 영역에 분산된 클러스터라도 핫 파티션의 모든 복제본이 동일한 장애 도메인에 위치하면 문제가 발생할 수 있습니다. 복제는 단순한 체크박스가 아닙니다.
제가 Kafka 복제를 생각하는 방식은 간단합니다: 각 파티션에 대해 Kafka는 여러 복사본을 유지하고, 하나의 복사본을 선택하여 읽기와 쓰기를 처리하며, 다른 복사본은 그 중 하나가 인수할 수 있을 정도로 가깝게 유지합니다. 여러분의 역할은 충분한 복사본 수, 쓰기가 성공한 것으로 간주되기 전에 동기화되어야 하는 복사본 수, 그리고 클러스터가 데이터 안전보다 가용성을 선호해야 하는지 여부를 결정하는 것입니다.
Kafka 토픽은 파티션으로 나뉩니다. 각 파티션은 하나의 리더 복제본과 0개 이상의 팔로워 복제본을 가집니다. 프로듀서는 리더에 씁니다. 컨슈머는 일반적으로 리더에서 읽습니다. 팔로워는 리더로부터 레코드를 가져와 로컬 로그를 정렬합니다. 리더의 브로커가 실패하면 Kafka는 안전한 후보로 간주되는 복제본 중에서 새 리더를 선출합니다.
이 안전한 후보 목록이 ISR(In-Sync Replicas)입니다. 복제본이 Kafka의 복제 지연 규칙에 따라 리더와 충분히 가깝게 유지되면 ISR에 포함됩니다. 팔로워가 가져오기를 중단하거나 너무 오래 뒤처지거나 브로커가 사라지면 Kafka는 ISR에서 제거합니다. 따라잡으면 다시 합류할 수 있습니다.
이 세부 사항이 중요한 이유는 ISR이 Kafka의 내구성을 단순한 희망 사항 이상으로 만들기 때문입니다. acks=all을 사용하면 리더는 레코드가 필요한 동기화된 복제본에 복제될 때까지 프로듀스 요청을 승인하지 않습니다. 정확한 요구 사항은 min.insync.replicas에 의해 제어됩니다. 토픽이 replication.factor=3이고 min.insync.replicas=2인 경우 Kafka는 acks=all 쓰기가 성공하려면 최소 두 개의 동기화된 복제본이 필요합니다.
이 조합은 실용적인 균형을 제공하기 때문에 프로덕션에서 일반적입니다. 하나의 브로커가 실패해도 토픽은 강력하게 승인된 쓰기를 계속 받을 수 있습니다. 첫 번째 브로커가 복구되기 전에 두 번째 브로커가 실패하면 acks=all을 사용하는 프로듀서는 NotEnoughReplicas 또는 NotEnoughReplicasAfterAppend와 같은 오류를 보기 시작해야 합니다. 이는 장애 상황에서 짜증나지만 일반적으로 올바른 동작입니다. Kafka는 충분한 안전한 복사본이 없을 때 쓰기가 내구성이 있다고 가장하는 것을 거부합니다.
다음은 일반적인 3개 이상의 브로커 클러스터에 대한 일반적인 프로덕션 기준입니다:
default.replication.factor=3
min.insync.replicas=2
unclean.leader.election.enable=false
이 값들이 모든 워크로드를 자동으로 안전하게 만들지는 않지만, 합리적인 시작점을 제공합니다. default.replication.factor=3은 토픽 생성 명령이 달리 지정하지 않는 한 새 토픽이 세 개의 복사본을 얻는다는 의미입니다. min.insync.replicas=2는 강력한 쓰기를 위해 최소 두 개의 복제본이 동기화되어야 함을 의미합니다. unclean.leader.election.enable=false는 Kafka가 파티션을 쓰기 가능하게 유지하기 위해 오래된 복제본을 리더로 선출하지 않도록 지시합니다.
복제 팩터를 브로커 수보다 높게 설정하지 마십시오. 브로커가 두 개만 있는데 Kafka는 세 개의 복제본을 세 개의 다른 브로커에 배치할 수 없습니다. 소규모 개발 클러스터에서는 replication.factor=1이 괜찮습니다. 편의성이 장애 허용보다 중요하기 때문입니다. 프로덕션에서 1은 단일 브로커 손실로 데이터를 사용할 수 없게 만들고 해당 브로커에만 저장된 레코드를 영구적으로 잃을 수 있습니다.
프로듀서 측은 토픽 측과 일치해야 합니다. 중요한 데이터의 경우 acks=all을 사용하십시오. 특별한 이유가 없는 한 idempotence도 활성화하십시오. 최신 Kafka 클라이언트에서 멱등성 프로듀서는 재시도로 인한 중복을 줄이기 위한 일반적인 선택입니다.
acks=all
enable.idempotence=true
retries=2147483647
max.in.flight.requests.per.connection=5
클라이언트 버전과 전달 요구 사항을 이해하지 않고 재시도 값을 모든 클라이언트에 맹목적으로 복사하지 마십시오. 중요한 아이디어는 내구성 있는 Kafka 프로덕션에는 일반적으로 재시도, 멱등성 및 acks=all이 함께 필요하다는 것입니다. acks=1을 설정하면 리더가 팔로워가 복사하기 전에 레코드를 승인할 수 있습니다. 리더가 잘못된 시점에 죽으면 승인된 레코드가 사라질 수 있습니다. 이는 일부 원격 측정 스트림에서는 허용되지만 결제, 감사 추적, 재고 이동 또는 다운스트림 팀이 진실 공급원으로 취급하는 모든 것에는 허용되지 않습니다.
토픽을 생성할 때 브로커 기본값에 의존하지 않고 복제 선택을 의도적으로 설정하십시오:
kafka-topics.sh --create --bootstrap-server broker1:9092 --topic orders.v1 --partitions 12 --replication-factor 3 --config min.insync.replicas=2
파티션 수는 복제와 별개입니다. 복제 팩터 3의 12개 파티션은 총 36개의 파티션 복제본을 의미합니다. 이는 스토리지, 네트워크, 파일 핸들러 및 컨트롤러 메타데이터 비용이 발생합니다. 복제는 내구성을 향상시키지만 무료는 아닙니다.
기존 토픽의 경우 min.insync.replicas 변경은 간단합니다:
kafka-configs.sh --alter --bootstrap-server broker1:9092 --entity-type topics --entity-name orders.v1 --add-config min.insync.replicas=2
기존 토픽의 복제 팩터 변경은 Kafka 버전과 도구에 따라 다릅니다. 최신 Kafka 릴리스는 kafka-reassign-partitions.sh를 지원하며, 경우에 따라 증가를 더 쉽게 만드는 토픽 변경 워크플로를 지원합니다. 이전 클러스터에서 복제 증가는 일반적으로 파티션 재할당 계획을 생성하고 실행하는 것을 의미합니다. 복제 감소는 복사본을 제거하기 때문에 더 민감합니다. 이를 계획된 작업으로 취급하고 시끄러운 장애 상황에서 입력하는 임의 명령이 아닌 것으로 간주하십시오.
토픽이 크거나 클러스터가 이미 바쁜 경우 재할당을 제한해야 합니다. 복제 따라잡기는 기존 복제본에서 이전 데이터를 읽고 새 복제본에 씁니다. 이는 라이브 프로듀서와 컨슈머의 디스크 및 네트워크 용량을 빼앗을 수 있습니다. 안전한 런북에는 일반적으로 유지 관리 기간, 이전 및 이후 --describe 출력, 재할당 제한 및 롤백 계획이 포함됩니다.
다음과 같이 토픽을 검사할 수 있습니다:
kafka-topics.sh --describe --bootstrap-server broker1:9092 --topic orders.v1
출력에서 Leader, Replicas 및 Isr의 세 가지 필드를 확인하십시오. Replicas는 할당된 세트입니다. Isr은 현재 따라잡고 있는 세트입니다. Replicas가 1,2,3이지만 Isr이 1,2인 경우 브로커 3이 해당 파티션에 대해 뒤처져 있거나 사용할 수 없습니다. 많은 파티션이 ISR에서 누락된 브로커를 표시하면 해당 브로커의 디스크, 네트워크, 프로세스 상태 및 로그를 확인하십시오. 소수의 핫 파티션만 영향을 받는 경우 리더가 과부하되었거나 파티션에 비정상적으로 높은 트래픽이 있을 수 있습니다.
비정상 리더 선출은 특별한 주의가 필요합니다. 파티션의 모든 동기화된 복제본이 사라지면 Kafka에는 두 가지 선택이 있습니다. 안전한 복제본이 반환될 때까지 파티션을 사용할 수 없게 두거나, 동기화되지 않은 복제본을 선출하여 이전 리더에서 승인된 레코드를 잃을 위험을 감수하는 것입니다. unclean.leader.election.enable=false는 안전을 선택합니다. true는 데이터 손실 위험을 감수하고 가용성을 선택합니다.
비정상 선출이 방어 가능한 워크로드가 있습니다: 수명이 짧은 클릭스트림 데이터, 일회용 메트릭 또는 업스트림 시스템이 모든 것을 재생할 수 있는 파이프라인입니다. 대부분의 비즈니스 데이터의 경우 비활성화된 상태로 두십시오. 파티션의 가용성을 잃는 것은 고통스럽지만, 침묵하는 데이터 손실은 더 나쁩니다. 컨슈머가 아무 일도 없었던 것처럼 계속할 수 있기 때문입니다.
랙 인식 복제는 다른 종류의 장애에 도움이 됩니다. 브로커가 랙, 영역 또는 공유 전원/네트워크 경로가 있는 호스트에 분산된 경우 Kafka에 각 브로커의 위치를 알려주십시오:
broker.rack=zone-a
모든 브로커에 올바른 값을 설정하십시오. Kafka는 복제본을 랙 전체에 분산시켜 단일 영역 장애가 파티션의 모든 복사본을 제거할 가능성을 줄입니다. 이것은 마법이 아닙니다. 각 영역에 충분한 브로커, 충분한 디스크 및 신중한 파티션 배치가 여전히 필요합니다. 그러나 broker.rack이 없으면 Kafka는 두 브로커가 동일한 장애 도메인을 공유한다는 것을 알 방법이 없습니다.
복제를 지속적으로 모니터링하십시오. 가장 유용한 조기 경고 신호는 복제 부족 파티션, 오프라인 파티션, ISR 축소 이벤트 및 복제본 부족과 관련된 프로듀스 오류입니다. Prometheus 기반 설정에서 팀은 일반적으로 Kafka 브로커 메트릭에서 복제 부족 파티션과 오프라인 파티션을 모니터링한 다음 이러한 알림을 브로커 디스크, 네트워크 및 JVM 메트릭과 쌍으로 연결합니다.
좋은 장애 질문은: ISR이 브로커 사망, 복제가 따라잡을 수 없음 또는 네트워크 불안정으로 인해 축소되었습니까? 수정 방법이 다릅니다. 죽은 브로커는 서비스 복구가 필요합니다. 느린 브로커는 디스크 교체, I/O 조사 또는 더 적은 파티션 리더가 필요할 수 있습니다. 네트워크 문제는 CPU와 디스크가 정상으로 보일 때에도 반복적인 연결 끊김과 가져오기 지연으로 나타날 수 있습니다.
롤링 브로커 재시작은 복제 설정의 가치를 보여주는 또 다른 경우입니다. 한 번에 하나의 브로커를 재시작하십시오. 다음 브로커를 재시작하기 전에 파티션이 건강한 ISR을 되찾을 때까지 기다리십시오. min.insync.replicas=2로 브로커를 너무 빨리 재시작하면 동기화된 복제본이 너무 적어 프로듀서가 실패하기 시작할 수 있습니다. 이 실패는 예상되지만 인내심과 모니터링으로 피할 수 있습니다.
실용적인 체크리스트는 짧습니다. 대부분의 프로덕션 토픽에 복제 팩터 3을 사용하십시오. 중요한 데이터에는 프로듀서 acks=all과 함께 min.insync.replicas=2를 사용하십시오. 데이터가 명시적으로 일회용이 아닌 한 비정상 리더 선출을 비활성화된 상태로 유지하십시오. 랙 인식으로 장애 도메인 전체에 복제본을 분산시키십시오. 브로커 가동 시간뿐만 아니라 ISR 상태를 모니터링하십시오. 그리고 실제 장애가 발생하기 전에 통제된 기간 동안 브로커를 재시작하여 가정을 테스트하십시오.
리뷰 중에 도움이 되는 한 가지 세부 사항은 내구성과 가용성을 평이한 언어로 분리하는 것입니다. 내구성은 "Kafka가 쓰기가 성공했다고 말한 후, 승인된 레코드가 위험에 처하기 전에 몇 번의 장애가 발생할 수 있습니까?"라고 묻습니다. 가용성은 "프로듀서와 컨슈머가 지금 파티션을 계속 사용할 수 있습니까?"라고 묻습니다. 강력한 설정은 때때로 가용성을 감소시킵니다. Kafka는 약하게 복제된 데이터를 받아들이기보다 쓰기를 거부하기 때문입니다. 이는 Kafka의 실패가 아닙니다. 이는 Kafka가 구성한 계약을 존중하는 것입니다.
예를 들어, 복제 팩터 3, min.insync.replicas=2, 프로듀서가 acks=all을 사용하는 토픽을 상상해 보십시오. 브로커 1이 리더이고 브로커 2와 3이 팔로워입니다. 브로커 3이 다운되면 ISR은 1,2가 됩니다. 두 개의 복제본이 동기화되어 있으므로 쓰기는 계속 성공합니다. 브로커 3이 반환되기 전에 브로커 2가 다운되면 ISR은 1만 됩니다. 쓰기가 실패합니다. 일부 팀은 프로덕션에서 이를 처음 보고 리더가 아직 살아 있는데 Kafka가 왜 다운되었는지 묻습니다. 답은 토픽이 일부 읽기에 대해 여전히 사용 가능하지만 강력하게 승인된 쓰기에 대해서는 안전하지 않다는 것입니다.
또한 컨슈머 복구에 대해 생각해야 합니다. 복제는 브로커 측 레코드 복사본을 보호합니다. 모든 워크플로 실수로부터 컨슈머 오프셋을 자동으로 보호하지는 않습니다. 컨슈머 오프셋은 일반적으로 __consumer_offsets에 Kafka에 저장되므로 해당 내부 토픽도 건강한 복제가 필요합니다. 사용자 토픽이 신중하게 구성되었지만 내부 토픽이 초기 클러스터 구축에서 약한 복제로 생성된 경우 장애 조치 동작이 예상보다 나쁠 수 있습니다. 프로덕션 준비 검토의 일부로 내부 토픽 복제를 확인하십시오.
멀티 테넌트 클러스터에서 모든 토픽이 동일한 구성을 받을 자격이 있는 것은 아닙니다. 볼륨이 높고 비즈니스 가치가 낮은 일회용 메트릭 토픽은 더 짧은 보존을 사용하고 약한 보장을 허용할 수 있습니다. 청구 토픽은 그래서는 안 됩니다. 실수는 우연한 기본값이 그 차이를 결정하도록 두는 것입니다. 토픽 클래스를 문서화하십시오: 중요 이벤트 스트림, 재생 가능한 원격 측정, 압축된 상태 토픽, 임시 개발 토픽. 그런 다음 각 클래스를 복제, ISR, 보존 및 프로듀서 설정에 매핑하십시오.
장애 상황에서 모든 사람이 트레이드오프를 이해하지 않는 한 오류를 잠재우기 위해 내구성 설정을 변경하지 마십시오. min.insync.replicas를 2에서 1로 낮추면 프로듀서가 움직일 수 있지만 승인된 쓰기가 하나의 브로커에만 존재할 수 있음을 의미합니다. 비정상 리더 선출을 활성화하면 파티션 가용성을 복원할 수 있지만 오래된 복제본은 레코드를 잃을 수 있습니다. 때로는 비즈니스가 그 트레이드오프를 선택할 수 있습니다. 이는 숨겨진 운영자 지름길이 아닌 의식적인 장애 결정이어야 합니다.