[Apache Kafka] 메세징 시스템과 Kafka의 작동 방식

목포·2022년 6월 26일
0

Apache Kafka

목록 보기
1/3

Apache Kafka란?

 아파치 카프카는 빠르고 확장 가능한 작업을 위해 데이터 피드의 분산 스트리밍, 파이프 라이닝 및 재생을 위한 실시간 스트리밍 데이터를 처리하기 위한 목적으로 설계된 오픈 소스 분산형 게시-구독 메시징 플랫폼이다.

메시징 시스템(Messaging System)

 메시징 시스템은 Kafka, RabbitMQ, ActiveMQ, AWS SQS, Java JMS 등이 있다. MSA 같은 시스템 같은 경우에 시스템 간 호출이 많아 서비스 결합도를 낮추기 위해 비동기 요청, 성능, 안정성 등을 위해 메시징 시스템을 사용하곤 한다.

 그렇다면 메시징 시스템이란 도대체 뭘까? 보통은 로그 데이터, 이벤트 메시지 등을 API로 호출할 때 보내는 데이터들을 처리하는 시스템이라고 생각하면 된다.

자동 메일 발송 시스템

  • MemberService : 회원 가입 시 이메일을 발송
  • OrderService : 주문 완료 시 이메일을 발송
  • MailServcie : 실제 메일을 발송하는 서비
  1. MemberService에서 회원가입 후, OrderService에서 주문완료 이벤트가 발생하면
  2. Messaging Client로 메일 전송에 필요한 데이터를 API로 호출
  3. Messaging Client에서 MOM을 구현한 소프트웨어(Kafka)로 메세지 생산
  4. MailService에서 메세지가 존재하는지 구독하고있다가 메세지가 존재하면 메세지 소비
  5. MailService에서 API 정보들을 통해 User에게 메일 발송

이런 메세징 시스템들은 ‘Publish/Subscribe’ 형태의 통신을 하며 메세지를 보내고(Publish) 받는(Subscribe)다. 대부분의 발행/구독 시스템은 메세지 큐프로세스 간 통신 채널을 갖는 형태로 만들어진다.

왜 메세징 시스템을 써야할까?

그렇다면 왜 메세징 시스템이 필요할까?
만약 아래 그림같이 프론트엔드 서버와 백엔드 서버로부터 전체 리소스 사용 데이터를 수집하는 메트릭 서버가 있다고 해보자. 서비스 배포 초기에는 위와 같이 간단하게 구성할 수 있을 것이다.

하지만, 우리는 운영하면서 데이터베이스 서버, 채팅 서버, 인증 서버등의 추가와 분리의 필요성을 느끼게 되었고 이를 아래와 같이 재구성하였다.

 서비스 인스턴스가 다음과 같이 늘어나고 추가적인 로그 수집 서버나 모니터링 서버가 추가되면 복잡해지게 된다. 이렇게 된 이유는 무엇일까?
각 인스턴스 서버(프론트엔드, 백엔드, 채팅, 인증 서버 등등, 이하 통칭 publisher)와 각 메트릭 서버(이하 통칭 consumer)들이 직접 연결된 구조이기 때문이다. 1:1의 관계기 때문에 복잡도가 증가하게 되는 것이다. 이는 한 곳에서 수정이 일어나면 다른 곳에 영향을 줄 수 있다는 의미가 된다.

 결국은 의존성 문젠데 이는 다른 아키텍처와 마찬가지고 결합도를 줄이는 느슨한 구조로 변경해야 한다.
이를 메세징 시스템을 통해 아래와 바꿔보자.

만약 메트릭 발행/구독 서버(Kafka와 같은)를 추가하게 되면

  1. publisher는 consumer의 정보를 알 필요가 없다.(직접 연결 없어짐) 서로의 변경의 영향을 받지 않게된다.
  2. publisher는 단순하게 발행/구독 시스템에 전송하면 consumer는 이 메세지를 읽어가기만 하면 된다.
  3. 서버가 추가되어도 서로에게 영향을 끼치지 않기 때문에 새로운 서버를 추가하거나 기존 서버를 변경하는데 수월해지고, 수평적인 확장이 가능해진다.

왜 Kafka인가?

