Kafka

[Kafka] 카프카 기본 개념_1 (브로커, 프로듀서, 컨슈머, 메시지 + 주키퍼)

15호의 개발자 2022. 1. 31. 13:14
반응형

[Kafka] 카프카 기본 개념 및 간단한 설명

 


 

카프카란?

카프카(Kafka) 또는 카프카 클러스터(Kafka Cluster) 분산 스트리밍 플랫폼으로써, 여러 대의 브로커를 구성한 클러스터를 의미한다. 카프카는 링크드인(LinkedIn)의 개발자 세 명(제이 크렙스, 준 라오, 네하 나크헤데)이 만든 것이 시초였으며, 2011년 아파치 오픈소스로 공개된 아파치 프로젝트 애플리케이션이다.

 

당시 링크드인 개발자들이 직면한 문제

  1. 데이터 중앙저장소가 무엇인가?
    - 현재는 DW(Data Warehous)가 익숙하지만, 당시에는 하둡(Hadoop)도 없었고 빠르게 대응할 SQL 데이터베이스도 없었다. 하지만 모든 데이터에 빠르게 접근하기 위한 조치가 필요했다.
  2. 다양한 데이터 소스가 존재한다는 이슈
    - 사용할 데이터들로 데이터베이스뿐만 아니라 애플리케이션, 이벤트, 네트워크 핑도 수집했는데, 이런 데이터들을 어떻게 통합할지에 관한 이슈

 

당시 링크드인 개발자들은 애플리케이션과 데이터를 바라보는 시각차에 대해 고민했다. 애플리케이션들은 데이터베이스나 큐를 사용했었고, 데이터는 ETL 툴이나 analytics 툴을 통해 사용했었다. 애플리케이션과 데이터 간에 시각차가 확실했지만, 이들 개발자들은 새롭게 무언가를 만드는 건 싫었고, 있는 것들을 조합해서 문제를 해결하려고 했다. 하지만 그럴수록 해결되는 건 없었고, 결국 카프카를 만들게 되었다.

 

 

카프카는 데이터 처리를 여러 애플리케이션에서 처리하는 것이 아니라 중앙에서 처리할 수 있도록 중앙처리화한 것이다. 카프카를 통해 웹사이트, 애플리케이션, 센서 등에서 취합한 데이터 스트림을 한 곳에서 실시간으로 관리하게 되었다. 기업의 대용량 데이터를 수집하고 이를 사용자들이 실시간으로 소비할 수 있게 만든 것이다.

 

카프카와의 연동을 지원하는 여러 프로그램들이 계속해서 증가하는 추세이므로, 이미 데이터 파이프라인이 복잡한 레거시 아키텍처를 가지고 있는 기업이더라도 손쉽게 카프카와 연동할 수 있을 것이다. 미래 빅데이터 처리의 핵심이라고 불리는 카프카가 좋은 이유는 아래와 같다.

 

카프카가 좋은 이유

1) 분산 시스템
  - 파티션을 통한 분산처리로 짧은 시간 내에 엄청난 양의 데이터를 컨슈머로 전송 가능 (High throughput message capacity)
  - 높은 성능
  - 서버에 장애 대응에 탁월 (Fault tolerant)
  - 시스템 확장 용이 (Scalability), 이미 사용하고 있는 브로커가 있다고 해도 신규 추가 용이
2) 페이지 캐시
  - 높은 처리량 가능 (High throughput message capacity)
  - OS의 페이지 캐시를 활용하는 방식 > 디스크 I/O에 대한 접근 ↓ > 고성능
3) 배치 전송 처리
  - 프로듀서/컨슈머와 통신할 때 배치 전송을 이용하여 네트워크 오버헤드 줄임
  - 빠르고 효율적으로 처리
4) 압축 전송
  - 메시지 전송시 압축 전송 사용하면 네트워크 대역폭이나 회선 비용 등 절감 가능
5) 토픽, 파티션, 오프셋
  - 토픽: 카프카가 데이터를 저장하는 곳
    파티션: 병렬 처리를 위해 토픽에서 나눈 단위
    오프셋: 파티션의 메시지가 저장되는 위치, 순차적으로 증가하는 숫자 형태(일종의 인덱스)
  - 오프셋을 통해 메시지의 순서를 보장하는 등 효율적인 처리 가능
6) 고가용성 보장
  - 리플리케이션을 통한 고가용성 보장
7) 주키퍼의 의존성
  - 브로커의 노드/토픽/컨트롤러 관리
  - cf. 추후 카프카에서 주키퍼 의존성은 제거될 전망
