Post

Kafka 01

Kafka 01

실전 카프카 개발부터 운영을 읽고 제 나름대로 정리한 글 입니다.

등장 배경

회사 규모가 커지고 사업이 다각화되면서 데이터에 대한 요구사항이 증가함.

-> 데이터의 변화가 스트림으로 컨슈머 측에 전달되는 Event Driven System 으로서의 전환이 필요.

데이터를 소비하는 컨슈머들이 자신의 요구사항에 따라 데이터를 처리하거나 구독할 수 있게 됨.

Event Driven Sytsem에서 가장 중요한 것

  • 여러 네트워크를 이용하는 환경에서 모든 데이터 변경에 대한 올바른 전달의 보장
  • 동일한 데이터를 동시에 수정하면서 순서를 보장
  • 다양한 클라이언트들의 요구사항을 효율적으로 지원
  • 빠른 전송 또는 대량 전송을 위한 클라이언트를 지원

동기 방식의 Event Driven System에서는 한계가 있다. 비동기 방식의 스트리밍 플랫폼인 Kafka 등장


Kafka 특징

  1. 높은 처리량과 낮은 지연시간 image Benchmarking Apache Pulsar, Kafka, and RabbitMQ

    처리량이 가장 높은 것은 카프카. 응답 속도가 가장 빠른 것은 래빗MQ.

  2. 높은 확장성

    카프카는 버전을 업그레이드 할 때 롤링 업그레이드 가능. 브로커의 수를 자유롭게 조절가능.

  3. 고가용성

    클러스터 내 리플리케이션 기능을 추가하여 고가용성 확보.

  4. 내구성

    프로듀서의 acks 옵션을 조저아여 메시지의 내구성 강화 가능. 컨슈머가 메시지를 가져가더라도, 메시지는 삭제되지 않고 지정된 설정시간 또는 로그의 크기만큼 로컬 디스크에 보관됨. 그러므로 코드의 버그나 장애가 발생하더라도 과거의 메시지들을 불러와 재처리 할 수 있음.

  5. 개발 편의성

    프로듀서와 컨슈머가 완벽히 분리되어 동작하고 서로 영향을 주지도 받지도 않음.

  6. 운영 및 관리 편의성

    카프카는 중앙 메인 데이터 파이프라인이 형성되는데, 중앙 모니터링을 위해 그라파나 대시보드 구성 가능.


Kafka의 기초

image

  • 주키퍼(Zookeeper) : 아파치 프로젝트 애플리케이션 이름으로 카프카의 메타데이터(metadata) 관리 및 브로커의 정상 상태 점검(health check)을 담당
  • 카프카(Kafka) 또는 카프카 클러스터(Kafka cluster) : 아파치 프로젝트 애플리케이션 이름으로 여러 대의 브로커를 구성한 클러스터를 의미
  • 브로커(Broker): 카프카 애플리케이션이 설치된 서버 또는 노드를 칭함
  • 프로듀서(Producer) : 카프카로 메시지를 보내는 역할을 하는 클라이언트를 총칭
  • 컨슈머(Consumer) : 카프카에서 메시지를 꺼내가는 역할을 하는 클라이언트 총칭
  • 토픽(Topic) : 카프카는 메시지 피드들을 토픽으로 구분하고, 각 토픽의 이름은 카프카 내에서 고유 함.
  • 파티션(Partition) : 병렬 처리 및 고성능을 얻기 위해 하나의 토픽을 여러 개로 나눈 것을 말함.
  • 세그먼트(Segment) : 프로듀서가 전송한 실제 메시지가 브로커의 로컬 디스크에 저장되는 파일을 말함.
  • 메시지(Message) 또는 레코드(Record) : 프로듀서가 브로커로 전송하거나 컨슈머가 읽어가는 데이터 조각을 말함.

리플리케이션

image

각 메시지들을 여러 개로 복제해서 카프카 클러스터 내 브로커들에게 분산시키는 동작을 의미. 리플리케이션 동작 덕분에 하나의 브로커가 종료되더라도 안정성을 유지 할 수 있음.

환경팩터 수
테스트, 개발 환경1
운영 환경(로그성 메시지로서 약간의 유실 허용)2
운영 환경(유실 허용하지 않음)3
리플리케이션 팩터 수가 많아지면 항상 좋은가?

No. 리플리케이션 수가 커지면 안정성은 높아지지만 그만큼 브로커의 리소스를 많이 사용하게 된다.

파티션

하나의 토픽이 한 번에 처리할 수 있는 한계를 높이기 위해 토픽 하나를 여러개로 나눠 병렬처리가 가능하게 만든 것. 파티션 수만큼 컨슈머를 연결 할 수 있음.

그럼 파티션 수는 몇 개가 적당할까?

파티션 수는 초기 생성 후 언제든지 늘릴 수 있지만, 반대로 한 번 늘린 파티션 수는 절대로 줄일 수 없다. 따라서 파티션의 수는 초기에 작게 생성한 후, 모니터링을 통해 점차 늘려나가야 한다.

