Kafka의 탄생 배경
LinkedIn은 사용자 프로필 조회, 피드 업데이트, 메시징, 광고 클릭 같은 이벤트가 초당 수십만 건 발생하고 있었고, 해당 데이터들을 활용하려면 여러 시스템에 흘려보내야했다.
- 실시간 서비스: 추천 시스템, 뉴스피드 랭킹, 광고 타겟팅
- 배치 분석: Hadoop 같은 데이터 웨어하우스
- 모니터링: 서비스 장애 탐지, 사용자 행동 분석
문제는 각각 다른 전송 파이프라인이 존재했고, 코드도 중복되며 관리가 매우 힘들었다.
ActiveMQ 를 사용해서 해결해보려고도 했지만 브로커를 정지시킬 수 있는 결함 + 규모 확장성 문제가 있었다.
이를 해결하기 위해 링크드인은 다음 목표를 내세워 자체 개발을 진행하였다.
- 푸시-풀 모델을 사용함으로써 프로듀서와 컨슈머를 분리
- 다수의 컨슈머가 사용할 수 있도록 메세지 교환 시스템의 데이터를 영속적으로 저장
- 높은 메세지 처리량을 보일 수 있도록 최적화
- 데이터 스트림의 양이 증가함에 따라 시스템을 수평 확장 가능
2010년에 Github 에 오픈소스로 공개되었고, 2011년 아파치 재단의 인큐베이터 프로젝트, 2012년 정식 프로젝트가 되었다.
카프카 이용 사례
앞서 링크드인 스토리를 살펴봤듯이 카프카를 다음과 같이 이용해볼 수 있다.
- 활동 추적
- 메시지 교환
- 지표 및 로그 수집
- 데이터베이스 커밋 로그
- 스트림 처리 (하둡은 많은 시간동안 누적된 대용량 데이터를 처리하는 것을 의미하고, 여기서 스트림은 실시간으로 데이터를 처리하는 것을 의미)
왜 카프카일까?
- 다중 프로듀서
- 다중 컨슈머
- 디스크 기반 보존
- 확장성
- 고성능
- 플랫폼 기능
여기서 다중 프로듀서, 컨슈머는 무슨 뜻일까? 네이버 뉴스 댓글 시스템을 떠올려보자.
수많은 유저가 동시에 댓글을 하나씩 작성한다면 각 웹 서버들을 댓글 이벤트를 카프카 프로듀서로 보내고, 하나의 토픽(ex: comments 이름의 토픽)에 모아서 저장한다. 이후 해당 메시지를 구독한 다수의 컨슈머에서 독립적으로 가져가 처리한다. 예를 들어, comments 라는 토픽을 토대로 검색 시스템 컨슈머(실시간 검색 가능하게 함), 스팸 필터링 컨슈머(욕설, 광고 감지), 추천 시스템 컨슈머(좋아요, 반응 데이터 토대로 인기 게시물 반영)가 있을 수 있다. 이는 전통적으로 하나의 메세지를 하나의 클라이언트에서만 소비할 수 있는 큐 시스템과 차이가 있다. 추가로 여러 개의 컨슈머를 하나의 그룹으로 만들어서 처리할 수도 있다. 이는 내부적으로 offset 을 관리하기 때문에 가능하다.
또한 카프카는 디스크 기반으로 데이터를 저장하기 때문에 데이터 유실의 위험도가 적다. 그리고 이후 데이터가 증가할 때도 유연하게 확장할 수 있는 장점이 있다.
메세지
앞서 네이버 뉴스 댓글 시스템을 예시로 들 때, '메세지', '토픽'이라는 단어를 사용했는데 정확히 무슨뜻일까? 카프카에서 사용하는 용어들 개념에 대해 알아보자.
메세지
- 카프카에서의 기본 데이터 단위
- 메세지 단위로 토픽 내 파티션에 저장된다
- 메세지에는 키라는 메타데이터가 존재하고, 추후 프로듀서는 해당 키를 통해 어느 파티션에 저장할지 결정한다
- 보통 메세지를 쉽게 이해할 수 있도록 JSON, XML, Avro 스키마에 맞춰 일관적인 형식에 맞춘다
- 카프카에서는 효율적으로 메세지를 관리하기 위해 배치단위로 저장한다. 물론 이는 지연과 처리량 간 트레이드오프를 발생시킨다. 예를 들어, 배치가 너무 클 경우 네트워크 상 한번에 데이터를 처리할 수 있는 장점이 있지만, 그만큼 즉각적으로 데이터를 처리하기는 어려운 단점이 있다.
토픽과 파티션
Topic (토픽)
- 데이터가 모이는 큰 카테고리
- Producer가 메시지를 보낼 때 “어떤 주제(Topic)”로 보낼지 지정
- Consumer는 특정 Topic을 구독해서 메시지를 가져감
예시)
orders → 주문 관련 이벤트
payments → 결제 관련 이벤트
logins → 로그인/회원 활동 이벤트
Partition (파티션)
- Topic은 내부적으로 여러 개의 Partition으로 쪼개져 저장된다
- 각 Partition은 쓰여질 때 뒷부분에 쓰여지고, 읽을 때는 앞부분부터 읽는다 => 순서 보장. 즉, 토픽 내 메세지 전체에 대해서 순서는 보장못하지만 파티션 내부에서는 순서가 보장된다.
- 토픽 내 Partition은 서로 다른 서버에 저장될 수 있어 메세지의 용량을 초과해서 저장이 가능하다
- 서로 다른 서버에 파티션 복제본을 만들 수 있다.
예시
orders Topic에 Partition이 3개 있다 가정
Partition 0: 주문 #1, #4, #7 …
Partition 1: 주문 #2, #5, #8 …
Partition 2: 주문 #3, #6, #9 …
Topic & Partition의 관계
Topic은 “데이터의 주제”
Partition은 “그 주제 안에서의 데이터 조각”
프로듀서와 컨슈머
프로듀서는 특정 토픽 내 파티션에 작성하고 컨슈머는 해당 파티션을 읽어온다.
Producer (프로듀서)
- Kafka에 데이터를 보내는 애플리케이션
- 메시지를 특정 Topic 내 어떤 Partition에 보낼지도 결정한다 (어떤 파티션에 보낼지는 내부 파티셔너에 해시 계산에 의해 정해진다)
Consumer (컨슈머)
- Kafka에서 데이터를 가져와 처리하는 애플리케이션
- 특정 Topic을 구독(subscribe)하고, 파티션 단위로 메시지를 읽는다
- 메시지를 읽은 위치를 Offset으로 기록함으로써 어느 메세지까지 읽었는지 유지한다
- 다수의 컨슈머들은 하나의 그룹으로 이루어지고, 하나의 컨슈머당 하나의 파티션을 담당한다
즉, 프로듀서는 특정 토픽 내 파티션에다가 쓰고(파티션 뒷부분에 추가), 컨슈머 그룹 내 컨슈머들은 각자 자기가 맡은 파티션을 읽는다(파티션 앞부분부터 읽기).
브로커와 클러스터
Broker (브로커)
- Kafka 서버 한 대 = Broker
- Producer가 보낸 메시지를 받아서 디스크에 저장하고, Consumer에게 전달해주는 역할
- 각 Broker는 여러 개의 Partition을 저장할 수 있다
- 메시지를 안전하게 저장하기 위해 복제(replication)하며 관리한다
예시
Broker 1: orders 토픽의 Partition 0 보관
Broker 2: orders 토픽의 Partition 1 보관
Broker 3: orders 토픽의 Partition 2 보관
Cluster (클러스터)
- 여러 대의 Broker가 모인 것 = Kafka Cluster
- Kafka 클러스터는 파티션과 리더/팔로워 구조를 통해 확장성 + 장애 복구를 제공한다
- 클러스터 내부 브로커들 중 하나는 컨트롤러로서 파티션을 브로커에게 할당하거나 보로커들 모니터링한다
- 파티션이 할당된 브로커를 통해 프로듀서와 컨슈머는 메세지를 처리한다. 이때 해당 브로커 내 파티션을 파티션 리더라고 부른다.
- 해당 파티션을 복제하여 다른 브로커에 넣어 안정성을 높인다. 만약 파티션 리더 역할을 맡은 파티션이 있는 브로커가 내려갈 경우 다른 브로커 내 파티션이 파티션 리더로 승격된다. 해당 브로커 내 파티션을 파티션의 팔로워라고 한다.
- 미러메이커 툴을 통해 데이터를 복제하여 다중 클러스터를 구축할 수도 있다