Infra/Kafka

Apache Kafka (1)

코드파고 2024. 11. 13. 17:15

Apache Kafka Series - Learn Apache Kafka for Beginners v3를 수강하며 기록한 내용입니다.

 

도입 이유

  • 소스 시스템, 타켓 시스템이 확장됨에 따라 중재 시스템의 필요도가 높아짐

장점

  • 오픈소스 프로젝트
  • 좋은 성능을 보여줌
  • 내결함성
  • 시스템 간 결합도를 낮출 수 있다.

사용처

  • 메시징
  • 활동 추적
  • 메트릭, 로그 수집

Topics

  • 특정 데이터의 스트림
  • 제약 없는 테이블과 같다
  • 이름으로 토픽을 선정함
  • 어느 종류의 메시지라도 괜찮다
  • 연속된 메시지는 "Stream"이라 칭함
  • 쿼리는 할 수 없고, Producer가 쓰면(publish), Consumer가 읽을 수 있다.

Partition

  • 토픽은 파티션으로 나뉜다.
  • 파티션에 데이터를 쓸 수록 Offset(index와 유사)이 증가
  • 파티션에 쓰인 데이터는 불변이며, append하는 수 밖에 없다.
  • 특정 기간동안만 데이터가 남아있다(디폴트 : 1주 -> 커스텀 가능)
  • 오프셋은 특정 파티션에만 의미 있다.
    • ex. 파티션 1의 오프셋 1 != 파티션 2의 오프셋 1
  • 데이터가 지워지더라도 지워진 데이터의 오프셋은 재사용되지 않음
  • 파티션은 원하는 만큼 생성 가능
  • 데이터는 키가 제공되지 않으면 임의의 파티션에 선정됨
만일 위/경도를 트럭 어플리케이션이 일정 주기로 전송할 시 trucks_gps를 토픽으로 선정, 메시지를 보냄
알람 서비스, 위치 대시보드가 이 스트림을 소비할 수 있음

Offset

파티션 내에서만 순서 보장(시퀀스와 유사)

Producers

  • 토픽에 데이터 적재
  • 데이터를 어느 파티션에 적재할 지 알고 있음(브로커가 해당 정보를 가지고 옴)
  • 브로커가 실패할 경우, 프로듀서가 자동으로 복구함Key
  • 카프카 메시지의 일부
  • 키가 null일 경우 라운드 로빈 방식으로 파티션에 전송
  • 값이 있을 경우 특정 파티션으로 전송

Message

Serialization

  • 인풋, 아웃풋 모두 바이트 형태이기 때문에 직렬화 필요
  • 오브젝트 또는 데이터를 바이트로 직렬화하는 것
  • 직렬화 대상 : value, key
  • 메시지 키 해싱은 murmur2. 알고리즘 사용

Producer Ack

  • 데이터를 쓸 때 ACK를 수신하는 것
  • ack = 0 : 프로듀서가 ack를 기다리지 않음(데이터 유실 가능성 있음)
  • ack = 1 : 프로듀서가 리더 ack를 기다림(데이터 유실 가능성이 일정 부분 제한됨)
  • ack = all : 리더 브로커, 레플리카 브로커의 ack 모두 기다림(데이터 유실이 없음)

Consumer

  • pull 모델 : 브로커로부터 데이터를 요청해서 읽는다.
  • 어느 브로커로부터 읽어들여야 하는지 자동으로 알고 있다.
  • 브로커가 실패하면, 복구할 방법을 안다.
  • 낮은 오프셋부터 높은 오프셋 순으로 각 파티션 내에서 탐색

Deserialization

  • Key 바이트를 원형으로, Value 바이트를 원형으로 역직렬화하여 사용한다.
  • 토픽의 라이프사이클 동안 절대 직렬화 타입을 바꾸면 안됨 -> 바꾸려면 새로운 토픽을 만들어라

Consumer Group

  • 모든 어플리케이션은 Consumer Group에 접근하여 데이터를 읽게 된다.
  • 그룹 내 컨슈머는 각각 배타적으로 파티션을 읽는다.
    • 파티션 개수 > 컨슈머 개수일 경우, 그룹 내 남는 컨슈머가 생기게 되며, 이는 비활성화된다.
  • 하지만 서로 다른 그룹이라면 같은 파티션에 대해 붙을 수 있다.
    • 컨슈머 프로퍼티 중 group.id를 활용
      이전 예제에서 알람 서비스, 위치 대시보드가 `trucks_gps` 토픽을 사용하려 한다면, 알람 서비스, 위치 대시보드는 별도의 `Consumer Group`에 존재해야 한다.

Offset

  • 카프카는 어느 컨슈머 그룹이 얼만치 읽고 있었는지를 저장한다.
    • 이는 __consumer_offsets 카프카 토픽에 저장. 그룹 그 자체에 저장하지 않음
    • 해당 그룹이 어디까지 읽었는지 알 수 있다.

