[Kafka] 카프카 너로 정했다! - 기본 개념

maxxyoung·2024년 3월 25일
0

정리글은 데브원영님의 아파치 카프카 애플리케이션 프로그래밍을 보고 작성되었습니다.
인프런 아파치 카프카 애플리케이션

카프카 브로커와 클러스터

브로커와 클러스터

  • 카프카 클라이언트와 데이터를 주고받기 위해 사용하는 주체
  • 데이터를 분산 저장하여 장애가 발생하더라도 안전하게 사용할 수 있도록 도와주는 애플리케이션
  • 하나의 서버에는 한 개의 카프카 브로커 프로세스가 실행
  • 데이터를 안전하게 보관하고 처리하기 위해 3대 이상의 브로커 서버를 1개의 클러스터로 묶어서 운영
  • 카프카 클러스터로 묶인 브로커들은 프로듀서가 보낸 데이터를 안전하게 분산 저장하고 복제하는 역할 수행

주키퍼

  • 카프카 클러스터를 실행하기 위해서는 주키퍼가 필요함
  • 주키퍼의 서로 다른 znode에 클러스터를 지정하면 됨
  • root znode에 각 클러스터별 znode를 생성하고 클러스터 실행 시 root가 아닌 하위 znode로 설정
  • 카프카 3.0 부터는 주키퍼가 없어도 클러스터 동작 가능

브로커의 역할

컨트롤러

  • 클러스터의 다수 브로커 중 한 대가 컨트롤러 역할을 함
  • 컨트롤러는 다른 브로커들의 상태를 체크하고 브로커가 클러스터에서 빠지는 경우 해당 브로커에 존재하는 리더 파티션을 재분배

데이터 삭제

  • 카프카는 다른 메시징 플랫폼과 다르게 컨슈머가 데이터를 가져가더라도 토픽의 데이터는 삭제되지 않음
  • 컨슈머나 프로듀서가 데이터 삭제를 요청할 수 없음
  • 오직 브로커만이 데이터를 삭제할 수 있음
  • 데이터 삭제는 파일 단위로 이루어지는데 이 단위를 '로그 세그먼트'라 부름

컨슈머 오프셋 저장

  • 컨슈머 그룹은 토픽이 특정 파티션으로부터 데이터를 가져가고 처리하고 이 파티션의 어느 레코드까지 가져갔는지 확인하기 위해 오프셋을 커밋함
  • 커밋한 오프셋은 __consumer_offsets 토픽에 저장

코디네이터

  • 코디네이터는 컨슈머 그룹의 상태를 체크하고 파티션을 컨슈머와 매칭되도록 분배하는 역할을 함
  • 컨슈머가 컨슈머 그룹에서 빠지면 매칭되지 않은 파티션을 정상 작동하는 컨슈머로 할당하여 끊임없이 데이터가 처리되도록 도와줌

데이터 저장

  • 카프카를 실행할 때 config/server.properties의 log.dir 옵션에 정의한 디렉토리에 데이터를 저장
  • 토픽 이름과 파티션 번호의 조합으로 하위 디렉토리를 생성하여 데이터를 저장

복제(Replication)

  • 데이터 복제는 카프카를 장애 허용 시스템(fault tolerant system)으로 동작하도록하는 원동력임
  • 클러스터로 묶인 브로커 중 일부에 장애가 발생해도 데이터를 유실하지 않고 안전하게 사용할 수 있음
  • 카프카의 데이터 복제 단위는 파티션 단위로 이루어짐
  • 토픽을 생성할 때 파티션의 복제 개수도 같이 설정되는데 직접 옵션을 선택하지 않으면 브로커에 설정된 옵션 값을 따라감
  • 복제 개수의 최솟값은 1(복제 없음)이고 최댓값은 브로커의 개수 만큼 설정하여 사용할 수 있음
  • 복제된 파티션은 리더와 팔로워로 구성
  • 프로듀서 또는 컨슈머와 직접 통신하는 파티션을 리더, 나머지 복제 데이터를 가지고 있는 파티션을 팔로워라고 부름
  • 팔로워 파티션들은 리더 파티션의 오프셋을 확인하여 현재 자신이 가지고 있는 오프셋과 차이가 나는 경우 리더 파티션으로 부터 데이터를 가져와서 자신의 파티션에 저장하는 과정을 복제라고함
  • 복제를 통해 데이터를 안전하게 사용할 수 있다는 강력한 장점 때문에 카프카를 운영할 때 2 이상의 복제 개수를 정하는 것이 중요

세그먼트

로그와 세그먼트

  • 로그파일 안은 세그먼트로 이루어져 있음
  • 로그파일의 이름은 시작 오프셋을 나타냄(ex.000000000010.log 시작 오프셋 10)
  • log.segment.bytes: 바이트 단위의 최대 세그먼트 크기 지정. 기본 1GB
  • log.roll.ms(hours): 세그먼트가 신규 생성된 이후 다음 파일로 넘어가는 시간 주기. 기본 값은 7일

