Kafka/Kafka
카프카 리플레이션 동작 방식
seungh0
2023. 4. 1. 00:10
반응형
카프카 리플리케이션
일시적인 하드웨어 이슈 등으로 브로커 한두 대에서 장애가 발생하더라도 안정성을 확보하기 위해 카프카 내부에서는 리플리케이션이라는 동작을 하게 된다.
리더와 팔로워
- 카프카는 내부적으로 모두 동일한 리플리케이션들을 리더와 팔로워로 구분하고, 리플리케이션 중 하나가 리더로 선정되며, 모든 읽기와 쓰기는 그 리더를 통해서만 가능.
- 프로듀서에서는 모든 리플리케이션에 메시지를 보내는 것이 아니라, 리더에게만 메시지를 전송한다.
- 또한 컨슈머도 오직 리더로부터 메시지를 가져온다
- 팔로워는 컨슈머와 비슷하게 지속적으로 파티션의 리더가 새로운 메시지를 받았는지 확인하고, 새로운 메시지가 있다면 해당 메시지를 리더로부터 복제한다.
복제 유지와 커밋
- 리더와 팔로워는 ISR(InsyncReplica)라는 논리적인 그룹으로 묶여 있다.
- 이렇게 리더와 팔로워를 별도의 그룹으로 나누는 이유는 기본적으로 ISR 그룹 안에 속한 팔로워들만이 새로운 리더의 자격을 가질 수 있기 때문.
- ISR 내의 팔로워들은 리더와의 데이터 일치를 유지하기 위해 지속적으로 리더의 데이터를 따라가게 되고, 리더는 ISR 내 모든 팔로워가 메시지를 받을 때까지 기다립니다.
- 파티션의 리더는 팔로워들이 뒤쳐지지 않고 리플리케이션 동작을 잘하고 있는지 감시한다.
- 만약 팔로워가 특정 주기의 시간만큼 복제 요청을 하지 않는 다면, 리더는 해당 팔로워가 리플리케이션 동작에 문제가 발생했다고 판단해 ISR 그룹에서 추방한다.
- ISR 내에서 모든 팔로워의 복제가 완료되면, 리더는 내부적으로 커밋되었다는 표시를 하게 되며, 마지막 커밋 오프셋 위치는 하이워터마크라고 한다.
- 이렇게 커밋된 메시지만 컨슈머가 읽어갈 수 있다. (메시지의 일관성 유지를 위해)
- 커밋된 위치는 브로커의 로컬 디스크의 replication-offset-checkpoint라는 파일에 마지막 커밋 오프셋 위치를 저장한다.
post_v1 0 23 post_v1 1 27 // 토픽 + 파티션 번호 + 커밋된 오프셋 번호
리플리케이션 동작 방식
- 프로듀서가 메시지를 전송해서 리더에게 메시지가 저장됨. (0번 오프셋이라고 가정하고, 아직 팔로워들에게 리플레케이션 되기 전)
- 팔로워들은 리더에게 0번 오프셋 메시지를 fetch 요청 후 새로운 메시지가 있다는 사실을 인지하고, message 1을 리플레케이션 한다.
- 현 상태에서 리더는 모든 팔로워가 0번 오프셋 메시지를 리플리케이션 하기 위한 요청을 보냈다는 사실을 알고 있지만, 0번 오프셋에 대한 리플리케이션 동작이 성공했는지 여부는 알지 못함. (카프카에는 리플레이션 성능을 위해 리더와 팔로워 사이에서 ACK를 주고받는 통신이 없음)
- 리더는 1번 오프셋의 위치에 두 번째 새로운 메시지를 프로듀서로부터 받은 후 저장한다.
- 0번 오프셋에 대한 리플리케이션 동작을 마친 팔로워들은 리더에게 1번 오프셋에 대한 리플리케이션을 요청.
- 팔로워들로부터 1번 오프셋에 대한 리플리케이션 요청을 받은 리더는 팔로워들의 0번 오프셋에 대한 리플리케이션 동작이 성공했음을 인지하고, 오프셋 0번에 대해 커밋 표시를 한 후 하이워터마크를 증가시킴.
- 즉 리더는 팔로워들이 보내는 리플리케이션의 요청의 오프셋을 보고, 팔로워들이 어느 위치 오프셋까지 리플리케이션을 성공했는지를 인지할 수 있음.
카프카는 리플리케이션 과정에서 ACK 통신 단계를 제거해서 성능을 개선하고, 또한 리더와 팔로워들의 리플리케이션 동작 방식이 리더가 푸시하는 방식이 아닌, 팔로워들이 풀 하는 방식으로 동작하여 리더의 부하를 줄여주도록 구성됨.
리더에포크(LeaderEpoch)와 복구
- 리더에포크는 카프카의 파티션들이 복구 동작을 할 때 메시지의 일관성을 유지하기 위한 용도로 이용됨.
- 리더에포크 정보는 리플리케이션 프로토콜에 의해 전파되고, 새로운 리더가 변경된 후 변경된 리더에 대한 정보는 팔로워에게 전달된다.
- 리더에포크는 복구 동작시 하이워터마크를 대체하는 수단으로도 활용된다.
- 관련 내용은 책 참고
컨트롤러
- 카프카에서 컨트롤러는 리더의 선출을 맡고 있음.
- 카프카 클러스터 중 하나의 브로커가 컨트롤러 역할을 하게 되며, 파티션 ISR 리스트 중에서 리더를 선출한다.
- 리더 선출을 위한 ISR 리스트 정보는 가용성 보장을 위해 주키퍼에 저장되어 있음.
- 컨트롤러는 브로커가 실패하는 것을 예의주시하고 있으며, 만약 브로커의 실패가 감지되면 즉시 ISR 리스트 중 하나를 새로운 파티션 리더로 선출한다.
- 그리고 나서 새로운 리더의 정보를 주키퍼에 기록하고, 변경된 정보를 모든 브로커에 전달한다.
- 파티션의 리더가 다운됐다는 것은 해당 파티션의 리더가 없는 상태를 의미하며, 카프카 클라이언트인 프로듀서나 컨슈머가 해당 파티션으로부터 읽기나 쓰기가 불가능 → 모든 읽기/쓰기 동작을 실패하게 되고, 클라이언트에 설정되어 있는 재시도 숫자만큼 재시도 수행
- 따라서 클라이언트들이 재시도하는 시간 내에 리더 선출 작업이 빠르게 이뤄져야 한다.
리더 선출 과정 (예기치 않은 장애로 인한)
- 파티션 0번의 리더가 있는 브로커 1번이 예기치 않게 다운된다.
- 주키퍼는 1번 브로커와 연결이 끊어진 후, 0번 파티션의 ISR에서 변화가 생겼음을 감지한다.
- 컨트롤러는 주키퍼의 위치를 통해 0번 파티션에 변화가 생긴 것을 감지하고, 해당 파티션 ISR 중 3번을 새로운 리더로 선출한다.
- 컨트롤러는 0번 파티션의 새로운 리더가 3번이라는 정보를 주키퍼에 기록한다.
- 이렇게 갱신된 정보는 현재 활성화 상태인 모든 브로커에게 전파된다.
리더 선출 과정(Graceful Shutdown)
- 관리자가 브로커 종료 명령을 실행하고, SIG_TERM 신호가 브로커에게 전달된다.
- SIG_TERM 신호를 받은 브로커는 컨트롤러에게 알린다.
- 컨트롤러는 리더 선출 작업을 진행하고, 해당 정보를 주키퍼에 기록한다.
- 컨트롤러는 새로운 리더 정보를 다른 브로커들에게 전송한다.
- 컨트롤러는 종료 요청을 보낸 다른 브로커들에게 정상 종료한다는 응답을 보냄.
- 응답을 보낸 브로커는 캐시에 있는 내용을 디스크에 저장하고 종료함.
제어된 종료를 사용하면, 카프카 내부적으로 파티션들의 다운타임을 최소화할 수 있다. (브로커가 종료되기 전, 컨트롤러는 해당 브로커가 리더로 할당 된 전체 파티션에 대해 리더 선출 작업을 진행하기 때문)
- 물론 제어된 종료라 할지라도 리더 선출 작업 시간동안 일시적으로 다운타임이 발생.
- 브로커 장애로 인한 리더 선출 작업 시, 컨트롤러는 순차적으로 하나의 파티션마다 리더를 선출하게 되며, 첫 번째 대상 파티션의 다운타임은 길지 않지만 마지막 대상 파티션의 다운타임은 꽤 오랜 시간 걸립니다.
- 뿐만 아니라 제어된 종료의 경우 브로커는 자신의 모든 로그를 디스크에 동기화한 후 종료되므로, 이후 다시 브로커가 재시작할 때 로그 복구 시간이 짧습니다.
제어된 종료를 사용하려면 브로커의 설정 파일인 server.properties에 적용되어야 한다.
controllered.shutdown.enable=true
반응형