본문 바로가기
데이터베이스

[데이터베이스] 아파치 카프카 (Apache Kafka)란?

by xangmin 2022. 4. 20.
반응형

데이터 파이프라인(Data Pipeline)을 구축할 때 가장 많이 고려되는 시스템 중 하나가 '카프카(Kafka)' 일 것이다. 아파치 카프카(Apache Kafka)는 링크드인(LinkedIn)에서 처음 개발된 분산 메시징 시스템이다. 

 

Publish/Subscribe (펍/섭) 시스템

카프카는 기본적으로 Publish-Subscribe 모델을 구현한 분산 메시징 시스템이다. Publish-Subscribe 모델은 데이터를 만들어내는 프로듀서(Producer, 생산자), 소비하는 컨슈머(Consumer, 소비자) 그리고 이 둘 사이에서 중재자 역할을 하는 브로커(Broker)로 구성된 느슨한 결합(Loosely Coupled)의 시스템이다.

 

프로듀서는 브로커를 통해 메시지를 발행(Publish) 한다. 이 때 메시지를 전달할 대상을 명시하지는 않으며 관련 메시지를 구독(Subscribe) 할 컨슈머가 브로커에게 요청하여 가져가는 식이다. 마치 블로그 글을 작성하여 발행하면 블로그 글을 구독하는 독자들이 따로 읽어가는 형태를 생각하면 된다. (반대되는 개념으로는 글을 작성한 프로듀서가 구독하려는 컨슈머에게 직접 메일을 보내는 걸 생각하면 된다.)

 

 

 카프카 역시 카프카 클러스터로 메시지를 전송할 수 있는 프로듀서와 카프카로부터 메시지를 읽어 갈 수 있는 컨슈머 클라이언트 API를 제공한다. 그 밖에 데이터 통합을 위한 커넥터(Connector)와 스트림 처리를 위한 스트림즈(Streams) API도 있지만 이 역시 크게 보면 프로듀서와 컨슈머의 확장이라고 볼 수 있다.

 

카프카에서 프로듀서는 특정 토픽(Topic)으로 메시지를 발행할 수 있다. 컨슈머 역시 특정 토픽의 메시지를 읽어 갈 수 있다. 카프카에서 토픽은 프로듀서와 컨슈머가 만나는 지점이라고 생각할 수 있다. (블로그의 RSS 피드 정도로 생각하면 되겠다. 티스토리는 카프카에 해당하며 티스토리에 존재하는 블로그 하나하나는 각각 하나의 토픽으로 대응될 수 있다. 프로듀서에 해당하는 블로거는 특정 블로그에 글을 작성하여 발행할 수 있고, 컨슈머에 해당하는 독자는 특정 블로그의 RSS를 구독하는 것을 생각하면 된다.)

 

카프카는 수평적인 확장(scale horizontally, scale out)을 위해 클러스터를 구성한다. 카프카를 통해 유통되는 메시지가 늘어나면 카프카 브로커의 부담(Load)이 증가하게 되어 클러스터의 규모를 확장할 필요가 있다. 카프카는 여러 브로커들의 클러스터링을 위해 아파치 주키퍼(Apache ZooKeeper)를 사용한다. 주키퍼를 사용하여 브로커의 추가 및 장애 상황을 간단하게 대응할 수 있다.

 

카프카 클러스터 위에서 프로듀서가 전송한 메시지는 중복 저장(Replication 되어 장애상황에서도 고가용성(High Availability)을 보장하게 된다. 프로듀서가 메시지를 카프카 클러스터로 전송하면 브로커는 또 다른 브로커에게 프로듀서의 메시지를 중복해서 저장한다. 만약 한 브로커에 장애가 생기더라도 중복 저장된 복사본을 컨슈머에게 전달 할 수 있으므로 장애 상황에 대비(Fault-tolerant) 할 수 있다.

 

카프카의 특징

링크드인에서 카프카를 개발 할 당시에도 다양한 메시징 시스템이 존재했었다. 하지만 링크드인에서 처음 개발 될 때 기존 메시징 시스템과 비교하여 장점으로 내세울 수 있는 몇 가지 특징을 가지도록 설계되었다.

 

1) 다중 프로듀서, 다중 컨슈머

카프카의 토픽에 여러 프로듀서가 동시에 메시지를 전송할 수 있다. 마찬가지로 카프카 토픽의 메시지를 여러 컨슈머들이 동시에 읽어 갈 수 있다. 뿐만 아니라 하나의 프로듀서가 여러 토픽에 메시지를 전송할 수도 있으며, 하나의 컨슈머가 여러 토픽에서 메시지를 읽어 갈 수도 있다. 특히 하나의 메시지를 여러 컨슈머가 읽어 갈 수 있는 측면이 카프카의 큰 강점으로 작용한다.

 

이러한 다중 프로듀서, 다중 컨슈머의 지원을 통해 하나의 카프카 시스템을 통해 다양한 애플리케이션이 데이터를 주고 받을 수 있게 되었으며, 데이터의 생산자/소비자 관계도 유연하게 구성할 수 있게 되었다.

 

2) 파일시스템에 저장

전통적인 메시징 시스템은 프로듀서가 전송한 메시지를 브로커의 메모리 상에 존재하는 큐(Queue)에 유지한다. 이후 컨슈머가 메시지를 읽어가면 큐에서 메시지를 제거한다. 프로듀서가 생성한 메시지는 브로커를 통해 컨슈머에 의해 빠른 시간내에 소비될 것이라고 가정하고 만들어졌기 때문이다. 

 

카프카는 프로듀서가 생성한 메시지를 브로커가 위치한 서버의 파일 시스템에 저장한다. 따라서 컨슈머는 프로듀서가 생성한 메시지를 바로바로 소비하지 않아도 되며 카프카가 메시지를 보존하고 있는 기간내에서는 언제든지 읽어 갈 수 있다.

 

