imhamburger 님의 블로그

데이터엔지니어 부트캠프 - 카프카(Kafka) 이해하기 (7주차) 본문

데이터엔지니어 부트캠프

데이터엔지니어 부트캠프 - 카프카(Kafka) 이해하기 (7주차)

imhamburger 2024. 8. 25. 16:41

아파치 카프카(Apache Kafka)는 데이터를 빠르고 안정적으로 주고받을 수 있게 해주는 시스템이라고 생각하면 된다.

Kafka는 대용량의 실시간 데이터 스트리밍을 처리하고, 데이터를 저장하며, 스트리밍 데이터를 다양한 시스템으로 전달하는 데 사용된다.

 

 

사실 카프카가 등장하기 전, 데이터를 주고받고 처리하는 방식은 원래부터 있었다.

 

 

예를들어, 이메일서비스를 생각해보았을 때, 

우리는 단순히 이메일을 쓰고 상대방에게 보내면 끝이지만, 사실 이메일 프로세스 안에서는 다양한 데이터를 보내고 받는다.

 

이메일을 보내는 서비스인 Send는 이메일을 보낼 때, Send는 이메일 피드를 갱신하는 서비스(Feed), 이메일 알람을 보내는 서비스(Notify), 이메일 상태를 담당하는 서비스(Status), 로그를 담당하는 서비스(Log) 등에 모두 데이터를 전달해야 한다.

 

따라서, 이들 모두 송신과 수신이 모두 가능한 상태여야 하고 서비스마다 필요로 하는 데이터가 다르다면 그에 맞춰 구현해줘야 하는 작업이 필요하다.

 

게다가....

 

위와 같이 받는 쪽이 더 늘어난다면 보내는 Send 서비스는 그에 맞춰 대응해줘야 한다.

 

반대로 보내는 쪽이 늘어날 수도 있다.

그럼 이메일 서비스라는 하나의 큰 서비스 안에 아키텍쳐 관리는 더욱 복잡해질 것이고 어딘가 문제가 생겨났을 때 어디서 문제가 되는건지 찾는 과정도 어려워질 수 있다.

 

 

따라서 이러한 단점을 해결하기위해 Kafka가 등장했다.

 

 

카프카의 구성요소를 크게 보자면, 아래와 같다.

  • 프로듀서(Producer): 데이터를 생성하는 시스템이 Kafka Producer를 통해 데이터를 Kafka로 전송
  • 브로커(Broker): 브로커는 Kafka 서버 인스턴스로 Kafka 클러스터는 여러 브로커로 구성되며, 각 브로커는 토픽과 파티션의 데이터를 저장하고 관리
  • 컨슈머(Consumer): 는 브로커에서 토픽별로 데이터를 읽어들이는 클라이언트. 컨슈머는 데이터를 실시간으로 처리하거나 저장

 

 

위처럼 카프카를 사용할 경우,

보내는 서비스인 Send 즉 프로듀서는 브로커에만 데이터를 전달하면 되니 받는 서비스쪽에서 잘 받았는지 확인할 필요도 없고 자기 일에만 집중할 수 있다.

 

반대로 컨슈머는 가능할 때마다 브로커에서 데이터를 가져오기만 하면 된다.

 

게다가 브로커는 프로듀서로부터 전달받은 데이터를 지정된 시점까지 보관하기 때문에 컨슈머가 바로 찾아가지 않더라도 데이터가 유실될 걱정을 할 필요가 없다.

 

게다가 그림처럼 프로듀서와 컨슈머가 아무리 늘어나더라도 데이터의 전달이 Broker를 통해 이뤄지기 때문에 아키텍쳐가 단순해지고 관리도 훨씬 수월해진다.

 

 

그럼 Broker는 어떻게 각 프로듀서마다 받은 데이터를 어떻게 정리해서 컨슈머에 보내는 걸까?

 

그게 가능한 이유는 카프카에서 데이터는 토픽(Topic) 단위로 관리된다.

예를들어 Send 서비스가 "알림" 이라는 이름의 토픽을 설정하고 "Notify"서비스에 같은 이름의 토픽을 설정하였다면, 해당 토픽을 통해 Notify가 데이터를 가져온다. 

 

