목차
Kafka 란
카프카는 링크드인에서 파편화된 데이터 파이프라인의 문제를 해결하기 위해 만들어졌다. 서비스 초기에는 데이터 사이즈가 크지 않고 파이프라인도 단순했지만, 시스템이 거대해지면서 데이터를 전달해야 할 노드의 수가 늘어났고 연결하는 시스템간의 복잡도가 매우 증가했다. 또한 MSA 를 채택하거나 기존 시스템을 마이그레이션하고, 연결해야 할 서비스들이 많아지고 처리해야 할 트래픽들은 높아지는데 장애 상황을 대비한 효과적인 대안들을 마련해야했다.
카프카는 대규모 데이터를 수집, 처리, 통합하기 위해 사용되는 이벤트 스트리밍 플랫폼이다.
이벤트는 소프트웨어 또는 응용 프로그램에서 식별하거나 기록하는 모든 유형의 사건, 작업 또는 변경사항을 의미하고 또 다르게 메시지라고 말하기도 한다.
예를 들어 회원 시스템이 있다면 회원가입, 회원정보변경 등이 이벤트의 한 예시이다.
상품 시스템이라면 상품등록, 상품정보수정, 재고차감 등이 될 수 있고,
주문 시스템이라면 고객의 주문, 취소
웹 페이지라면 사용자의 클릭 이벤트 등 이러한 이벤트들은 사용자의 다양한 데이터를 분석하기 위해 중요하다.
카프카는 이벤트 발행자와 구독자 사이에서 중계자 역할을 한다.
시스템이 작고 단순한 상황에서는 이벤트를 생성하는 시스템과 사용하는 시스템이 서로 직접 메시지를 정의하고 트래픽의 양이나 특성을 공유할 수 있었지만, 대규모 시스템에서는 이러한 시스템 간의 공유가 너무 많아 시스템의 복잡도가 크게 증가하고, 변경에 취약하고, 장애에 노출되기 쉽게 된다.
메시지 브로커가 없는 상태라면 아래 그림과 같이 각각의 이벤트 발행자는 이벤트를 수신하거나 활용하는 시스템을 직접 연결해야한다. 또한 시스템 간의 직접적인 연결은 이후에 상대 시스템의 변경이나 장애 상황에도 직접 노출되기에 잠재적인 위협이 있다.
그렇다면 카프카와 같은 이벤트 스트리밍 플랫폼을 적용하면 이벤트 발행자는 카프카 브로커에게 메시지를 전달함으로써 프로세스를 단순화할 수 있고, 서로 다른 시스템에 대한 의존성을 낮추며, 서로간의 변동이나 장애 등에서 비교적으로 자유롭게 될 수 있다.
대규모 트래픽이 발생하는 상황에서 상대적으로 시간이 많이 발생하는 로직을 가지고 있는 기능이거나 여러 관심사를 한꺼번에 처리해야하는 로직에서는 동기 방식의 프로세스보다는 비동기 방식 프로세스를 적극 활용하는 것이 유리하다.
Zookeeper 란
주키퍼는 카프카 클러스터의 토픽 정보와 같은 메타 데이터를 관리하는 역할을 한다.
Kafka cluster 구조
실무에서는 대부분 3대 이상의 클러스터로 구성되어 사용할 것이다. 이는 서버에 장애가 생겨도 서버의 중단이나 데이터 유실없이 안정적으로 시스템을 운영하기 위한 필수적인 요구사항이다. 데이터 복제 즉 레플리케이션과 결합하여 고가용성 구성이라고 한다. 엘라스틱서치, 레디스, 카산드라 클러스터 등 대규모 트래픽 처리를 위한 분산 서비스 도구들은 구체적인 방법들은 조금씩 차이는 있지만 클러스터 구성과 데이터 레플리케이션을 통해 특정 노드의 시스템 장애상황에서도 서비스 위험을 최소화하기 위해 자기만의 아키텍처를 제시하고 있다.
아래 그림에서 장애 상황을 가정하여 팔로워가 있는 브로커2에 장애가 발생할 경우 기본적으로 프로듀서와 컨슈머에 영향을 끼치지 않는다. 프로듀서와 컨슈머가 리더 파티션에서만 읽기와 쓰기를 하기 때문이다. 물론 이 경우에도 해당 토픽의 레플리케이션 팩터나 프로듀서 설정에 따라 영향을 받을수도 있다.
리더 파티션이 있는 브로커0에 장애가 생긴다면 카프카 클러스터 내에 컨트롤러 모듈이 이를 탐지하여 팔로워 파티션 중 하나를 즉시 리더 파티션으로 만들어 서비스를 계속할 수 있도록 한다. 리더가 결정되는 순간에는 일시적으로 읽기와 쓰기시에 타임아웃이 일어날 수 있는데 프로듀서는 새로운 리더 파티션이 확정될 때 까지 재시도를 통해 서비스 영향을 최소화할 수 있다. 리더 파티션 선정이 완료되면 프로듀서와 컨슈머는 다시 정상적인 발행과 수신을 이어나갈 수 있다.
Topic
토픽은 하나의 주제 즉 데이터 또는 메시지 종류를 구분하기 위한 카프카의 기본 단위이다.
예를 들어 주문 서비스 개발팀에서 주문완료 라는 토픽을 생성하고 새로운 주문이 완료될 때마다 주문정보를 해당 토픽에 지속적으로 발행해줄 수 있다.
토픽은 프로듀서와 컨슈머가 이벤트 종류를 구별하는 문자열로 이름을 정할 때 나름의 네이밍룰을 고려해두는 것이 좋다. 메시지를 발행하는 팀이나 도메인의 이름, 이벤트의 종류, 버전 등의 값을 이용하여 문자열만으로 그 의미를 쉽게 유추할 수 있게 하는 것이 좋다.
토픽을 생성할 때 하위 요소인 파티션의 레플리케이션 팩터를 설정하는 옵션이 있는데 최소 3을 설정하는 것이 바람직하다. 레플리케이션 팩터를 디폴트값인 1로 설정하면 원본 이외에 복제를 수행하지 않겠다는 설정이고, 3이면 2개의 복제본을 생성하겠다는 의미이다.
Partition
프로듀서가 토픽을 지정하여 메시지를 발행하면 브로커는 해당 토픽의 하위 저장소인 파티션에 메시지를 안전하게 저장한다. 카프카는 운영체제의 파일시스템 자체를 저장소로 활용하는데, 그래서 토픽을 파일시스템의 폴더와 같다고 생각하면 된다.
이는 server.properties 파일에 log.dirs 옵션을 확인하면 되는데, 이 위치는 카프카의 메시지가 저장되는 위치이다.
레플리케이션은 이 파티션 단위로 실행되어 복제본 생성을 통해 브로커 장애에 대한 대비를 한다.
리더 파티션은 프로듀서와 컨슈머가 연결되어 읽기와 쓰기가 실행되는 파티션이다.
팔로워 파티션은 레플리케이션 팩터에 의해 만들어진 복제본으로 장애 상황에 대비하여 준비되어 있을 뿐 실제 프로듀서와 컨슈머가 직접 연결되어 있지는 않다.
팔로워 파티션들은 리더 파티션이 문제가 생겼을 때 언제라도 리더가 될 수 있도록 데이터를 지속적으로 싱크하여 만약의 장애 상황을 대비하고 있다.
보통의 스토리지들이 복제본을 통해 읽기 트래픽을 분산시키는 경우가 많은데, 카프카의 경우 리더 파티션에서만 읽기와 쓰기가 일어난다.
Partition 확장
카프카는 리더 파티션에서만 읽기와 쓰기가 일어나서 만약 하나의 파티션만 사용한다면 많은 머신과 브로커를 준비하느라 처리량 확대는 제한적일 수 밖에 없을 것이다. 카프카는 하나의 토픽에 인입되는 메시지를 분할하여 개별 파티션에 나누어 처리하도록 하여 이 문제를 해결한다.
위 그림에는 topic-example1 에 4개의 파티션으로 구분되어 있다. 이 그림처럼이면 하나의 토픽에 하나의 파티션만 있을 때 보다 분산환경에 장점을 살릴 수 있고, 훨씬 많은 동시처리 능력을 제공할 수 있다.
토픽을 생성할 때 파티션의 개수는 동시처리량과 깊은 관계를 갖기 때문에 토픽 생성시에 주요한 관심사인데, 해당 토픽에서 예상되는 메시지 인입량과 컨슈머의 처리량 등을 고려하여 초기값을 설정하고, 이후 서비스를 운영하는 과정에서 모니터링을 통해 파티션을 확대하는 경우도 있다.
Parition 내에 데이터 소비방식
파티션 내부는 하나의 큐처럼 보인다. 일반적인 메시지큐는 컨슈머가 데이터를 가져가면 브로커에 메시지는 사라지는데, 카프카는 컨슈머가 데이터를 가져가는 것과 상관없이 파티션 내에 고유의 순서에 따라 메시지를 저장하다가 토픽에 지정된 시간이 지나면 자동으로 오래된 메시지가 삭제되는 방식으로 메시지를 관리한다.
카프카는 오프셋을 통해 각각의 컨슈머 그룹이 각각의 파티션별로 읽어간 레코드의 위치를 관리한다. 오프셋은 컨슈머 그룹이 이미 가져간 마지막 레코드에 대한 포인터인데, 컨슈머 그룹은 동일한 그룹명을 사용하는 컨슈머들의 집합이다.
예를 들어 프로듀서가 메시지를 발행하여 파티션 내에 순서에 따라 기록하여 마지막으로 4번의 오프셋에 레코드를 저장했을경우 새로운 컨슈머가 메시지를 읽어간 후 컨슈머 내에서 정상적으로 데이터를 처리하게 되면 컨슈머는 커밋 메시지를 카프카에 전달하여 current offset 을 이동하게 된다. 이후 프로듀서가 5,6번의 메시지를 전송하면 컨슈머가 current offset 이후부터 다시 메시지를 읽어가기 시작한다.
current offset 은 컨슈머 그룹별로 카프카 내부에 저장되고 있기에 새로운 컨슈머 그룹은 기존 컨슈머 그룹이 얼마만큼의 메시지를 읽어갔는지와 무관하게 from, beginning 옵션을 통해 처음부터 메시지를 읽어가거나 그렇지 않으면 현재 접속한 시점 이후 새로 인입된 메시지부터 읽어갈 수 있다.
파티션을 여러 개 만들게 되면 컨슈머 그룹내에 컨슈머 숫자를 증가시켜 병렬처리 능력을 향상 시킬 수 있어 실무에서는 특별한 경우가 아니면 파티션을 다수로 설정하여 처리량 증가를 도모한다.
토픽이 여러 개의 파티션으로 나뉘게 되면 프로듀서가 메시지 발행 시 여러 메시지를 어떤 파티션에 기록할 지 결정할 방법이 필요하다. 일반적으로 발행 메시지 내에 키 값이 비어있으면 메시지들은 모든 파티션에 라운드 로빈 방식으로 기록된다.
하나의 파티션을 사용할 때는 카프카에 인입된 순서 그대로 컨슈머에서 읽어가는 순서를 보장할 수 있지만 여러 개의 파티션에 라운드 로빈 방식으로 메시지를 기록하게 되면 컨슈머가 메시지를 처리할 때 반드시 카프카의 인입된 순서대로 처리하는 것을 보장하지 않는 특성이 있다.
컨슈머의 메시지 처리순서를 보장하기 위해서는 파티션을 한 개만 설정하거나 여러 개 파티션을 사용하게 되면 라운드 로빈 방식이 아니라 발행 메시지의 키 설정을 통해 특정한 메시지 키의 경우 동일한 파티션에 할당되도록 강제할 수 있다.
예를 들어 모두 동일한 고객과 연결된 이벤트를 생성한 경우 컨슈머가 메시지를 처리할 시 카프카에 인입순서가 중요하다면 고객의 아이디를 키로 설정하여 모두 동일한 파티션으로 할당될 수 있도록하고, 해당 고객의 모든 이벤트가 항상 순서대로 도착하도록 할 수 있다. 메시지 순서가 매우 중요한 시스템이라면 파티션 개수 및 메시지 키 설정, 컨슈머 처리 프로세스를 고민해볼 필요가 있지만 이러한 제약은 분산 병렬처리 관점에서의 이점을 약화시키고 메시지 처리코드의 복잡성을 증가시키거나 메시지 처리의 어려움을 발생시킬 수 있기에 순서에 기반한 처리가 반드시 필요한 것인지 신중히 검토해서 결정할 필요가 있다.
만약 가능하다면 메시지 발행 순서에 의존하지 않도록 메시지 스펙이나 데이터 플로우 설계를 해보는 것이 시스템 관점에서 유연성을 제공하는데 더 이로울 것이다.
Kafka connect
카프카 커넥트는 카프카를 이용한 퍼블리싱이나 컨슈밍같은 반복적인 개발을 단순히 하기 위해서 카프카 오픈소스에 포함되어 있는 모듈이다. 커넥트용 JAR 파일과 설정파일을 이용해서 지속적으로 로그파일을 읽어들여 카프카에 퍼블리싱하거나 컨슘해서 엘라스틱 서치에 기록하는 행위들을 단순화할 수 있는 도구이다.
오픈소스 형태로 다양한 소스 커넥트와 싱크 커넥트가 공개되고 있다.
파일 싱크 커넥트는 카프카의 특정 토픽을 컨슘하여 파일에 지속적으로 쓰기를 해주는 커넥터이다.
'IT > 대용량 데이터&트래픽 처리' 카테고리의 다른 글
Redis 에 대해서 (세션, 캐시, 클러스터, 쿼리튜닝) (1) | 2023.06.06 |
---|---|
외부 서비스 연동 시 비동기 처리에 대해서 (0) | 2023.01.27 |
Mysql 정규화, 인덱스, 트랜잭션, Lock, 동시성에 대해서 (0) | 2023.01.19 |