액티브 세그먼트

  • 쓰기가 일어나고 있는 가장 마지막 세그먼트 파일을 액티브 세그먼트라고 부름
  • 액티브 세그먼트는 브로커의 삭제 대상에서 포함되지 않음
  • 액티브 세그먼트가 아닌 세그먼트는 retention 옵션에 따라 삭제 대상으로 지정됨

세그먼트와 삭제 주기(cleanup.policy=delete)

  • retention.ms(minutes, hours): 세그먼트를 보유할 최대 기간. 기본 값은 7일(금, 토, 일을 생각해 보통 3일로 잡음)
  • retention.bytes: 파티션당 로그 적재 바이트 값. 기본 값은 -1
  • log.retention.check.interval.ms: 세그먼트가 삭제 영역에 들어왔는지 확인하는 간격. 기본 값은 5분.

세그먼트 삭제(cleanup.policy=delete)

  • 카프카에서 데이터는 세그먼트 단위로 삭제되기 때문에 로그 단위로 개별 삭제는 불가
  • 로그의 메시지 키, 메시지 값, 오프셋, 헤더 등 이미 적재된 데이터에 대해서 수정이 불가능하기 때문에 데이터를 적재할 때 또는 사용할 때 데이터를 검증하는 것이 좋음

cleanup.policy=compact

  • 토픽 압축에서 압축이란 메시지 키 별로 해당 메시지 키의 레코드 중 오래된 데이터를 삭제하는 정책을 뜻함
  • 삭제 정책과 다르게 일부 레코드만 삭제가 될 수 있음
  • 압축은 액티브 세그먼트를 제외한 데이터가 대상임

테일/헤드 영역, 클린/더티 로그

  • 테일 영역: 압축 정책에 의해 압축이 완료된 레코드들. 클린로그 라고도 부름. 중복 메시지 키가 없음
  • 헤드 영역: 압축 정책이 되기 전 레코드들. 더티 로그 라고도 부름. 중복되는 메시지 키가 있음

min.cleanable.dirty.ratio

  • 데이터 압축 시작 시점은 min.cleanable.dirty.ratio 옵션값을 따름
  • 액티브 세그먼트를 제외한 세그먼트에 남아있는 테일 영역의 레코드 개수와 헤드 영역의 레코드 개수의 비율을 뜻함
  • 테일 영역 레코드 + 헤드 영역 레코드 / 헤드 영역 레코드 (ex. 0.5 일경우 테일 영역이 5개, 헤드 영역이 5개면 압출 실행)

브로커에 장애가 발생한 경우

  • 브로커가 다운되면 해당 브로커에 있는 리터 파티션을 사용할 수 없기 때문에 팔로워 파티션 중 하나가 리더 파티션 지위를 넘겨 받음
  • 운영 시, 데이터 종류마다 다른 복제 개수를 설정하고 상황에 따라서는 토픽마다 복제 개수를 다르게 설정하여 운영하기도 함
  • 데이터가 일부 유실 되어도 무관하고 데이터 처리 속도가 중요하다면 1 또는 2로 설정하고 금융 정보와 같이 유실이 일어나면 안되는 데이터의 경우 복제 개수를 3으로 설정하기도 함

ISR(In-Sync-Replicas)

  • ISR은 리더 파티션과 팔로워 파티션이 모두 싱크된 상태를 뜻함

unclean.leader.election.enable

  • 리더 파티션의 데이터를 모두 복제하지 못한 상태고, 이렇게 싱크가 되지 않은 팔로워 파티션이 리더 파티션으로 선출되면 데이터가 유실 될 수 있음. 이때의 설정 값
  • unclean.leader.election.enable=true: 유실을 감수함. 복제가 안된 팔로워 파티션을 리더로 승금
  • unclean.leader.election.enable=flase: 유실을 감수하지 않음. 해당 브로커가 복구될 때 까지 중단

토픽, 파티션, 레코드

토픽과 파티션

  • 토픽은 카프카에서 데이터를 구분하기 위해 사용하는 단위
  • 토픽은 1개 이상의 파티션을 소유하고 있음
  • 파티션에서는 프로듀서가 보낸 데이터들이 들어가 저장되는데 이 데이터를 레코드라고 부름
  • 파티션은 자료구조에서 접하는 큐와 비슷한 구조라고 생각하면 쉬움. FIFO구조 같이 먼저 들어간 레코드는 컨슈머가 먼저 가져가게 됨
  • 파티션의 레코드는 컨슈머가 가져가는 것과 별개로 관리되기 때문에 다양한 목적을 가진 여러 컨슈머 그룹들이 토픽의 데이터를 여러 번 가져갈 수 있음

토픽 생성 시 파티션이 배치되는 방법

  • 파티션이 5개인 토픽을 생성했을 경우 0번 브로커부터 시작하여 라운드로빈 방식으로 리더 파티션들이 생성됨
  • 카프카는 리더 파티션이 있는 브로커와 통신하여 데이터를 주고 받으므로 여러 브로커에 골고루 네트워크 통신을 하게 됨. 이를 통해 데이터가 특정 서버 통신되는 hot spot 현상을 막고 선형 확장인 linear scale out하여 데이터가 많아지더라도 자연스럽게 대응 가능

