[Kafka] 2장-(1). 카프카 기본 개념과 구조 (도서: 실전 카프카 개발부터 운영까지)

사명기·2022년 6월 23일
0

최근 흥미롭게 읽었던 책인 실전 카프카 개발부터 운영까지 - 고승범을 다시 정리해서 기록해두려고 한다.
중요한 이론과 개인적으로 느낀점을 다룰 예정이며, 실습은 기록하지 않는다.

이번 장에서는 카프카의 기본 개념(주요 요소, 리플리케이션, 파티션 등)과 핵심 개념(분산 시스템, 페이지 캐시, 배치 전송, 압축 전송 등)에 대해 알아본다.

2장. 카프카 기본 개념과 구조

2.1 카프카 기초 다지기

용어 정리

이름설명
주키퍼(Zookeeper)카프카의 메타데이터(metadata) 관리 및 브로커의 정상상태 점검(Health Check)을 담당한다.
카프카(Kafka) 또는 카프카 클러스터(Kafka cluster)여러 대의 브로커를 구성한 클러스터를 의미
브로커(broker)카프카 애플리케이션이 설치된 서버 또는 노드를 말한다.
프로듀서(producer)카프카로 메시지를 보내는 역할을 하는 클라이언트를 총칭한다.
컨슈머(consumer)카프카에서 메시지를 꺼내가는 역할을 하는 클라이언트를 총칭한다.
토픽(topic)카프카는 메시지 피드들을 토픽으로 구분하고, 각 토픽의 이름은 카프카 내에서 고유하다.
파티션(partition)병렬 처리 및 고성능을 얻기 위해 하나의 토픽을 여러 개로 나눈 것을 말한다.
세그먼트(segment)프로듀서가 전송한 실제 메시지가 브로커의 로컬 디스크에 저장되는 파일을 말한다.
메시지(message) 또는 레코드(Record)프로듀서가 브로커로 전송하거나 컨슈머가 읽어가는 데이터 조각을 말한다.



2.1.1 리플리케이션

카프카에서 리플리케이션(replication)이란 각 메시지들을 여러 개로 복제해서 카프카 클러스터 내 브로커들에 분산시키는 동작을 의미한다. 쉽게 말해, 하나의 메시지를 여러 군데에 저장하는 것이다. 이로 인해 하나의 브로커가 종료되더라도 카프카는 안정성을 유지할 수 있다. (=HA구성이 가능하다)

하나의 메시지를 '여러 군데'에 저장한다고 했는데, 그 여러 군데를 의미하는 수가 replication-factor다. 즉, replication-factor라는 옵션은 카프카 내 몇 개의 리플리케이션을 유지하겠다는 의미다.

리플리케이션 팩터 수가 커지면 당연히 안정성은 높아지지만 그만큼 브로커 리소스를 많이 사용하게 된다. 따라서 복제에 대한 오버헤드를 줄여서 최대한 브로커를 효율적으로 사용하는 것을 권장한다. 토픽 생성 시 아래와 같은 기준을 세워두고 리플리케이션 팩터 수를 설정해 사용한다면 조금 더 효율적으로 카프카를 운영할 수 있다.

  • 테스트나 개발 환경: 리플리케이션 팩터 수를 1로 설정
  • 운영 환경(로그성 메시지로서 약간의 유실 허용): 리플리케이션 팩터 수를 2로 설정
  • 운영 환경(유실 허용하지 않음): 리플리케이션 팩터 수를 3으로 설정

안정성을 높히고자 리플리케이션 팩터 수를 더 늘릴 수 있지만, 저자말에 의하면 3 정도로 설정하더라도 충분한 안정성을 보장하고, 적절한 디스크 공간을 사용할 수 있었다고 한다.

2.1.2 파티션

하나의 토픽이 한 번에 처리할 수 있는 한계를 높히기 위해 토픽 하나를 여러 개로 나눠 병렬 처리가 가능하게 만든 것을 파티션(partition)이라고 한다. 이렇게 하나를 여러 개로 나누면 분산처리도 가능하다. 이렇게 나뉜 파티션 수만큼 컨슈머를 연결할 수 있다. 아래 그림은 토픽과 파티션의 관계를 나타낸 그림이다

위 그림에서, 토픽1은 파티션 1개로 구성되어 있고, 토픽3은 파티션이 3개로 구성되어 있는 것을 알 수 있다.