그렇다면 왜 Kafka를 써야 하는가?

Kafka 도입 전


위 그림은 카프카가 개발되기 전 링크드인의 데이터 시스템 구성도이다. 당시 링크드인이 서비스를 유지하려면 다음과 같은 시스템들이 필수적이었다.

  1. 메트릭 모니터링용 데이터 시스템 : 앱이나 서비스에서 일어나는 미터링(사용량, 응답 시간, 에러 카운트 등) 정도를 저장할 시계열(Time Series) 데이터 처리용 시스템
  2. 로그 모니터링용 데이터 시스템 : 앱/서비스에서 발생하는 로그를 저장하고 있고, 이것을 기반으로 실시간/배치로 분석할 수 있도록 데이터를 저장하는 시스템
  3. 서비스에 필요한 컨텐츠와 고객 정보 데이터들을 저장하는 메인 데이터 시스템, 대부분의 앱들에서 보낸 OLTP(OnLine Transaction Process) 쿼리(주로 데이터 갱신)를 실행
  4. 추천이나 장바구니와 같이 트랜잭션 처리까진 필요없지만 실시간으로 처리를 해줘야하는 내용들을 저장하는 키/값 저장소. 앱이나 사용자는 HTTP 프로토콜이나 OLTP 쿼리를 실행한다.
  5. 서비스와 제품군 전체에서 발생하는 데이터를 모아서 일간/주간/월간/연간 데이터를 제공하는 데이터 마켓이나 이것을 활용해 배치 분석을 하는 데이터 웨어하우스, 각종 데이터 시스템에서 이곳으로 데이터를 보낸다.
  6. 빅데이터를 저장/처리하기 위한 하둡 시스템, 빅데이터를 처리해서 데이터 웨어하우스에 보낸다. 이런 작업을 ETL이라고 한다.

📝시나리오로 생각해보자

  • 그림에 표시된 ‘앱/서비스1’개발팀 같은 경우 사용자의 요청 데이터를 메인 DB, 메트릭/로그 데이터를 정해진 데이터 스토어로 전달했다.
  • 시간이 지나면서 자신에게 온 요청을 회사가 만든 다른 서비스(이를테면 SlidShare 같은)와 비동기로 연동하는 기능이 필요해져서 메세지 큐에도 동일한 데이터를 저장하게 된다. 하지만, ActiveMQ와 같은 기존 메세지큐는 성능이 느리고 큰 데이터를 처리할 수 없어, 원본 데이터를 줄이고 포맷도 변경한다.
  • 시간이 지날수록 링크드인의 서비스가 증가하면서 비동기 작업은 많아지지만 메세지 큐를 확장시키는데는 아주 큰 어려움을 겪는다.
  • 얼마의 시간이 지나고나서, 새로운 기능이 개발되면서 키/값 스토리지에도 사용자 요청 데이터를 넣게 된다. 이때 메세지 큐에 넣는 데이터를 그대로 사용하고 싶지만 이미 데이터를 너무 축약한 상태라, 사용자 요청 데이터를 특정 객체로 만들고 직렬화해서 키/값 스토리지에 저장하게된다.
  • 비지니스 분석을 하는데 고객 요청 데이터가 필요하다고해서 기존에 데이터베이스, 메세지 큐, 키/값에 넣었던 것 중 하나의 포맷을 사용하려고 했으나 대부분 데이터 웨어하우스는 다양한 포맷의 데이터를 지원하지 못하므로 데이터베이스 컬럼에 맞춰서 다시 한 번 CSV 포맷으로 변경한다. 그리고 주기적으로 CSV 형태로 만들어 데이터 웨어하우스에 넣는 작업을 하는 앱을 또 개발하고 운영해야한다.
  • 하둡이 도입되어 데이터를 넣어야하는데 다행히도 하둡은 CSV도 저장한다!
    하지만, 하둡은 실시간으로 데이터를 Write할 수 없고 1시간에 1번 씩만 데이터를 올릴 수 있다고 한다. 이번엔 1시간동안 데이터를 모았다가 하둡에 올리는 앱을 따로 만든다. 이 앱이 오작동하거나 장애가 발생하면 데이터 분석 자체가 어려워지므로 장애가 나지 않도록 신경써야한다.
  • 그러다가 Splunk(로그분석SW)용량이 너무 커져서 비용을 줄이기 위해 회사에서 중앙 로깅 시스템을 만들었으니 그 쪽으로 데이터를 옮기라고 한다. 하지만 기존의 스플렁크에 저장한 데이터를 기반으로 쿼리를 하던 로그 모니터링 시스템을 그대로 옮길 수가 없어서 추가로 개발을 해야하는데 그러자니 시간적/인적 자원이 부족하다.
  • 어느덧 연결성 관리를 해야하는 시스템이 7개나 되어버려 유지 관리 부담이 늘어났다.

