Kafka

카프카 기본 개념과 구조

반응형

 

개요

  • 이번 글에서는 카프카의 기본 개념과 구조에 대해 포스팅할 계획입니다.
  • 카프카의 처리량을 높이기 위해 설계된 분산 시스템, 페이지 캐시, 배치 전송 등에 대해서 살펴보겠습니다.

카프카의 기초 용어와 개념

용어 정리

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

리플리케이션

  • 리플리케이션이란 브로커로 전송된 각 메시지들을 여러 개로 복제해서 카프카 클러스터 내부 브로커들에게 분산시키는 동작을 의미합니다.
  • 메시지들을 분산시키기 위해 브로커를 복제하게 되는데 이때 개발자가 설정한 팩터 수 만큼 리플리케이션이 수행됩니다.(ex:)3팩터를 수행할 경우 총 3개의 브로커가 수행됩니다.)
  • 리플리케이션 개수가 증가할 수록 프로그램은 안정적으로 변하지만 그만큼의 브로커 리소스를 사용하게 됩니다.

브로커를 효율적으로 사용하는 방법

  • 테스트나 개발 환경에서는 리플리케이션 팩터 수를 1로 설정합니다.
  • 운영 환경에서 유실을 허용해도 되는 서비스라면 팩터 수를 2로 설정하고 아닐 경우 3개로 설정합니다. 그 이상의 개수는 카프카 공인 개발자의 경험상 필요 없는 수치라고 합니다.

파티션

  • 파티션이란 하나의 토픽이 한 번에 처리할 수 있는 한계를 높이기 위해 등장한 개념으로서 하나의 토픽을 여러개로 나눠 병렬 처리가 가능하게 만든 것을 파티션이라고 호칭 합니다.
  • 파티션의 적절한 수는 인터넷에 공식도 있지만 메시지의 크기나 초당 메시진 건수에 따라 다르기 때문에 시행착오가 필요합니다.
  • 파티션의 수는 늘일 수는 있지만 줄일 수 없다는 것을 반드시 기억해야 합니다.

세그먼트

  • 프로듀서가 브로커로 메시지를 전송할 경우 토픽의 파티션에 메시지가 전송되게 되는데 각 메시지들은 세그먼트라는 로그 파일 형태로 브로커의 로컬 디스크에 저장됩니다.
  • Broker 안에 파티션이 있고 파티션 안에 세그먼트가 존재합니다.
  • 프로듀서가 메시지를 보내게 되면 카프카 안의 토픽 안의 파티션에 세그먼트 형태로 저장되게 되며 컨슈머는 카프카의 알림으로 인해 메시지를 가져가게 되는 형식입니다.

카프카의 핵심 개념

  • 이번 목차에서는 카프카가 어떻게 안정적이면서 높은 처리량을 가질 수 있었는지에 대해서 설명해보겠습니다.

분산 시스템

  • 카프카는 카프카 브로커의 리소스 사용률이 증가할 경우 확장하여 분산 처리가 가능하도록 구현되어 있습니다.
  • 또한 확장에도 용이하여 분산 시스템으로서 역할을 수행하기에 적합하게 설계되어 있습니다.

페이지 캐시

  • 카프카는 OS의 페이지 캐시를 활용하는 방식으로 설계되어 있습니다. 페이지 캐시는 직접 디스크에 읽고 쓰는 대신 물리 메모리 중 애플리케이션에 사용하지 않는 잔여 메모리를 사용합니다.

배치 전송 처리

  • 카프카는 프로듀서에서 브로커로 메시지를 보낼때 레코드의 일정 개수가 충족되었을때 메시지를 보내는 배치 처리 기능이 존재합니다.

압축 전송

  • 카프카는 메시지 전송이 높은 압축 전송을 권장하고 있습니다.
  • 지원하는 확장자는 아래와 같습니다.
    • gzip
    • snappy
    • lz4
    • zstd
  • 높은 압축률이 필요한 경우라면 gzip 또는 zstd를 권장하고 빠른 응답 속도가 필요하다며 lz4 또는 snappy를 권장합니다.

토픽, 파티션, 오프셋

  • 파티션이 저장되는 위치를 오프셋이라고 합니다. 오프셋은 순차적으로 증가하는 64비트 정수 형태로 구성되어있습니다.
  • 카프카는 오프셋을 통해 메시지의 순서를 보장하고 컨슈머에서는 마지막에 읽은 메시지의 위치를 알 수 있습니다.