이런 방식은 프로듀서와 컨슈머의 속도 차이가 있을 때 유용하다. 예를 들어 컨슈머 쪽에 장애가 생겼거나 순간적인 네트워크 트래픽 폭주로 처리가 늦어졌을 때 브로커의 동작에 큰 영향을 주지 않으면서 처리 속도를 따라갈 수 있게 해준다. 또 한 컨슈머들이 데이터를 모았다가 처리하는 배치(batch) 처리를 가능하게 해주며, 컨슈머 쪽에서 에러가 생겼을 때 이전에 읽었던 데이터를 다시 읽을 수 있게 해준다.  

 

카프카 브로커가 파일 시스템에 저장한 메시지는 관리자에 의해 설정된 일정 보존 기간동안 사용가능하며 이후 브로커가 위치한 서버의 파일 시스템에서 삭제된다. 

 

3) 확장성(Scalability)

카프카 클러스터는 운영중에 확장이 용이하도록 설계되었다. 데이터 파이프라인을 구축한 초창기 적은 수의 브로커들로 클러스터를 운영하다가 시스템 트래픽이 높아지면 브로커를 추가해서 클러스터를 확장할 수 있다. 이른바 수평적인 확장 (Horizontally Scale, Scale out) 이 쉽게 이뤄진다. 

 

카프카 토픽에 메시지를 전송하는 프로듀서 역시 운영중에 얼마든지 증가시킬 수 있다. 카프카에서 메시지를 읽어가는 컨슈머의 경우 컨슈머 그룹으로 묶이며 컨슈머 그룹에 컨슈머를 추가할 수 있다. 컨슈머 그룹에 컨슈머가 추가되면 컨슈머의 파티션 소유권(Ownership)이 재분배되는 리밸런스 과정을 거쳐 컨슈머 그룹에 속한 컨슈머들이 고르게 파티션을 할당 받게 된다. 여튼 컨슈머 역시 운영 중에 무난하게 추가될 수 있다.

 

카프카 토픽은 내부에서 파티션(Partition)이라는 세분화된 단위로 나뉘어 저장되는데 토픽의 파티션 개수도 운영중에 추가할 수 있다. 

 

이런 모든 확장 작업이 카프카 운영에 심각한 부담을 줄 정도는 아니며 쉽고 간편하게 이루어 질 수 있도록 설계되었다. 

 

4) 고성능

카프카는 대용량 실시간 로그 처리에 특화되어 있다. 일반적인 범용 메시징 시스템이 지원하는 몇 가지 기능을 포기하면서 높은 처리량(Throughput)을 갖도록 설계되었다. 예를 들어 IBM Websphere MQ 같은 경우 복수의 큐(Queue)에 메시지들을 원자적(Atomically)으로 전달 할 수 있는 트랜잭션(Transaction) 기능을 제공한다. JMS의 경우 컨슈머가 메시지를 소비했는지 여부를 알 수 있는 기능도 제공한다. 

 

하지만 범용 메시징 시스템이 제공하는 이런 기능은 링크드인에서 크게 중요한 기능으로 생각되지는 않았다. 오히려 이런 기능을 제공하기 위해 메시징 시스템 내부에서 복잡한 처리와 병목현상 등이 발생하여 성능을 최대로 끌어낼 수 없었다. 

 

카프카는 이런 기능을 배제하고 뛰어난 처리량(Throughput)을 갖도록 설계되었다. 불필요한 기능을 제외하고 내부적으로 배치처리, 분산 처리와 같은 다양한 기법을 사용해 성능을 처리량을 최대로 끌어냈다. (덕분에 튜닝해야 할 파라미터가 많아지긴 했다)

 

카프카를 소개한 netDB 2011에 게재된 "Kafka: a Distributed Messaging System for Log Processing" 논문에서 카프카의 처리량을 다른 메시지 시스템과 비교했다. 비교 대상은 Apache ActiveMQ v5.4, RabbitMQ v2.4이며 카프카의 배치 사이즈를 1과 50으로 설정한 내용도 함께 비교했다. (RabbitMQ와 ActiveMQ의 경우 별도의 배치 처리를 지원하지 않는 듯 해서 batch 1로 설정된 것으로 간주하고 실험을 했다고 한다)

 

실험에는 2대의 리눅스 머신이 사용되었다. 리눅스 머신의 사양은 8개의 2GHz 코어와 16GB 메인 메모리, RAID 10으로 구성된 6장의 디스크로 구성되었다. 둘 중 하나는 브로커로 사용되었으며 나머지 하나는 컨슈머와 프로듀서로 사용되었다.

 

5) 컨슈머의 pull 방식

기존의 메시징 시스템의 경우 브로커가 컨슈머에게 데이터를 전달해주는 "Push 방식"을 채택한 경우가 많이 있다. 카프카는 컨슈머가 브로커에게서 메시지를 가져오는 "Pull 방식"을 채택했다. 

 

Pull 방식을 사용하면 컨슈머의 처리량을 브로커가 고민할 필요가 없다. 컨슈머는 자신이 처리할 수 있는 만큼의 메시지만 브로커에게서 가져가면 되기 때문에 최적의 메시지처리 성능을 갖을 수 있다. 만일 컨슈머의 처리 속도가 프로듀서의 생산 속도보다 느리다면 컨슈머를 추가하여 처리량을 늘릴 수 있다. 또 한 메시지를 모았다가 한번에 처리할 수 있는 배치처리도 간단하게 구현할 수 있게 되었다.

 

출처 : https://soft.plusblog.co.kr/3?category=896352

반응형

댓글