Semantics

  • 기본적으로 자바는 자동으로 오프셋을 저장하게 된다.
  • 수동 조작 시 3가지 semantic이 있음
    • At Least Once 최소 한번(주로 사용)
      • 메시지가 사용되면 커밋됨
      • 메시지 처리가 잘못되면 다시 읽을 수 있음
      • 메시지를 여러번 가공할 위험이 있기 때문에 멱등성 보장이 필수적
    • At Most Once 최대 한 번
      • 메시지 수신 시 커밋
      • 메시지 처리가 잘못 되면 메시지를 잃을 수 있음
    • Exactly At Once 딱 한번
      • 카프카에서 카프카로 전송할 시 Transactional API 사용(Kafka Streams API 사용)
      • 카프카에서 외부 시스템으로 전송할 시: 멱등한 컨슈머를 사용해야 한다.

Brokers

  • 카프카 클러스터는 다수의 브로커(서버)로 구성
  • int 형태의 id로 구분됨
  • 각 브로커는 특정 토픽 파티션을 포함.
  • Bootstrap 브로커(사실상 아무 브로커)에 연결하면 클러스터 전체에 대해 알 수 있다.
  • 3개의 브로커부터 시작하는 게 좋다. 큰 클러스터는 100개 이상의 브로커로 구성되기도 함
  • 파티션을 분산하여 브로커에 분산 저장

Serach Mechanism

하나의 브로커에 일단 접속하게 되면 카프카 클라이언트는 어느 브로커에 접속할 지 찾아갈 수 있다. 순서는 다음과 같다.

  1. 부트스트랩 브로커 접속하여 메타데이터 요청
  2. 브로커는 모든 브로커 리스트를 반환
  3. 해당하는 브로커에 접속

Replication Factor

  • 2, 3 개의(보통 3) 복제 인자를 가져야 한다
  • 브로커가 다운되었을 때, 다른 살아있는 브로커의 replica 데이터를 사용한다.
  • 리더 브로커를 선정해야 한다. 팔로워는 ISR(in-sync Replica)라 칭함
    • 프로듀서는 리더 브로커에만 데이터 기입
  • 2.7 버전 이후로 Client들은 성능 개선을 위해 가까운 브로커 replica를 읽기도 한다.

Durability

Replication Factor가 3이면, 두개의 브로커 다운까지 견딜 수 있다.
= 즉 Replication Factor가 N개이면, N-1 개의 브로커의 다운까지 견딤

Zookeeper

  • 브로커를 관리
  • 리더 파티션을 선정
  • 변경 시 알림을 전송
    • ex) 새 토픽, 브로커 다운, 브로커의 생성, 토픽 삭제, etc
  • 버전별 주키퍼 사용
    • 카프카 2.x는 주키퍼가 없으면 동작하지 않음
    • 카프카 3.x는 Kafka Raft를 주키퍼 대신 사용
    • 카프카 4.x는 Zookeeper 사용하지 않음
  • 주키퍼는 홀수로 구성되어야 함
  • 리더, 팔로워로 구성
    • 리더 : 쓰기
    • 팔로워 읽기
  • v0.10 이전에는 Consumer Offset을 저장. 이후에는 topic에 저장
  • 브로커를 쓰기 위해서는 Kafka 4.0 버전 출시 전 까지는 Zookeeper 사용
  • 클라이언트에서는 그 동안의 버저닝을 통해 주키퍼를 대체하도록 이전해왔으므로 사용하지 않아도 됨
    • 절대 Client에서 주키퍼를 쓰지 말라고 당부.. ㅎㅎ
  • 카프카보다 안전하지 않기 때문에 서서히 떼어내고 있는 중

Kafka KRaft

  • 카프카는 주키퍼 의존성을 떼어내고 있는 중이다.
    • bec) 카프카가 10만개 이상의 파티션을 가질 시 주키퍼에서 스케일링 이슈가 발생
  • 주키퍼를 떼어내면 갖는 장점
    • 수백만의 파티션을 관리하고, 설정하는 데 용이
    • 안정성, 모니터링, 관리가 쉬워진다.
    • 주키퍼에 대한 별도 보안을 설정하지 않아도 됨(하나의 보안 모델만 관리 가능)
    • 카프카를 싱글 프로세스로 시작 가능
    • 컨트롤러 종료와 복구의 속도 개선
  • 3.3.1 버전에서 KRaft 사용 가능
  • 4.0 버전에서는 주키퍼를 완전 대체
  • Quorom Controller 내에 브로커를 위치시키고 리더 브로커를 선정
    • Zookeeper는 Zookeeper의 집합 레이어가 Broker 상위에서 Broker를 관리하는 형태
    • Zookeeper보다 단순화된 구조를 가짐