파티션 수도 토픽을 생성할 때 옵션으로 설정하게 되는데, 각 메시지 크기나 초당 메시지 건수 등에 따라 달라지므로 정확하게 예측하긴 어렵다. 특히 파티션 수는 초기 생성 후 늘릴 수 있지만, 반대로 한 번 늘린 파티션 수는 절대로 줄일 수 없다. 따라서 처음에는 파티션 수를 2 또는 4 정도로 작게 생성한 후, 메시지 처리량이나 컨슈머의 LAG 등을 모니터링하면서 조금씩 늘려가는게 좋다. 여기서 컨슈머의 LAG이란 '프로듀서가 보낸 메시지 수(카프카에 남아 있는 메시지 수) - 컨슈머가 가져간 메시지 수'다. 이 LAG이라는 지표를 통해 컨슈머에 지연이 없는지 확인할 수 있다. (LAG 모니터링 방법은 후에 더 알아볼 것이다)

※ 참고
적절한 파티션 수를 산청하기 위해 계산해주는 공식을 제공하는 컨플루언트 사이트(https://eventsizer.io)를 참고해 적절한 파티션 수를 산정해 볼 수도 있다.




2.1.3 세그먼트

프로듀서에 의해 브로커로 전송된 메시지는 토픽의 파티션에 저장되며, 각 메시지들은 세그먼트(segment)라는 로그 파일의 형태로 브로커의 로컬 디스크에 저장된다. 아래 그림은 파티션과 세그먼트의 관계를 나타낸 그림이다.

위 그림을 보면, 각 파티션마다 N개의 세그먼트 로그 파일들이 존재한다. 로그 파일을 직접 확인하고 싶으면, 카프카 서버로 접근해서 /data/kafka-logs/ 디렉토리로 이동 후 파티션 디렉토리로 이동하면 로그 파일을 볼 수 있다.





2.2 카프카의 핵심 개념

2.2장에서는 카프카가 어떻게 높은 처리량과 안정성을 지닐 수 있는지 알아보도록 한다.


2.2.1 분산 시스템

분산 시스템은 네트워크상에서 연결된 컴퓨터들의 그룹을 말하며, 단일 시스템이 갖지 못한 높은 성능을 목표로 한다. 또한 하나의 서버 또는 노드 등에 장애가 발생할 때 다른 서버 또는 노드가 대신 처리하므로 장애 대응이 탁월하며, 부하가 높은 경우에는 시스템 확장이 용이하다는 장점이 있다.

카프카도 분산 시스템이므로 클러스터의 리소스가 한계치에 도달해 더욱 높은 메시지 처리량이 필요한 경우, 브로커를 추가하는 방식으로 확장이 가능한데 이는 매우 간단하게 할 수 있다. 이렇게 확장이 용이하다는 점은 카프카의 매우 큰 장점이다.


2.2.2 페이지 캐시

카프카는 높은 처리량을 얻기 위해 몇 가지 기능을 추가했는데, 그중 대표적인 것이 페이지 캐시(page cache)의 이용이다. 운영체제(OS)는 성능을 높이기 위해 꾸준히 진화&개선되고 있는데, 특히 페이지 캐시의 활용이 대표적이다. 카프카 역시 OS의 페이지 캐시를 활용하는 방식으로 설계되어 있다. 페이지 캐시는 직접 디스크에 읽고 쓰는 대신 물리 메모리 중 애플리케이션이 사용하지 않는 일부 잔여 메모리를 활용한다. 페이지 캐시를 이용하면 디스크 I/O에 대한 접근이 줄어드므로 성능을 높일 수 있다.

카프카가 OS의 페이지 캐시를 이용한다는 것은 카프카가 직접 디스크에서 읽고 쓰기를 하지 않고 페이지 캐시를 통해 읽고 쓰기를 한다고 이해하면 된다.


2.2.3 배치 전송 처리

카프카는 프로듀서, 컨슈머 클라이언트들과 서로 통신하며, 이들 사이에서 수많은 메시지를 주고받는다. 이때 발생하는 수많은 통신을 묶어서 처리할 수 있다면, 단건으로 통신할 때에 비해 네트워크 오버헤드를 줄일 수 있을 뿐만 아니라 장기적으로는 더욱 빠르고 효율적으로 처리할 수 있다.

예를 들어, 온라인 상품의 재고 수량 업데이트 작업과 구매 로그를 저장소로 보내는 작업을 생각해보자. 사용자가 상품 구매시 재고 수량은 지연 없이 실시간으로 처리되어야하지만, 구매 로그는 이미 서버에 기록되어 있으므로 몇개 혹은 몇초간 로그를 모아 배치 처리를 이용하는 것이 효율적일 것이다. 설정하는 방법은 후에 알아볼 것이다.


2.2.4 압축 전송

카프카 메시지는 전송 시 좀 더 성능이 높은 압축 전송을 사용하는 것을 권장한다. 카프카에서 지원하는 압축 타입은 gzip, snappy, lz4, zstd 등이 있다. 압축만으로 네트워크 대역폭이나 회선 비용 등을 줄일 수 있는데, 앞서 설명한 배치 전송과 결합해 사용한다면 더 높은 효과를 얻을 수 있다.

일반적으로 높은 압축률이 필요한 경우라면 gzip이나 zstd, 빠른 응답 속도가 필요하다면 lz4나 snappy를 권장한다. 하지만 메시지의 형식이나 크기에 따라 또 다른 결과를 나타낼 수 있으니 실제로 메시지를 전송해보면서 압축 타입별로 직접 테스트를 해보고 결정하는 것이 가장 좋다.


2.2.5 토픽, 파티션, 오프셋

카프카는 토픽(topic)이라는 곳에 데이터를 저장하는데, 이는 우리가 흔히 사용하는 메일 전송 시스템에서 이메일 주소 정도의 개념으로 이해하면 쉽다. 토픽은 병렬 처리를 위해 여러 개의 파티션(partition)이라는 단위로 다시 나뉜다. 카프카에서는 이와 같은 파티셔닝을 통해 단 하나의 토픽이라도 높은 처리량을 수행할 수 있다. 이 파티션의 메시지가 저장되는 위치를 오프셋(offset)이라고 부르며, 오프셋은 순차적으로 증가하는 숫자(64비트 정수) 형태로 되어있다.

위 그림에서 하나의 토픽이 3개의 파티션으로 구성되어 있다. 프로듀서로부터 전송되는 메시지들의 쓰기 동작이 각 파티션별로 이뤄짐을 볼 수 있다. 각 파티션마다 순차적으로 증가하는 숫자가 오프셋이다. 각 파티션에서의 오프셋은 고유한 숫자로, 카프카에서는 오프셋을 통해 메시지의 순서를 보장하고 컨슈머에서는 마지막까지 읽은 위치를 알 수도 있다.


2.2.6 고가용성 보장

앞서 설명한 것처럼 카프카는 분산 시스템이기 때문에 하나의 서버나 노드가 다운되어도 다른 서버 또는 노드가 장애가 발생한 서버의 역할을 대신해 안정적인 서비스가 가능하다. 이러한 고가용성을 보장하기 위해 카프카에서는 리플리케이션 기능을 제공한다. 카프카에서 제공하는 리플리케이션 기능은 토픽 자체를 복제하는 것이 아니라 토픽의 파티션을 복제하는 것이다. 원본과 리플리케이션을 구분하기 위해 카프카에서는 리더(leader)와 팔로워(follower)라고 부른다.

위는 리플리케이션 팩터 수에 따른 리더와 팔로워의 수이다. 팔로워의 수가 많을수록 안정적이고 좋을 거라 생각하지만, 팔로워의 수만큼 브로커의 디스크 공간도 소비되므로 이상적인 리플리케이션 팩터 수를 유지해야 한다. 따라서 일반적으로 리플리케이션 팩터 수를 3으로 구성하도록 권정한다. 리더는 프로듀서, 컨슈머로부터 오는 모든 읽기와 쓰기 요청을 처리하며, 팔로워는 오직 리더로부터 리플리케이션하게 된다. 리플리케이션과 관련된 내용은 다음 장에서 더 알아볼 것이다.


2.2.7 주키퍼의 의존성

카프카를 언급하면서 뺴놓을 수 없는 부분이 주키퍼(ZooKeeper)다. 주키퍼는 많은 분산 애플리케이션에서 코디네이터 역할을 하는 애플리케이션으로 사용되고 있다.

주키퍼는 여러 대의 서버를 앙상블(클러스터)로 구성하고, 살아 있는 노드 수가 과반수 이상 유지된다면 지속적인 서비스가 가능한 구조다. 따라서 주키퍼는 반드시 홀수로 구성해야 한다.

지노드(znode)를 이용해 카프카의 메타 정보가 주키퍼에 기록되며, 주키퍼는 이러한 지노드를 이용해 브로커의 노드 관리, 토픽 관리, 컨트롤러 관리 등 매우 중요한 역할을 하고 있다. 하지만 주키퍼 성능의 한계가 드러나기 시작했고, 최근에는 주키퍼에 대한 의존성을 제거하려는 움직임이 한참 진행중이다. 지금은 카프카의 중요한 메타데이터를 저장하고 각 브로커를 관리하는 중요한 역할을 하는 것이 주키퍼라는 사실만 알고 넘어가자.



2장-(2)에서 계속...

0개의 댓글