위 시나리오처럼 성장하는 회사들은 이와 같은 과정을 한 번씩은 거치게 된다. 위에서 말한 것 처럼 End-to-End 방식의 아키텍처는 많은 문제점을 발생시킨다.

Kafka의 특성은

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

 메세징 시스템의 publisher 역할을 하는 것이 카프카의 producer이다.
카프카는 하나의 토픽에 여러 프로듀서 또는 컨슈머들이 접근 가능한 구조로 되어있다. 즉, 하나의 프로듀서가 하나의 토픽에만 메세지를 보내는 것이 아니라 하나 또는 하나 이상의 토픽으로 메세지를 보낼 수 있다.(컨슈머도) 멀티 프로듀서와 멀티 컨슈머를 구성할 수 있기 때문에 카프카는 중앙 집중형 구조를 구성할 수 있다.

 또한, 여러 클라이언트가 많은 토픽(메세지 저장소)를 사용하거나 같은 토픽을 사용해도 카프카는 무리없이 처리할 수 있다. 여러 프로듀서로부터 데이터를 수집하고 일관성을 유지하는데 이상적인 구조를 가지고 있다.

 구독자 역할을 하는 것이 카프카의 consumer이다. 카프카는 상호 간섭없이 어떤 메세지도 읽을 수 있게 지원한다. 다른 메세징 시스템의 경우엔 한 구독자 클라이언트가 특정 메세지를 소비하면 다른 구독자 클라이언트에서는 그 메세지를 사용할 수 없다. 하지만 카프카 컨슈머는 컨슈머 그룹이란 개념을 사용하여 각각의 컨슈머가 메세지를 관리할 수 있다.

디스크에 기반의 보존

 카프카가 기존의 메세징 시스템과 가장 다른 특징 중 하나는 디스크에 메세지를 저장하고 유지할 수 있다는 것이다. 일반적인 메세징 시스템들은 컨슈머가 메세지를 읽어가면 큐에서 바로 메세지를 삭제한다. 하지만, 카프카는 컨슈머가 메세지를 읽어가더라도 정해져 있는 보관 주기 동안 디스크에 메세지를 저장해둔다.

 만약, 트래픽이 일시적으로 폭주해 컨슈머의 처리가 늦어지더라도 카프카의 디스크에 안전하게 보관되어 있기 때문에 컨슈머는 손실 없이 메세지를 가져갈 수 있다. 또한, 컨슈머에 버그가 있어 오류가 발생했다면, 컨슈머를 잠시 중단하고 버그를 찾아 해결한 후 컨슈머를 다시 실행할 수 있다.

확장성

 카프카는 확장이 매우 용이하도록 설계되어있다. 확장 작업은 카프카 서비스의 중단 없이 온라인 상태에서 작업이 가능하다.

분산 처리

 Scale-out이 가능하여 시스템 확장이 용이하며 어떤 하나 혹은 몇 개의 서버가 다운되도 서비스 자체가 중단될 일 없이 시스템 운용이 가능하다. 또한, 카프카는 파티션이라는 개념을 도입하여 여러 개의 파티션을 서버에 분산시켜 처리할 수 있다. (빠른 메세지 처리가 가능)

페이지 캐시

 잔여 메모리를 이용해 디스크 Read/Write를 하지 않고, 페이지 캐시를 통한 Read/Write를 인해 처리 속도가 매우 빠르다.

카프카를 도입한 후의 링크드인