적절한 파티션 수를 계산해주는 공식을 제공하는 사이트 컨플루언트 사이트도 있지만, 참고 정도만 하자.

세그먼트

프로듀서에 의해 브로커로 전송된 메시지는 토픽의 파티션에 저장된다. 각 메시지들은 세그먼트라는 로그 파일의 형태로 브로커의 로컬디스크에 저장된다.

토픽 > 파티션 > 세그먼트를 논리적으로 표현하면 다음과 같다. image


Kafka의 핵심 개념

분산 시스템

카프카는 분산 시스템으로 설계되어 있어, 노드(서버)를 클러스터로 구성하여 확장성과 내결함성을 제공합니다. 이를 통해 데이터를 여러 서버에 걸쳐 저장하고 처리할 수 있으며, 서버 장애가 발생해도 시스템 전체의 운영에 큰 영향을 미치지 않습니다.

페이지 캐시

카프카는 운영 체제의 페이지 캐시를 효율적으로 사용하여 높은 I/O 성능을 제공합니다. 이는 카프카가 디스크에서 메시지를 읽거나 쓸 때 발생하는 딜레이를 최소화하여 빠른 데이터 전송 속도를 가능하게 합니다.

배치 전송 처리

카프카는 네트워크와 디스크 I/O를 최소화하기 위해 메시지를 배치로 처리합니다. 생산자는 여러 메시지를 한 번에 카프카에 보내고, 카프카는 이를 모아 한 번에 디스크에 쓰거나 소비자에게 전송하여 처리 효율을 높입니다.

압축 전송

메시지를 카프카 토픽에 보낼 때, 데이터의 용량을 줄이기 위해 압축할 수 있습니다. 이는 네트워크 사용량과 저장 공간을 절약할 수 있게 하며, 특히 대량의 데이터를 처리할 때 유용합니다.

토픽, 파티션, 오프셋

카프카는 데이터를 토픽이라는 카테고리로 분류하고, 각 토픽은 다수의 파티션으로 나뉩니다. 파티션 내에서 각 메시지는 오프셋이라는 고유 식별자를 통해 정렬됩니다. 이 구조는 데이터의 확장성과 병렬 처리를 용이하게 합니다.

고가용성 보장

카프카는 데이터의 복제를 통해 고가용성을 보장합니다. 각 파티션은 여러 브로커에 걸쳐 복제될 수 있으며, 주 브로커에 장애가 발생하더라도 다른 브로커에서 데이터를 제공함으로써 서비스 중단 없이 운영을 지속할 수 있습니다.

주키퍼의 의존성

초기 카프카 버전은 클러스터의 메타데이터 관리와 브로커 간 조정을 위해 주키퍼(ZooKeeper)에 의존했습니다. 주키퍼는 클러스터 구성원의 상태를 추적하고, 리더 선출 등의 중요한 역할을 담당했습니다. 최근 버전의 카프카는 자체 메타데이터 관리 시스템을 구축하여 주키퍼 의존성을 제거하는 방향으로 발전하고 있습니다.


프로듀서

프로듀서는 파티션의 리더로 직접 메시지를 전송.

특정 파티션 또는 랜덤 파티션으로 전송.

빠른 전송 속도 보장.

효율성이 좋은 배치 처리 가능.

설정을 통해 배치 크기나 지연시간 조정.

기본 동작