파티션 개수와 컨슈머 개수의 처리량

  • 파티션은 카프카 병렬처리의 핵심으로써 그룹으로 묶인 컨슈머들이 레코드를 병렬로 처리할 수 있도록 매칭됨
  • 컨슈머의 처리량이 한정된 상황에서 많은 레코드를 병렬로 처리하는 가장 좋은 방법은 컨슈머를 스케일 아웃하는 것
  • 컨슈머의 개수를 늘림과 동시에 파티션의 개수도 늘리면 처리량이 증가하는 효과를 볼 수 있음

파티션의 개수를 줄이는 것은 불가능

  • 카프카에서 파티션의 개수를 줄이는 것은 지원하지 않음
  • 한 번 늘리면 줄이는 것은 불가능하기 때문에 토픽을 삭제하고 재생성하는 방법 외에는 없음. 따라서 늘리는 거에 신중할 것

레코드 상세히 살펴보기

레코드

  • 레코드는 타임스탬프, 헤더, 메시지, 키, 메시지 값, 오프셋으로 구성
  • 프로듀서가 생성한 레코드가 브로커로 전송되면 오프셋과 타임스탬프가 지정되어 저장
  • 브로커에 한 번 적재된 레코드는 수정할 수 없고 로그 리텐션 기간 또는 용량에 따라서만 삭제됨

타임스템프

  • 타임스탬프는 스트림 프로세싱에서 활용하기 위한 시간을 저장하는 용도로 사용
  • 카프카 0.10.0.0 이후 버전부터 추가된 타임스탬프는 Unix timestamp가 포함되며 프로듀서에서 따로 설정하지 않으면 기본값으로 ProducerRecord 생성 시간이 들어감. 또는 브로커 적재 시간으로 설정할 수도 있음. 해당 옵션은 토픽 단위로 설정 가능하며 message.timestamp.type을 사용

오프셋

  • 프로듀서가 전송한 레코드가 브로커에 적재될 때 오프셋에 저장됨
  • 오프셋은 0부터 시작되고 1씩 증가
  • 컨슈머는 오프셋을 기반으로 처리가 완료된 데이터와 앞으로 처리해야할 데이터를 구분
  • 각 메시지는 파티션별로 고유한 오프셋을 가지므로 컨슈머에서 중복 처리를 방지하기 위한 목적으로도 사용

헤더

  • 0.11부터 제공한 기능
  • key/value 데이터를 추가할 수 있으며 레코드의 스키마 버전이나 포맷과 같이 데이터 프로세싱에 참고할만한 정보를 담아서 사용할 수 있음

메시지 키

  • 처리하고자 하는 메시지 값의 분류하기 위한 용도로 사용되며 파티셔닝이라고 부름
  • 파티셔닝에 사용하는 메시지 키는 파티셔너에 따라 토픽의 파티션 번호가 생성됨
  • 메시지 키가 null인 레코드는 특정 토픽의 파티션에 라운드로빈으로 전달됨
  • null이아닌 메시지 키는 해쉬값에 의해서 특정 파티션에 매핑되어 전달 됨

메시지 값

  • 레코드의 메시지 값은 실질적으로 처리할 데이터가 담기는 공간
  • 메시지 값의 포맷은 제네릭으로 사용자에 의해 지정됨
  • Float, Byte[], String 등 다양한 형태로 지정 가능하며 필요에 따라 사용자 지정 포맷으로 직렬화/역직렬화 클래스를 만들어 사용할 수도 있음
  • 브로커는 저장된 레코드의 메시지 값은 어떤 포맷으로 직렬화되어 저장되었는지 알 수 없기 때문에 컨슈머는 미리 역질렬화 포맷을 알고 있어야함

카프카 브로커와 클라이언트가 통신하는 방법

클라이언트 메타데이터

  • 카프카 클라이언트는 통신하고자 하는 리더 파티션의 위치를 알기 위해 데이터를 주고 받기 전에 메타데이터를 브로커로부터 전달 받음
  • 메타 데이터는
    metadata.max.age.ms: 메타데이터를 강제로 리프레시하는 간격. 기본값 5분
    metadata.max.idle.ms: 프로듀서가 유휴상태일 경우 메타데이터를 캐시에 유지하는 기간. 기본값 5분
    다음과 같은 옵션을 통해 리프레시 됨
  • 카프카 클라이언트는 반드시 리더 파티션과 통신해야함. 만약 메타데이터가 현재의 파티션 상태에 맞게 리프레시되지 않은 상태에서 잘못된 브로커로 데이터를 요청하면 LEADER_NOT_AVAILABLE 익셉션이 발생
profile
오직 나만을 위한 글. 틀린 부분 말씀해 주시면 감사드립니다.

0개의 댓글