고가용성 보장

  • 카프카에서는 브로커(서버)가 다운되어도 다른 브로커가 기능을 대리로 수행하여 안정적인 고가용성을 보장합니다.
  • 이러한 고가용성을 보장하기 위해 카프카는 리플리케이션 기능을 제공합니다.
  • 카프카의 리플리케이션은 토픽을 복제하는 것이 아닌 파티션을 복제합니다.
  • 원본과 복제하려는 파티션을 구분하기 위해서 카프카에서는 리더(원본 브로커)와 팔로워(복제할 브로커)라는 용어로 원본과 복제할 브로커를 구분합니다.
💡
카프카가 권장하는 리플리케이션 팩터수는 3입니다.

프로듀서의 동작 원리

프로듀서의 내부 흐름

  • 프로듀서는 메시지를 보낼때 실제 데이터를 프로듀서 레코드라고 호칭합니다.
  • 프로듀서 레코드에는 브로커의 최종 목적지인 Partiton에 전송하기위해 Partition의 정보를 담아서 카프카의 토픽에 메시지를 보냅니다.
  • 여기서 좀 더 자세히 살펴보면 실제로 Partition에 전송해야 하기 때문에 Partition 상위의 레이어인 Topic에 대한 정보를 포함해서 전송합니다.
  • 그래서 총 Partition, Topic, 보내고자 하는 데이터의 Key값과 Value값을 포함해서 전송합니다.
  • 프로듀서 레코드의 Key값은 필수값이 아닙니다. 만약 Key값을 등록하지 않는다면 파티셔너는 기본 정책인 라운드 로빈 정책을 통해 수행되어 도착될 파티션을 결정합니다. 만약 파티션을 지정해서 프로듀서 레코드를 전송한다면 파티셔너는 수행되지 않고 지정되 파티션으로 레코드를 전송합니다.
  • 이후 프로듀서 레코드를 파티션에 보관한 뒤 설정된 배치 옵션을 통해 카프카로 전송됩니다.

프로듀서 주요옵션

  • 도메인과 서비스의 정책에 따라서 개발자는 프로듀서를 커스터 마이징하여 사용할 수 있습니다.
  • 아래는 카프카 프로듀서의 주요 옵션입니다.
프로듀서 옵션 설명
bootstrap.servers 클라이언트가 카프카 클러스터에 처음 연결하기 위한 호스트와 포트 정보를 적는 옵션
client.dns.lookup 클라이언트가 host에 연결에 실패 했을 경우 다른 IP로 host에 연결을 시도하는 설정
acks 프로듀서가 카프카 토픽 리더에 메시지를 전송한 후 프로듀서가 요청이 완료된지를 판단하는 옵션을 의미함. 0,1,all(-1) 3가지로 나타낼 수 있으며 0은 빠른 전송을 의미하여 컨슈머의 팔로워가 메시지를 받는지 확인 안하여 일부 메시지 손실 가능성이 있음. 1은 리더가 메시지를 받았는지 확인하지만 모든 컨슈머의 팔로워를 확인하지는 않음. all은 컨슈머의 모든 팔로워가 메시지를 받았는지 여부를 확인함. 팔로워가 존재하다면 메시지가 손실될수 없음.
buffer.memory 프로듀서가 카프카 서버로 데이터를 보내기 위해 잠시 대기 할 수 있는 전체 메모리 바이트를 의미함
compression.type 프로듀서가 메시지 전송 시 선택할 수 있는 압축타입입니다. none, gzip, snappy, lz4, zstd중 원하는 타입 선택 가능.
enable.idempotence 멱등성을 유지할 지에 대한 옵션입니다. true와 false로 설정이 가능하여 true로 설정할 경우 멱등성을 유지하겠다는것을 의미합니다. true로 설정할 경우 max.flight.requests.per.connection은 5이하, retries는 0이상, acks는 all로 설정해야함
max.in.flight.requests.per.connection 하나의 커넥션에서 프로듀서가 최대한 ACK 없이 전송할 수 있는 요청수를 의미함. 메시지 순서를 1로 설정할 것을 권장하지만 성능이 감소할 수 있음
retries 일시적인 오류로 인해 전송에 실패한 데이터를 다시 보내는 횟수를 의미함
batch.size 배치 처리할 데이터 size의 크기를 명시하는 옵션
linger.ms linger.ms에 설정한 시간을 초과하면 record의 개수가 batch.size 개수를 만족하지 않아도 브로커로 전송하게 됨.
transactional.id ‘정확히 한 번 전송’을 위해 사용하는 옵션. 동일한 TransactionalId에 한해 정확히 한 번을 보장함. 해당 옵션을 사용하기 위해서는 enable.idempotence를 true로 설정해야함.
💡
프로듀서의 전송방식에는 메시지를 보내고 확인 하지 않기, 동기 전송, 비동기 전송이 있다. 동기 전송에서는 CallBack으로 추가 로직을 작성할 수도 있다.