image 프로듀서 디자인 개요(출처: https://dzone.com/articles/take-a-deep-dive-into-kafka-producer-api/)

ProducerRecord라고 표시된 부분은 카프카로 전송하기 위한 실제 데이터이다. 레코드는 토픽, 파티션, 키, 밸류로 구성.

프로듀서가 카프카로 레코드를 전송할 때, 카프카의 특정 토픽으로 메시지를 전송하기 때문에 레코드에서 토픽과 밸류(메시지 내용)는 필수 값이며, 특정 파티션을 지정하기 위한 레코드의 파티션과 특정 파티션에 레코드들을 정렬하기 위한 레코드의 키는 선택사항(옵션)

각 레코드들은 프로듀서의 send() 메소드를 통해 시리얼라이저(serializer), 파티셔너(partitioner)를 거침.

프로듀서 레코드의 파티션을 지정했을 경우 –> 파티셔너는 아무 동작하지 않고 지정된 파티션으로 레코드를 전달. 프로듀서 레코드의 파티션을 지정 안했을 경우 –> 키를 가지고 파티션을 선택해 레코드를 전달 함. 기본적으로 라운드 로빈(round robin) 방식으로 동작. send() 메소드 동작 이후 레코드들을 파티션별로 잠시 모아 둠. 모아둔 이유는 프로듀서가 카프카로 전송하기 전, 배치 전송하기 위함.

전송이 실패하면 재시도 동작이 이뤄지고, 지정된 횟수 만큼의 재시도가 실패하면 최종 실패 전달. 전송이 성공하면 메타데이터를 리턴하게 됨.

주요 옵션

ACKS(Acknowledgements) 메시지 손실과 직접적인 관련이 있는 옵션

  • ACKS = 0
    프로듀서는 메시지를 보낸 후 어떤 응답도 기다리지 않습니다. 따라서 전송 속도는 매우 빠르지만, 메시지가 실제로 카프카 클러스터의 파티션 리더에 의해 받아졌는지 확인할 수 없습니다. 이로 인해 네트워크 문제나 서버 오류가 발생할 경우 메시지 손실이 발생할 수 있습니다

  • ACKS = 1 (Strongly Recommend)
    프로듀서는 파티션의 리더가 메시지를 받았음을 확인한 후에만 메시지 전송을 완료합니다. 이 설정은 메시지 전송의 신뢰성을 높이면서도, 전송 속도를 비교적 빠르게 유지할 수 있어 많은 환경에서 기본값으로 사용됩니다. 리더가 확인 응답을 보내면, 프로듀서는 전송을 성공적으로 마무리합니다

  • ACKS = ALL
    가장 높은 신뢰성을 요구할 때 사용하는 설정입니다. 메시지가 파티션의 리더 뿐만 아니라 모든 팔로워 복제본에도 성공적으로 복사되었음을 확인한 후에만 메시지 전송이 완료됩니다. 이 방식은 메시지 손실을 거의 방지하지만, 모든 복제본의 확인을 기다려야 하므로 전송 속도가 상대적으로 느립니다


컨슈머

컨슈머는 파티션의 리더에게 fetch 요청을 하는 역할

컨슈머는 위치를 기록하고 있는 오프셋으로부터 메시지를 가져온다.

컨슈머의 목적은 컨슈머가 가능한 최대 속도로 가져갈 수 있도록 하는 것.

기본 동작

프로듀서가 카프카의 토픽으로 메시지를 전송하면 해당 메시지들은 브로커들의 로컬디스크에 저장된다. 컨슈머를 이용해 토픽에 저장된 메시지를 가져올 수 있다.

컨슈머 그룹은 하나 이상의 컨슈머들이 모여있는 그룹을 의미한다. 컨슈머는 반드시 컨슈머 그룹에 속하게 된다. 컨슈머 그룹은 각 파티션의 리더에게 카프카 토픽에 저장된 메시지를 가져오기 위한 요청을 보낸다. 이때 파티션 수와 컨슈머 수는 일대일로 매핑되는 것이 이상적이다.

메시지를 가져오는 방식

  • 오토 커밋
    auto.commit.enable 옵션이 true로 설정된 경우. 기본값으로 가장 많이 사용됨. 오프셋을 주기적으로 커밋하므로 관리자가 따로 관리하지 않아도 되는 장점. 컨슈머 종료 등이 빈번히 일어나면 일부 메시지를 못 가져오거나 중복으로 가져오는 경우가 있음.

  • 동기 가져오기(ConsumerSync)
    오토커밋과 달리 poll()을 이용해 메시지를 가져온 후 처리 완료하고 현재의 오프셋을 커밋하는 방식. 동기 방식으로 가져오는 경우 속도는 느리지만, 메시지 손실은 거의 발생하지 않음. 메시지의 중복 이슈는 피할 수 없음.

  • 비동기 가져오기(ComsumerAsync)
    비동기 가져오기는 컨슈머가 데이터를 요청한 후, 바로 오프셋을 커밋하는 방식. 다음 데이터 블록을 병렬로 요청하면서 동시에 데이터를 처리할 수 있음. 이 방식은 네트워크 지연의 영향을 덜 받으며, 데이터 처리와 데이터 요청을 동시에 진행할 수 있어 전반적인 처리 성능을 향상시킬 수 있다. 중간에 비동기 커밋이 실패하더라도 재시도를 하지 않음. 그러나 프로그래밍이 더 복잡할 수 있고, 데이터 처리 순서를 관리해야 하는 추가적인 로직이 필요할 수 있다.

컨슈머 그룹

하나의 토픽을 여러 컨슈머들이 구독.

컨슈머 그룹으로 그룹핑하여 컨슈머를 확장할 수 있다.

프로듀서가 토픽으로 보내는 메시지 비율을 높인다면?

컨슈머는 프로듀서의 속도를 따라가지 못하게 된다.

image

위 그림과 같이 토픽의 파티션과 일대일로 매핑되어 메시지를 가져오게 됨.

컨슈머들은 하나의 컨슈머 그룹 안에 속해 있으며, 그룹 내의 컨슈머들은 서로의 정보를 공유 함.

예를 들어 Consumer01이 문제가 생겨 종료됐다면, Consumer02 또는 Consumer03은 Consumer01이 하던 일을 대신해 peter-01 토픽의 partition0을 컨슘하기 시작 함.

This post is licensed under CC BY 4.0 by the author.