그럼 카프카를 도입하고 난 후의 링크드인은 어떻게 됐을까? 위 그림처럼 사내에서 발생하는 모든 이벤트/데이터 흐름을 중앙에서 관리하여 깔끔하게 변한 것을 알 수 있다.

 기존의 모든 데이터 스토어(1번~6번)가 그대로 있고 여기서 발생하는 데이터/이벤트가 카프카를 중심으로 연결되어있다. 또, 새로운 데이터 스토어(7~9)가 들어와도 서로 카프카가 제공하는 표준포맷으로 연결되어있어 데이터를 주고받는데 부담이 없다.
뿐만 아니라 이전에는 할 수 없었던 다양한 분석(10번~12번)이 가능해져 신뢰성 높은 데이터 분석/실시간 분석이 가능해졌다.

 이런 새로운 데이터 처리 시스템에서는 링크드인 사용자가 프로필을 업데이트하면 이 정보가 바로 카프카로 전달된다. 이 정보를 실시간 스트림으로 처리해서 데이터 웨어하우스에 저장된 회사 정보, 위치, 기타 속성들을 변경한다. 그리고 카프카에서 이 데이터의 변경사항을 모니터링하고 있던 검색 인덱스 시스템, 소셜 그래프 시스템, 직업 추천을 위한 추천 시스템으로 이동해 처리하게 된다.


파티션 읽기/쓰기 프로세스

🤷‍♂️먼저 Kafka 용어 정리부터!

  • Broker : 카프카 애플리케이션이 설치되어 있는 서버 또는 노드
  • Topic : 프로듀서와 컨슈머들이 카프카로 보낸 자신들의 메세지를 구분하기 위한 네임으로 사용
  • Partition : 병렬처리가 가능하도록 토픽을 나눌 수 있고, 많은 양의 메세지 처리를 위해 파티션의 수를 늘려줄 수 있다.
  • Producer : 메세지를 생산하여 브로커의 토픽 이름으로 보내는 서버 또는 애플리케이션 등을 말한다.
  • Consumer : 브로커의 토픽 이름으로 저장된 메세지를 가져가는 서버 또는 애플리케이션 등을 마랗ㄴ다.

 아파치 카프카에서의 쓰기, 읽기 연산은 카프카 클러스터 내의 리더 파티션들에게만 적용된다. 하늘색 표시의 리터 파티션에게 프로듀서들이 쓰기 연산을 진행하며 이렇게 업데이트 된 데이터는 각 파티션들의 복제본들(Replica)들에게 복사된다.

Producer의 쓰기 연산

카프카는 데이터를 순차적으로 디스크에 저장하는 특징을 가진다. 따라서 프로듀서는 저장된 데이터 뒤에 붙이는 append 형식으로 write 연산을 진행하게 된다. 이 때 파티션들은 각각의 데이터들의 순차적인 집합인 오프셋(offset)으로 구성되어있다.

컨슈머그룹의 각 컨슈머들은 파티션의 오프셋을 기준으로 데이터를 순차적으로 처리하게 된다. (먼저 들어온 순서부터) 이 때, 컨슈머들은 컨슈머 그룹으로 나뉘어서 데이터를 분산처리하고 같은 컨슈머 그룹 내에 있는 컨슈머끼리 같은 파티션의 데이터를 처리할 수 없다.
파티션에 저장되어있는 데이터들은 순차적으로 데이터가 저장되어 있으며 이 데이터들은 설정값에 따라 데이터를 디스크에 보관하게 된다.


위 그림은 컨슈머 그룹 단위로 그룹 내 컨슈머들이 각각의 파티션의 데이터를 처리하는 모습을 나타낸 것이다.

만일 컨슈머와 파티션의 개수가 같다면 컨슈머는 각 파티션을 1:1로 맡게 된다.(첫 번째 경우) 만일 컨슈머 그룹 안의 컨슈머의 개수가 파티션의 개수보다 적을 경우 컨슈머 중 하나가 남는 파티션의 데이터를 처리하게 된다. (두 번째 경우) 만약 컨슈머의 개수가 파티션의 개수보다 많을 경우는 남는 컨슈머가 파티션 개수가 많아질 때까지 대기한다.(세 번째 경우)

profile
mokpo devlog

0개의 댓글