8) Undeleted log
  - 다른 플랫폼과 달리 컨슈머가 데이터를 가져가더라도 데이터가 사라지지 않음.
    따라서, 컨슈머의 그룹 아이디만 다르다면 동일한 데이터도 다르게 처리할 수 있음.

 


 

브로커, 프로듀서, 컨슈머

브로커(broker) 카프카 애플리케이션이 설치된 서버나 노드를 의미한다.

프로듀서(producer) 카프카로 메시지를 보내는 역할을 하는 클라이언트를 총칭한다.

컨슈머(consumer) 카프카에서 메시지를 꺼내가는(읽어오는, 컨슘하는) 역할을 하는 클라이언트를 총칭한다.

 

 

(과정)
1) 프로듀서가 카프카(브로커)의 토픽으로 레코드를 전송
  - 레코드에 특정 토픽과 밸류(메시지 내용)를 입력해야함.
2) 카프카에서 각 메시지를 저장
3) 컨슈머가 토픽에 저장되어 있는 메시지를 가져와서 읽음.
  - 단순히 메시지를 읽는 것뿐만 아니라
    컨슈머 그룹이나 리밸런싱 등 빠르게 메시지를 읽기 위한 동작을 수행.

 

 

 

(토픽, 파티션, 세그먼트와 관련된 내용은 아래 링크에 자세히 나와있다.)

 

[Kafka] 카프카 기본 개념_2 (토픽, 파티션, 세그먼트, 리플리케이션, 리더-팔로워)