컨슈머의 기본 동작과 예제 맛보기

  • 컨슈머는 카프카 토픽에 저장되어있는 메시지를 가져오는 역할을 담당합니다.
  • 컨슈머는 카프카에서 메시지를 가져오는것 말고도 내부적으로 컨슈머 그룹, 리벨런싱 등 여러 작업을 수행합니다.
  • 이러한 옵션들을 잘 활용하지 못한다면 프로듀서가 빠르게 메시지를 전달해도 컨슈머는 메시지를 소화하지 못하여 지연이 발생하게 되므로 컨슈머의 내부 동작과 기능들에 대해 학습해야합니다.

컨슈머의 기본 동작

  • 앞서 설명한 것처럼 컨슈머는 카프카 토픽에 저장된 메시지를 가져옵니다.
  • 이때 컨슈먼는 반드시 컨슈머 그룹이라는는 그룹에 속하게 됩니다.
  • 컨슈머 그룹은 각 파티션의 리더에게 카프카 토픽에 저장된 메시지를 가져오기 위한 요청을 날리게 됩니다.
  • 이때 파티션 수와 컨슈머의 수는 일대일로 매핑되는것이 이상적입니다. 이상적인 조건일뿐 반드시 일대일로 매핑해야 되는것은 아니지만 파티션 수보다 컨슈머 수가 많게 구현되는것이 바람직한 설계는 아닙니다.
  • 왜냐하면 컨슈머의 개수가 파티션의 개수보다 많다고 해서 빠르게 토픽의 메시지를 가져오거나 처리량이 높아지는것이 아니기 때문입니다. 단순히 컨슈머들이 대기 상태로만 존재하기 때문에 리소스만 사용하므로 비효율적인 설계입니다.

컨슈머의 주요 옵션

  • 컨슈머 또한 프로듀서와 같이 다양한 옵션을 제공합니다.
  • 컨슈머를 어떤 옵션으로 어떻게 섲렁하는지에 따라 메시지의 중복, 유실 등의 상황을 막을 수 있습니다.
컨슈머 옵션 설명
bootstrap.servers 프로듀서와 동일하게 브로커의 정보를 입력한다.
fetch.min.bytes 브로커에서 한 번에 가져올 수 있는 최소 데이터 크기를 의미함. 지정한 크기 보다 작은 경우 데이터를 가져오지 않고 누적하여 최소 데이터 크기가 될 경우 메시지를 가져옴
group.id 컨슈머가 속한 컨슈머 그룹을 식별하는 식별자를 의미함. 동일한 그룹 내의 컨슈머 정보는 모두 공유됨
heartbeat.interval.ms 하트비트가 있다는 것은 컨슈머의 상태가 active임을 의미함. session.timeout.ms와 밀접한 관계가 있으며 session.timeout.ms보다 낮은 값으로 설정해야 함. 일반적으로 session.timeout.ms의 1/3로 설정한다.
max.partition.fetch.bytes 파티션당 가져올 수 있는 최대 크기를 의미함
session.timeout.ms 컨슈머 그룹에서 컨슈머가 종료된지를 판단하는 시간을 의미함. 컨슈머는 주기적으로 컨슈머 그룹에게 하트비트를 보내야 함. 만약 해당 session.timeout.ms 전까지 하트비트를 보내지 않는다면 해당 컨슈머는 종료된 것으로 간주하고 컨슈머 그룹에서 제외하여 리밸런싱을 진행함
enable.auto.commit 백그라운드에 주기적으로 오프셋을 커밋함
auto.offset.reset offset reset 타입을 의미함. 카프카에 초기 오프셋이 없거나 현재 오프셋이 존재하지 않는 경우 auto.offset.reset에서 설정한 옵션으로 reset 작업을 진행함. reset type은 아래 3가지가 존재함. 1.earliest: 가장 초기의 오프셋값으로 설정 2.latest: 가장 마지막의 오프셋값으로 설정 3.none: 이전 오프셋값을 찾지 못하면 에러를 발생
fetch.max.bytes 한 번에 가져오기 요청으로 가져올 수 있는 최대 크기
group.instance.id 컨슈머의 고유한 식별자를 의미함. 설정한다면 static 맴버로 간주되어 불필요한 리밸런싱을 하지 않음
💡
컨슈머의 메시지를 가져오는 방식에는 오토 커밋, 동기 가져 오기, 비동기 방식 3가지가 존재합니다.
💡
컨슈머의 비동기 처리에서는 오프셋 커밋을 실패해도 재시도 하지 않습니다. 카프카는 오프셋 순서를 기준으로 메시지를 재전송하는데 순서가 보장안되는 비동기처리에서 중복으로 메시지를 보낼수 있기 때문입니다.

 

반응형