근데 하나의 토픽에 너무 많은 데이터가 들어오면 정리하기가 어려워지니 Kafka는 토픽을 파티션(Partition)이라는 여러 조각으로 나누고 여러 조각에는 오프셋(Offset)이라 부르는 고유 번호를 부여해준다. 이렇게 나누어서 정리를 해야 처리 속도가 빨라진다.

마치 우리가 방을 정리하였을 때 물건을 바로바로 찾을 수 있는 것처럼 말이다.

 

각 파티션은 여러 개의 복제본(리플리카)을 가지는데, 이 복제본들은 Kafka 클러스터 내의 여러 브로커에 분산되어 저장된다.

카프카는 보내는 서비스와 받는 서비스의 소통을 유연하게 만들고, 데이터 유실을 방지해준다.

 

 

따라서,

카프카는 빠른 응답을 필요로하는 서비스보다 안전한 메세지 전달, 확장성을 필요로 하는 서비스들에 쓰이는게 적합하다.

이를테면, 이메일, 온라인 결제, 로그를 통해 실시간 맞춤형 콘텐츠를 제공하는 SNS 등에서 유용하게 활용된다. 

 

 

 

카프카 명령어 (참고 블로그

 

주키퍼(Zookeeper) 실행

$KAFKA_HOME/bin/zookeeper-server-start.sh config/zookeeper.properties

 

 

카프카(Kafka) 실행

$KAFKA_HOME/bin/kafka-server-start.sh config/server.properties

 

 

프로듀서 실행

$KAFKA_HOME/bin/kafka-console-producer.sh --topic {토픽이름} --bootstrap-server localhost:9092

 

 

 

컨슈머 실행

$KAFKA_HOME/bin/kafka-console-consumer.sh --topic {토픽이름} --from-beginning --bootstrap-server localhost:9092

 

 

카프카를 실행하는 데 왜 주키퍼를 실행해야 할까?

 

Kafka는 분산 시스템이기 때문에 여러 개의 브로커가 함께 동작한다. 이 브로커들 간의 상태를 관리한다.

브로커가 다운되거나 네트워크 문제로 연결이 끊기면, Zookeeper는 이를 감지하고 해당 브로커에서 관리하던 파티션을 다른 브로커로 재할당?하는 등의 작업을 수행한다.

 

또한, 브로커가 추가되거나 제거될 때, 혹은 새로운 토픽이 생성될 때, Zookeeper는 이를 감지하고 클러스터의 구성 정보를 업데이트한다. 이렇게 함으로써 모든 브로커가 클러스터의 최신 상태를 알 수 있게 된다.

Zookeeper는 Kafka의 조정자 역할을 하며, Kafka 클러스터의 상태를 관리하고, 브로커들 간의 조율을 돕는 중요한 역할을 한다.

 

그러나 최신 카프카 버전에서는 주키퍼의 의존성을 줄이기 위해 여러 변화를 주고 있다고 한다.

 

 

사실 나는 카프카를 이해하기 전까지 스파크와 혼동이 있었다...

 

두 시스템은 상호 보완적으로 사용될 수 있는데, 예를 들어, Kafka를 사용하여 실시간으로 데이터를 수집하고, Spark를 사용하여 수집된 데이터를 실시간으로 분석하거나 배치 처리할 수 있다. 이는 대규모 데이터 시스템에서 실시간 분석 파이프라인을 구축하는 데 유용하다.

 

 

kafka-python

 

카프카가 지원하는 프로그래밍언어는 Java, Scala, Python, Go, C#, Ruby, Node.js, Rust 등 다양한 언어를 지원한다.

나는 파이썬을 쓰기때문에 kafka-python을 이용한다.

kafka-python은 Python 애플리케이션에서 Kafka와 상호작용해야 할 때 사용된다. (kafka-python 설치)

 

가상환경에서 기능을 구현할 시..

파이썬 실행파일 경로에 __init__.py가 있는지 확인하자.!!

 

kafka-python에서 쓰이는 함수명들은 공식문서에서 확인하고 적절히 사용하면 된다. (공식문서 바로가기)

 

프로듀서에서 쓰이는 producer.flush() 에 대해 잠깐 설명하자면...

producer.flush()는 KafkaProducer가 보낸 모든 메시지가 카프카 브로커에 안전하게 전송될 때까지 대기하는 역할을 한다.

프로듀서가 내부 버퍼에 있는 모든 메시지를 브로커로 전송하고, 브로커로부터 해당 메시지들이 성공적으로 수신되었는지 확인할 때까지 대기한다.

 

producer.send()를 통해 메시지를 보냈을 때, 메시지는 즉시 브로커로 전송되지 않을 수 있으며, 내부 버퍼에 저장된다.

이는 효율적인 네트워크 사용을 위해 배치 처리되거나 네트워크 부하를 줄이기 위해 잠시 대기를 거는 것이다.


중요성
데이터 일관성 보장: flush()는 모든 메시지가 성공적으로 전송되었는지 확인할 수 있는 방법을 제공. 이는 메시지의 일관성을 보장하는 데 매우 중요하다.
프로그램 종료 전 보장: 예를 들어, 프로그램이 종료되기 전에 flush()를 호출하여 아직 전송되지 않은 메시지들이 안전하게 전송되었는지 확인할 수 있다.

 

producer.flush()는 테스트 목적이나 작은 메시지 전송 시 유용할 수 있지만, 대량의 메시지를 보내는 경우에는 성능에 영향을 줄 수 있어서, 필요에 따라 적절하게 사용해야 한다.

 

 

Apache Kafka와 비슷한 기능을 제공하는 다른 메시징 및 스트리밍 플랫폼이 몇 개 더 있는데 사용용도에 따라 장단점이 다 있다.

RabbitMQ, Amazon Kinesis만 비교해보자.

 

특징 Apache Kafka RabbitMQ Amazon Kinesis
주요 사용 실시간 데이터 스트리밍,
로그 수집
메시지 큐잉, 작업 분산 실시간 데이터 스트리밍, 분석
데이터 모델 로그 기반, 스트림 중심 큐 기반, 메시지 중심 스트림 중심
확장성 높은 확장성, 수평적 확장 지원 제한적 확장성 매우 높은 확장성, 수평적 확장 지원
성능 매우 높은 처리량 적당한 처리량 매우 높은 처리량
지속성  디스크에 지속적으로 저장 큐기반이기 때문에 브로커가 컨슈머에 데이터를 전달하면 브로커에서 해당 데이터는 제거됨 클라우드 스토리지에 저장
클러스터 관리 ZooKeeper 필요
(KRaft 모드 도입 중)
쉬운 설치 및 관리 AWS 관리형 서비스로 자동 관리
운영 복잡성 높음 낮음 낮음 (AWS에서 관리)
장점 높은 처리량, 확장성, 내구성 쉬운 사용성, 다양한 기능 클라우드에서 완전 관리형 서비스
단점 운영 복잡성, ZooKeeper 의존 확장성 제한, 성능 저하 가능성 AWS 종속성, 비용 문제

 

 

 

일주일을 보내면서...

카프카를 배웠는데 코드는 그렇다치고 개념이 이해가 가지않아 뭐가 어떻게 작동되는건지 헤맸다.

이런 경험을 통해 느낀 것은, 개념을 확실히 이해하는 것이 얼마나 중요한지 다시금 깨닫게 되었다는 것이다. 코드 자체는 손에 익히는 과정이 시간이 지남에 따라 자연스럽게 해결될 수 있지만, 그 코드가 어떤 원리로 돌아가는지 이해하지 못하면 진정한 의미에서의 문제 해결 능력을 키우기 어렵다는 생각이 들었다. 

따라서 앞으로는 코드 작성뿐만 아니라 개념에 대한 깊은 이해를 목표로 해야겠다.

 

 

앞으로 나의 방향

두번째 팀프로젝트가 시작되었다. 금요일에는 각자 조에서 회의하고 일정을 잡는 시간을 가졌다. 이번엔 3명이 아닌 4명에서 한 조를 이루어 기능 2개를 만들어야 하는데 둘둘씩 나누어 작업하기로 하여 효율적으로 작업할 수 있을 것 같다.

 

그리고 이번 기회를 통해 협업과 소통의 중요성을 다시 한번 체감할 수 있을 것 같다. 지난 프로젝트에서 얻은 교훈을 바탕으로 이번에는 원활하게 프로젝트를 진행해 나가고 싶다. 다음 주에는 팀프로젝트에 집중하여 최선을 다해 보자!