[Kafka] 카프카 기본 개념 및 간단한 설명 토픽 ⊃ 파티션 ⊃ 세그먼트 1. 토픽(topic) 카프카는 메시지 피드들을 토픽으로 구분한다. 각 토픽의 이름은 카프카 내에서 고유하다. 2. 파티션(partiti

unit-15.tistory.com

 

 

 

프로듀서와 컨슈머를 지원하는 언어로는 크게 자바(Java), 파이썬(Python), 고(Go)가 있다.

(Java에서 사용하려면 org.apache.kafka.clients.producer(또는 consumer) 라이브러리를 import하면 된다.)

 

 

 

1. 브로커

: 일종의 메인 허브 역할

: 카프카가 설치되어 있는 서버 단위를 말함

: 보통 3개 이상의 브로커를 설치해서 사용함

: kafka cluster(카프카 클러스터)란? 보통 3개 이상의 카프카 브로커로 이루어진 클러스터를 의미함


2. 프로듀서

: 프로듀서가 전송하는 메시지는 send() 메소드를 통해 '시리얼라이저→파티셔너→카프카'로 전송된다.

: 배치(batch) 전송을 통한 빠른 처리 가능

: 토픽에 해당하는 메시지를 생성 & 특정 토픽으로 데이터를 publish & 처리 실패시 재시도

 

 

파티셔너 (partitioner)

- 프로듀서가 토픽으로 메시지(=데이터)를 보낼 때 토픽의 어느 파티션으로 보낼지 결정하는 역할

- 프로듀서가 데이터를 보내면 무조건 파티셔너를 통해서 브로커로 데이터가 전송된다.
- 동작 알고리즘: 메시지의 키(key)를 해시(hash) 처리하여 파티션을 구한다. 키값은 토픽의 파티션이 지정될 때 쓰인다. 메시지의 키값이 동일하면 같은 파티션으로 전송되고, 키값을 null로 지정하면 round-robin 방식으로 전송된다. 이때, 새로운 파티션을 추가해서 key와 파티션의 개수 매칭이 깨지게 되면 key와 파티션의 연결이 보장되지 않는다. 따라서 key를 사용할 경우 이를 유의하여 파티션 개수를 만들어야하며 추후에 추가하지 않아야 한다.

  • 스티키 파티셔닝 전략 (sticky partitioning) [default]
    : 하나의 파티션에 레코드 수를 먼저 채워서 카프카로 빠르게 전송하는 알고리즘
    : 라운드 로빈 전략의 배치 전송시 대기 시간이 생기는 문제점을 보완
  • 라운드 로빈 전략 (round-robin)
    : 키값을 지정하지 않은 경우(null)의 기본 알고리즘
    : 배치 전송을 위해 최소 레코드 수를 채워야 하는데, 이 과정에서 대기하는 시간이 생길 수 있음

 

레코드에 포함된 메시지 키 또는 메시지 값에 따라서 파티션의 위치가 결정된다. 프로듀서를 사용할 때, 파티셔너를 따로 설정하지 않으면 UniformStickyPartitioner로 설정이 된다. 이 파티셔너는 메시지 키가 있는 경우와 없는 경우에 따라 다르게 동작한다. 

  • 메시지 키가 있는 경우
    - 메시지 키를 가진 레코드는 파티셔너에 의해 특정한 해쉬값이 생성된다. 이 해쉬값을 기준으로 어느 파티션에 들어갈지 정해진다.
    - ex. 토픽에 파티션이 2개 있는 경우: 파티셔너의 해쉬 로직에 의해서, 동일한 메시지 키를 가진 레코드는 동일한 해쉬값을 만들어내므로 항상 동일한 파티션에 들어간다. 그러므로 순서를 지켜서 데이터를 처리할 수 있다는 장점이 있다.
    - ex. 메시지 키에 '서울'이라는 String 값을 입력하면 같은 파티션에 순서대로 데이터가 쌓인다. 한 개의 파티션 내부에서는 데이터가 큐처럼 쌓이기 때문에 순서를 지킬 수 있게 되는 것이다.
  • 메시지 키가 없는 경우
    - 라운드 로빈 방식으로 파티션에 들어가지만, 전통적인 라운드 로빈 방식과는 조금 다르게 동작한다. UniformStickyPartitioner는 프로듀서에서 배치 전송을 하기 때문에 모을 수 있는 최대한의 레코드들을 모아서 파티션으로 데이터를 보낸다. 이렇게 배치 방식으로 데이터를 넣으면서 메시지 키가 없는 레코드들은 파티션에 적절히 분배된다.

 

기본으로 제공되는 파티셔너뿐만 아니라, 직접 개발한 파티셔너도 사용할 수 있다.

카프카에서는 커스텀 파티셔너를 만들 수 있도록 Partitioner 인터페이스를 제공하고 있다. 메시지 키 or 메시지 값 or 토픽 이름에 따라 어느 파티션에 데이터를 보낼 것인지 고를 수 있다.

(ex) VIP 고객을 위해서 데이터 처리를 조금 더 빠르게 처리하는 로직이 필요한 경우를 생각해보자. 10개 파티션이 있다고 가정하면, 8개에는 VIP고객 데이터를 넣어놓고 나머지 2개에는 일반 고객 데이터를 넣어놓으면 된다. 이는 AMQP기반 메시징 시스템에서 우선순위 큐를 만드는 것과 비슷하다.

 

 

정확히 한 번 전송 (Exactly-Once Delivery)

- 트랜잭션 코디네이터와 내부적으로 에포크, 오프셋 등을 활용하여 구현 가능하다.
- 트랜잭션 코디네이터 (transaction coordinator): PID(Producer ID)와 transactional.id를 매핑하고 해당 트랜잭션 전체를 관리한다. 컨슈머 코디네이터와 유사하며 브로커에 위치한다. 프로듀서의 정확히 한 번 전송이 성공하면 해당 레코드에 트랜잭션 성공을 나타내는 메시지를 추가하고, 컨슈머에서는 이 메시지를 포함하는 레코드만 읽음으로써 정확히 한 번 전송을 구현할 수 있다.


3. 컨슈머

: 토픽 내부의 파티션에서 메시지를 가져오는(=polling) 역할을 한다. 가져온 메시지를 특정 DB에 저장하거나 또다른 파이프라인에 전달할 수 있다. poll() 메서드를 통해 데이터(메시지)를 가져온 후, 하둡 또는 엘라스틱서치 같은 저장소에 저장한다.

: 컨슈머는 파티션의 오프셋 관리(오프셋 위치 commit)를 통해 카프카에 저장된 메시지를 효율적으로 가져오도록 한다. 어떠한 이유로 컨슈머가 다시 시작되는 경우, 기존 컨슈머의 마지막 메시지 위치부터 메시지를 가져와야지 효율적으로 동작할 수 있는데, 이는 오프셋을 이용하여 가능하게 된다. 오프셋은 컨슈머가 데이터를 어디까지 읽었는지 확인하는 용도로 사용된다. 또한, 컨슈머는 Push가 아닌 Pull 모델을 기반으로 하기 때문에 성능을 높인다. (cf. 오프셋(offset): 파티션에 있는 데이터의 번호)

: 다른 메시징 시스템의 경우 컨슈머가 데이터를 가져가면 큐 내부의 데이터가 사라지지만, 카프카에서는 컨슈머가 데이터를 가져가더라도 데이터가 사라지지 않는다.

: 컨슈머가 여러 개인 경우 컨슈머 그룹을 통해 병렬 처리를 도와준다.

: 여러 파티션을 가진 토픽에 대해서 컨슈머를 병렬 처리하고 싶다면 컨슈머를 파티션 보다 적은 개수로 생성해야 한다.

case  
파티션 2개 & 컨슈머 1개인 경우 컨슈머는 2개의 파티션에서 데이터를 가져간다.
파티션 2개 & 컨슈머 2개인 경우 컨슈머가 각각의 파티션을 할당받아서 데이터를 가져간다.
파티션 2개 & 컨슈머 3개인 경우 컨슈머 2개만 각각의 파티션을 할당받아서 데이터를 가져오고, 나머지 컨슈머는 동작하지 않는다.

 

 

컨슈머 그룹 (consumer group)

- 컨슈머 그룹 ⊃ 컨슈머

- 컨슈머 그룹은 각 파티션의 리더에게 카프카 토픽에 저장된 메시지를 가져오기 위한 요청을 보낸다.
- 컨슈머 그룹에 속한 컨슈머는 토픽의 파티션과 일대일 매핑되는 게 일반적이다. (개수도 동일)
- 컨슈머 리밸런싱(consumer rebalancing): 컨슈머 그룹에서 각 컨슈머들에게 작업을 균등하게 분배하는 동작

- 서로 다른 컨슈머 그룹에 속한 컨슈머들은 서로 영향을 미치지 않는다. 이는 컨슈머 오프셋에 관한 정보를 저장해두는 __consumer_offset 토픽이 컨슈머 그룹별/토픽별로 오프셋을 나누어 저장하기 때문이다. 따라서 서로 다른 여러 개의 컨슈머 그룹이 동일한 토픽의 데이터를 가져갈 수 있게 된다

- 컨슈머 그룹으로 묶인 컨슈머가 1개 이상의 토픽에서 데이터를 가져가서 처리한다. 컨슈머가 특정 파티션의 데이터도 가져갈 수 있다.

- 그룹 아이디(group id)라고도 부른다.

 

그룹 코디네이터 (group coordinator)

- 컨슈머 그룹이 구독한 토픽의 파티션들과 그룹의 멤버들을 트래킹(tracking)한다. 따라서 파티션이나 그룹 멤버에 변화가 생기면 컨슈머 리밸런싱 동작이 일어나면서 작업을 균등하게 재분배해준다.
- 각 컨슈머 그룹별로 존재하며, 브로커에 위치한다.

- 프로듀서의 트랜잭션 코디네이터와 유사하다.

 

파티션 할당 알고리즘 (프로듀서의 파티셔너가 하는 역할과 대응)

  • 레인지 파티션 할당 (기본값)
    - 토픽별로 할당 전략을 사용한다. 동일한 키를 이용하는 2개 이상의 토픽을 컨슘할 때 유용
  • 라운드 로빈 파티션 할당
    - 사용 가능한 파티션과 컨슈머들을 라운드 로빈으로 할당한다. 균등 분배 가능
  • 스티키 파티션 할당
    - 컨슈머가 컨슘하고 있는 파티션을 계속 유지할 수 있다.
  • 협력적 스티키 파티션 할당
    - 스티키 방식과 유사하지만, 전체 일시 정지가 아닌 연속적인 재조정 방식

 

정확히 한 번 전송 (Exactly-Once Delivery)

- 프로듀서의 정확히 한 번 전송이 성공하면 트랜잭션 코디네이터는 해당 레코드에 트랜잭션 성공을 나타내는 메시지를 추가한다. 이후 컨슈머에서는 이 메시지를 포함하는 레코드만 읽음으로써 정확히 한 번 전송을 구현할 수 있다.

 


 

메시지 또는 레코드

메시지(message) 또는 레코드(record) 프로듀서가 브로커로 전송하거나 컨슈머가 읽어가는 데이터 조각을 의미한다.

 

구성 요소

1) 토픽
  - 메시지를 전송할 카프카의 특정 토픽
2) 파티션 (선택값)
  - 토픽 내의 특정 파티션
3) 키 (선택값)
  - 레코드 정렬을 위한 값
4) 밸류
  - 메시지 내용

 

cf. 오프셋(offset): 카프카에서 메시지의 위치를 나타내며, 순차적으로 증가하는 숫자 형태로 일종의 인덱스와 같다.

 


주키퍼 (ZooKeeper)

아파치 프로젝트 애플리케이션 이름으로써, 카프카의 메타데이터 관리 및 브로커의 정상상태 점검(health check) 담당
주키퍼는 하둡의 서브 프로젝트로 시작했으며, 현재는 카프카를 비롯해 하둡(Hadoop), 나이파이(NiFi), 에이치베이스(HBase) 등 분산 애플리케이션의 코디네이터 역할을 하는 애플리케이션이다.

 

 

 

 

(출처: 고승범 『실전 카프카 개발부터 운영까지』, 유튜브 데브원영 DVWY)

반응형