-
[Kafka] 아파치 카프카(Apache Kafka)에 대해서Computer Science/Database 2023. 12. 20. 01:49728x90
어플리케이션에 사용자가 많아지며 트래픽이 증가하고, 요청에 대한 추가작업이 많아질 때 빠른 응답과 성능을 위해 메시지 큐를 사용할 수 있다. 메시지큐는 Producer와 Consumer 사이에서 메시지를 저장하고 전달하는 중간 장치로서, 비동기적으로 작동하며 큐에 담긴 메시지를 처리한다. 메시지큐를 포함하는 메시지 브로커로는 RabbitMQ, Redis, ActiveMQ, Kafka 등이 있다. 그 중 Kafka에 대해 알아보자! (메시지 큐를 모른다면 여기로)
Kafka는 왜 생겼을까?
Kafka(카프카)는 미국의 대표적인 비즈니스 인맥 소셜 네트워크 서비스인 링크드인(LinkedIn)에서 처음 출발한 기술이다. 링크드인 사이트가 성장하면서 발생했던 여러 이슈들을 해결하기 위해 개발되었다.
기존의 링크드인 데이터 처리 시스템
기존 데이터 처리 시스템의 경우 위처럼 각 애플리케이션과 DB가 end-to-end로 연결되어 있어 각 파이프라인이 파편화되어 있었다.
데이터 파이프라인
- 데이터 처리 및 이동을 자동화하기 위한 일련의 단계로 구성된 프로세스를 말한다.시간이 지나며 요구사항이 늘어남에 따라 데이터 시스템의 복잡도가 높아지면서 다음과 같은 문제가 발생하게 되었다.
1. 시스템의 복잡도 증가
- 통합된 전송 영역이 없어 데이터 흐름을 파악하기 어렵고 시스템 관리가 어려움
- 특정 부분에서 장애 발생 시 조치 시간이 증가 (연결되어있는 애플리케이션을 모두 확인해야함)
- HW 교체, SW 업그레이드 시 관리포인트가 늘어나고, 작업시간 증가 (연결된 애플리케이션에 side effect가 없는지 확인해야함)
2. 데이터 파이프 라인 관리의 어려움
- 각 애플리케이션과 데이터 시스템(DB, Apache Hadoop, monitoring system, key-value store 등) 간의 별도의 파이프라인이 존재하고, 파이프라인마다 데이터 포맷과 처리 방식이 다름
- 새로운 파이프라인 확장이 어려워지면서, 확장성 및 유연성 감소
- 데이터 불일치 가능성이 있어 신뢰도 감소
시스템의 복잡도와 데이터 파이프 관리의 어려움을 해결하기 위해 아래와 같은 목표로 새로운 시스템을 만들었다.
- Producer와 Consumer를 분리할 것
- 메시징 시스템과 같이 영구적으로 메시지를 저장하고, 여러 Consumer에게 허용할 것
- 높은 처리량을 위해 메시지를 최적화할 것
- 데이터가 증가함에 따라 스케일 아웃이 가능한 시스템일 것
이렇게 모든 데이터 흐름을 한 곳에서 관리하고 실시간으로 대용량 데이터를 처리할 수 있는 카프카를 개발했다.
카프카 적용 후 링크드인 데이터 처리 시스템
카프카를 적용하며 모든 데이터 흐름을 중앙에서 관리할 수 있게되었다. 복잡했던 파이프라인이 하나의 파이프라인으로 연결되었다. 덕분에 확장성과 신뢰성이 증가하였고, 개발자는 서비스간 연결이 아닌 서비스들의 비즈니스 로직에 집중할 수 있게 되었다.
Kafka란?
Kafka는 다양한 서비스에서 나오는 데이터 스트림을 실시간으로 제어하는 분산 이벤트 스트리밍 플랫폼이다. 이벤트 스트리밍이란 이벤트 소스에서 실시간으로 데이터를 캡처하는 방식으로 이렇게 수집한 데이터를 저장하고, 처리하고, 관리하고 대상 어플리케이션으로 전송한다. 데이터를 생성하는 어플리케이션(Producer)과 데이터를 소비하는 어플리케이션(Consumer) 간의 중재자 역할을 함으로써 비동기 데이터 교환을 용이하게 한다.
Kafka 구성 요소
카프카의 구성요소로는 다음이 있다. 하나 하나 자세히 알아보자!
▷ Topic
보내는 메시지를 구분하기 위한 카테고리화로서 메시지의 주제를 나타낸다.
▷ Partition
토픽을 구성하는 데이터 저장소로서 수평 확장(파티션 개수 늘리기)이 가능한 형태이다. 분산 처리를 위해 사용되며 topic 생성 시 partition 개수를 지정할 수 있다. 파티션 내부에서 각 메시지는 offset(고유번호)로 구분된다.
▷ Offset
Consumer에서 메시지를 어디까지 읽었는지 저장하는 값이다. Consumer들은 각각의 파티션에 자신이 가져간 메시지 위치 정보(Offset)을 기록하여 장애 시 복구를 위해 사용한다.
▷ Kafka Cluster
클러스터란 여러 대의 컴퓨터들이 연결되어 하나의 시스템처럼 동작하는 컴퓨터들의 집합이다. Kafka Cluster는 하나 이상의 브로커로 구성되어 동작한다.
▷ Producer
데이터를 만들어내어 브로커로 전달하는 전달자 역할을 한다.
▷ Consumer
프로듀서에서 전달한 데이터를 브로커에 요청하여 메시지(데이터)를 소비하는 역할을 한다. Consumer는 Consumer Group에 속한다. Consumer는 메시지를 소비하여도 삭제하지 않아 한번 저장된 메시지를 여러번 소비할 수 있다. (Kafka delete policy에 의해 삭제)
▷ Consumer Group
하나의 Topic을 책임지는 Consumer들의 집합이다. Consumer Group내의 Consumer들이 Topic내의 Partition을 나누어 소비한다.
▷ Broker
생산자와 소비자 사이의 중재자 역할을 한다. 실행된 Kafka Server를 브로커라고 하며 Kafka Cluster 내부에 존재한다. broker.id가 존재하여 클러스터 내의 브로커 노드를 식별할 수 있다.
▷ Zookeeper
분산 애플리케이션 관리를 위한 코디네이션 시스템으로 분산 메시지 큐 시스템의 정보를 중앙에서 관리하는 역할을 한다. 브로커 간의 정보 공유, 상태 체크, 동기화 등을 관리한다.
Kafka 동작 원리
- Producer(Publisher)는 메시지의 topic을 지정하고 전달한다. (pull)
- Consumer(Subscriber)는 원하는 topic을 구독함으로써 메시지를 읽어온다. (pull)
- Producer(Publisher)와 Consumer(Subscriber)는 오로지 topic 정보만 알 뿐 서로에 대해 알지 못한다.
- kafka는 broker들이 하나의 클러스터로 구성되어 동작하도록 설계한다.
- 클러스터 내 broker에 대한 분산 처리는 zookeeper가 담당한다.
Partition과 Consumer Group의 필요성
일반적인 메시지 브로커의 구조에서는 Producer와 Consumer 그리고 메시지를 담을 topic별 Message Queue가 존재했다. 카프카에서는 추가적으로 topic내의 Partition과 Consumer들의 집합인 Consumer Group을 만들었는데 이 개념은 왜 필요할까?
하나의 topic을 여러 개의 partition으로 분할한 이유 - 병렬 쓰기
위의 그림처럼 하나의 topic은 여러개의 partition으로 구성되고, partition은 offset을 가진다. 하나의 topic에 대해 partition을 나누는 이유는 메시지를 병렬로 처리하기 위해 분산 저장을 하기 위함이다.
카프카의 topic에 메시지가 쓰여지는 것도 어느정도 시간이 소비된다. 몇 천건의 메시지가 동시에 카프카에 쓰여지면 병목현상이 발생할 수 있다. 파티션을 여러개 두어 분산 저장을 함으로써 쓰기 동작을 병렬로 처리할 수 있다. (다만, 한번 늘린 파티션은 줄일 수 없다)
- 파티션이 1개면 한 파티션에 차례로 쓰여지기 때문에 모든 메시지에 대해 순서가 보장된다.
- 파티션이 여러개면 Kafka 클러스터가 라운드 로빈 방식으로 분배해서 분산처리되기 때문에 순서가 보장되지 않는다.
파티션이 많을수록 처리량이 좋지만 장애 복구 시간이 늘어난다.
라운드 로빈(Round Robin, RR)
- 라운드 로빈 스케줄링은 선점형 스케줄링(하나의 프로세스가 CPU를 할당받아 실행하고 있을 때 우선순위가 높은 다른 프로세스가 CPU를 강제로 빼앗아 사용할 수 있는 기법)의 하나로 프로세스들 사이에 우선순위를 두지 않고 시간단위(Time Quantum)로 CPU를 할당하는 방식의 CPU 스케줄링 알고리즘이다.
- 파티션의 라운드 로빈 방식은 0, 1, 2, 3 파티션에 대해 0-1-2-3-0-1-2-3-0-1.. 로 분배하는 방식이다.Consumer Group이 필요한 이유 - 병렬 읽기
Consumer Group은 하나의 topic에 대한 책임을 가지고 있다. topic의 partition의 메시지에 대해 Consumer Group의 서로 다른 Consumer들이 소비한다. 즉, 한개의 partition에 대해 Consumer Group 내의 여러 개의 Consumer가 연결할 수 없다. Consumer Group내의 Consumer들은 다른 Consumer가 처리한 메시지에 대해 알 필요없이 자신의 메시지만 처리한다.
또한, 어떤 Consumer가 장애가 난다면 파티션 재조정을 통해 다른 Consumer가 해당 파티션을 맡아서 소비한다. offset 정보를 그룹 간에 공유하고 있기 때문에 down 되기 전 마지막으로 읽었던 메시지 위치부터 시작한다.
Kafka의 특징
- AMQP, JMS API를 사용하지 않은 TCP 기반 프로토콜 사용
- 파일 시스템에 메시지를 저장하므로 데이터의 영속성 보장
- Pub-Sub 모델의 메시지 큐 형태로 동작하는 이벤트 브로커
- 읽기/쓰기 성능을 중시
- Producer가 Batch 형태로 broker로 메시지 전송이 가능하여 속도 개선
Kafka의 Pub-Sub 모델
Pub-Sub 모델
발행-구독 모델은 송신자가 메시지를 특정 수신자에게 직접 보내는 point-to-point 방식이 아닌, 발행자가 보내는 메시지를 topic이라는 카테고리로 묶어서 보내고 구독자는 해당 topic을 구독함으로써 메시지를 받는 모델을 말한다. (참고)
카프카의 Pub-Sub 모델
카프카의 Pub-Sub 모델은 기존의 방식에서 partition과 Consumer Group 개념이 포함되었다.
- Producer는 topic에 이벤트를 보내고, 이 이벤트는 해당 topic의 각 partition에 분산되어 저장된다.
- topic을 구독하고 있는 Consumer Group 내의 Consumer는 각각 1개 이상의 partition으로부터 이벤트를 가져온다.
- Consumer Group의 서로 다른 Consumer들은 partition내의 이벤트를 처리하고 다른 Consumer가 처리한 이벤트의 존재조차 모른다.
이때 Consumer Group내의 Consumer 수와 partition 수의 조정이 중요하다.
만약 partition 수보다 consumer 수가 많다면, 아무일도 하지 않는 consumer가 생기게 된다. 따라서 항상 partition의 수를 consumer보다 같거나 크게 해주는 것이 좋다. (partition >= consumer)
Kafka VS Redis
kafka와 Redis는 모두 Pub-Sub 메시징 패턴을 사용한 메시지 브로커이다. 이 두 메시지 브로커는 어떤 차이가 있을까?
1. 이벤트 저장
kafka는 발행된 이벤트가 topic의 각 partition에 저장된다. 하지만 redis는 발행된 이벤트를 따로 저장하지 않기 때문에 Subscriber가 없다면 해당 이벤트는 사라진다. 따라서 Kafka는 언제든 발행된 이벤트를 읽으면 되는 상황일 때, Redis는 이벤트 발행, 구독이 실시간으로 이루어져야할 때 사용할 수 있다.
2. 한 이벤트를 받을 수 있는 Subscriber 개수
Kafka에는 Consumer Group이 존재하기 때문에 Consumer Group 내의 한 Consumer만 이벤트를 받을 수 있다. Redis의 경우 topic을 구독하는 Consumer들은 모두 이벤트를 받을 수 있다. 이때 동일한 역할을 하는 Consumer가 여러개라도 모든 Consumer가 이벤트를 받을 수 있다. 따라서 상황에 따라 선택해야 한다.
- 회원가입한 사용자에게 쿠폰을 발행하는 경우
- 회원가입 이벤트에 대해 한번 쿠폰 발행 기능이 동작해야하기 때문에 kafka를 사용
- redis 사용 시 여러 개의 쿠폰 발행 서버가 있다면 쿠폰 발행이 여러번 동작
- 다수의 서버가 동기화되어야 하는 경우
- kafka 사용 시 이벤트가 하나의 인스턴스에서만 동작하여 서로 다른 서버 간의 불일치 발생
- redis 사용 시 모든 서버에서 이벤트를 실행하여 동기화
Kafka의 데이터 쓰기, 복제, 저장
kafka는 topic에 대해 1개 이상의 partition이 존재한다. Producer는 partition에 나누어 데이터를 쓰고, partition에 적힌 번호는 각 partition의 offset 번호이다.
▷ 각 topic의 partition은 데이터 손실의 안정성과 가용성을 위해 복제 구성을 제공한다.
partition은 1개의 Leader Replica와 0개 이상의 Follower Replica로 구성된다. Leader Replica는 쓰기 및 읽기 작업을 담당하며, Follower Replica는 Leader Replica로부터 데이터를 복제하여 실시간으로 동기화를 유지하며, 필요 시 Leader 역할을 대체할 수 있다.
▷Topic의 데이터 중 replica 데이터는 log segment(로그 세그먼트)라는 파일로 디스크에 기록된다.
로그 파일 하나를 세그먼트라고 하는데, partition마다 n개의 세그먼트들이 존재한다. segments의 최대 크기는 1GB로 기본 설정 되어있다.
▷메모리가 남아있다면 페이지 캐시를 사용한다.
페이지 캐시란 최근에 사용된 블럭(페이지)를 메모리에 보관하는 방법이다. Kafka는 디스크를 사용하기 때문에 디스크에서 데이터를 읽거나 쓸 때 메모리에 페이지 단위로 데이터를 캐싱하여, 다시 필요할 때 메모리에서 읽어 빠르게 액세스할 수 있다.
Kafka 데이터 읽기
Consumer는 Partition 단위로 데이터를 병렬로 읽을 수 있다. Consumer Group을 구성하면 그룹 내의 복수의 Consumer가 한 topic의 데이터를 분산하여 처리할 수 있다. 이때 partition의 개수 >= Consumer Group내 Consumer 수 여야만 가능하다.(consumer가 더 많다면 1개 이상의 consumer가 유휴상태가 된다.)
Kafka 데이터 삭제
Kafka는 메시지를 소비하여도 메시지가 삭제되지 않고 저장되어 있다. Kafka에서는 Topic의 메시지에 대해서 직접 삭제할 수 있는 방법이 없는데 이는 Kafka는 데이터를 disk에 저장하기 때문에 세그먼트 단위로 삭제가 가능하기 때문이다. kafka의 데이터 삭제는 kafka delete policy에 의해 삭제되며 이를 설정할 수 있다.
kafka의 로그는 cleanup policy에 의해 삭제된다. cleanup policy는 다음 옵션을 가진다.
- delete(default) : 오래된 세그먼트를 삭제한다.
- compact : 메시지의 key별로 가장 최근의 value만 저장하여 로그를 압축한다.
Delete Policy
삭제 정책의 경우 오래된 세그먼트를 삭제하는데 그 기준은 시간 기반과 크기 기반이 있다.
시간 기반 삭제
segment에 대해 retention time에 도달하면 정의된 정책에 따라 segments가 삭제 또는 압축 대상으로 표시된다. 기본 보존 기간은 7일(604800000 ms)이며 retention.hours, minutes, ms 등으로 런타임에서 값을 수정할 수 있다.
만약 retention time이 7일이면, segments의 보관기간이 7일 이후에 삭제 작업이 일어난다.
크기 기반 삭제
topic의 partition에 대한 로그 데이터 구조의 최대 크기를 설정할 수 있다. 로그 크기가 설정에 도달하면 마지막부터 세그먼트를 제거하기 시작한다. 메시지 삭제에 대해 가시성을 제공하지 않기 때문에 널리 사용되지는 않지만 디스크 공간이 제한되는 상황에서 유용할 수 있다. 하지만 세그먼트가 하나밖에 없는 경우 정상적으로 삭제되지 않을 수 있다. (segments크기가 retention 크기보다 작아야 삭제될 수 있다.)
default 값이 -1인 경우 크기 제한은 하지 않고 시간 제한만 생기게 되어 retention.ms에 따라 파티션 크기가 결정된다.
- segments.bytes = 512KB, retention.bytes=1MB
- 이때 하나의 partition 메시지에 대해 최대 512KB로 로그파일이 분리되고 생성된 segments들이 1MB에 도달하면 가장 오래된 segment가 삭제된다.
Compact Policy
topic에 대한 로그압축을 수행한다. 메시지의 key별로 가장 최근의 value만 저장하는 방식이다. 메시지의 key는 필수가 아니기 때문에 로그 압축 기능을 사용하려면 메시지에 key값을 필수로 포함해야한다.
- K1에 대해서 V1을 정리하고 가장 최근 값이 V4로 유지시킨다.
로그 압축을 사용하면 장애 시 빠르게 복구할 수 있다. 메시지 키를 기준으로 최신의 상태만 복구하기 때문에 전체 로그를 복구할 때보다 복구 시간을 줄일 수 있다. 하지만 로그 압축이 실행되는 동안 브로커의 과도한 입출력 부하가 발생할 수 있으니 유의해야한다.
Producer의 Batch 작업
Producer는 메시지를 생산하여 Broker에게 보내는 client이다. 이때 메시지를 Record라고도 한다. Record는 몇 단계를 거쳐 Broker에게 전달된다. 다수의 메시지가 있을 때 하나씩 Broker에게 보내는 과정을 반복하면 서버에 부하가 올 수 있다. 이를 해결할기 위해 여러 데이터를 함께 Batch로 보냄으로써 서버 부하를 감소시킨다.
Record
Record는 Headers, Key, Value로 구성되어있다. Key와 Value는 Apache avro, JSON 형식도 가능하다.
Producer -> Broker 메시지 전달 과정
1. Serialize
- Producer가 send() 메소드를 호출한다.
- Record는 serialize(직렬화)되어 byte 배열로 보내지고 broker에 저장된다.
- consumer가 이용할 때는 deserialize 된다.
2. Partition
- key값을 확인하여 해당 topic의 partition으로 보낸다.
- key가 없다면 Round Robin 방식으로 topic으로 알아서 partition을 할당한다.
3. Compression
- 압축 옵션이 설정되어 있다면 압축을 진행한다.
4. Sender
- Producer는 Broker의 Leader Partition으로 Message를 전송한다.
- 메시지마다 매번 네트워크를 통해 전달하는 것은 비효율적이기때문에 메시지를 모아뒀다가 한번에 Broker에게 전달하는 Batch 전송을 사용할 수 있다.
Producer의 Batch 전송
Producer는 Record를 Producer 내부 Buffer에 저장해두었다가 어느정도 메시지가 모였을 때 여러개의 메시지를 한번에 보내는 Batch 전송을 사용한다.
Producer 내부의 Buffer는 Record Accumulator(RA)이다. RA는 각 Topic의 Partition 별로 배치 큐(Batch Queue)를 가지고 메시지를 Batch(Record Batch)형태로 묶어 큐에 저장한다. 이후 설정 조건에 만족한 Record Batch를 Broker로 전송한다.
기존의 메시징 시스템과 다른점
▷ 디스크에 메시지 저장
- 기존 메시징 시스템은 Consumer가 메시지를 소비하면 큐에서 바로 메시지를 삭제한다.
- 카프카는 Consumer가 메시지를 소비하더라도 디스크에 메시지를 일정 기간 보관하기 때문에 메시지 손실이 없다. →영속성
▷ 멀티 Producer와 멀티 Consumer
- 카프카의 경우 디스크에 메시지를 저장하는 특징으로 인해 여러 Producer가 동시에 메시지를 전송하여, 여러 Partition에 동시에 메시지를 쓸 수 있고, 여러 Consumer가 동시에 여러 partition에서 메시지를 소비할 수 있다.
- 메모리에 저장된 데이터는 특정 프로세스 또는 스레드에서만 접근 가능하기 때문에 데이터 공유를 위해서는 프로세스 간 통신(IPC, Inter-Process Communication)이나 스레드 간 공유 메모리 등의 방법을 사용해야한다.
▷ 분산형 스트리밍 플랫폼
- 분산형 스트리밍 플랫폼이란 실시간으로 데이터를 처리하고 분석하는데 특화된 플랫폼을 말한다.
- 단일 시스템 대비 성능이 우수하며, 시스템 확장이 용이하다.
- 일부 노드가 죽더라도 다른 노드가 해당 일을 지속한다. → 고가용성
- 배치 처리도 가능하다.
▷ 페이지 캐시
- 카프카는 잔여 메모리를 이용해 디스크 읽기/쓰기 대신 페이지 캐시를 통한 읽기/쓰기로 인해 처리 속도가 빠르다.
▷ 배치 전송 처리
- 서버와 클라이언트 사이에서 빈번하게 발생하는 메시지 통신을 하나씩 처리할 경우 네트워크 왕복의 오버헤드가 발생한다.
- 메시지 여러개를 묶어 배치 처리함으로써 속도 향상에 큰 도움을 준다.
Kafka는 언제 써야할까?
장점
- 낮은 지연 시간으로 메시지를 빠르게 처리할 수 있다. → 높은 처리량, 고성능
- 여러 브로커에게 데이터 분산 처리, 디스크를 통한 병렬 쓰기 작업, 메모리 캐싱을 통한 빠른 읽기, 메시지 배치, 브로커 추가로 시스템 확장을 통해 낮은 지연시간, 높은 처리량이 가능하다.
- kafka cluster의 broker를 추가해서 시스템을 확장할 수 있고, 복제 기능을 통해 장애 시에도 데이터 손실 없이 복구할 수 있다. → 확장성, 안전성, 신뢰성 → 고가용성
단점
- 메시지 조정이 필요한 경우 카프카 성능 저하
- 메시지 압축, 압축 해제에서 성능 저하
출처 및 참고
더보기https://log-laboratory.tistory.com/143
https://cloud.google.com/learn/what-is-etl?hl=ko
https://blog.voidmainvoid.net/179
https://colevelup.tistory.com/18
https://www.nasa1515.com/apache-kafka-pruducer-processing-method-ack-idempotence/
https://kafka.apache.org/documentation
https://medium.com/@sdjemails/kafka-producer-overview-4c44b1b9ece1
https://magpienote.tistory.com/251
https://m.blog.naver.com/arkdata/222632637775
https://medium.com/frientrip/pub-sub-%EC%9E%98-%EC%95%8C%EA%B3%A0-%EC%93%B0%EC%9E%90-de9dc1b9f739
728x90'Computer Science > Database' 카테고리의 다른 글
[MongoDB] MongoDB란? (2) 2024.01.03 [메시지 큐] 메시지 큐에 대해서 (메시지 큐, MOM, 특징, 이점 등) (0) 2023.12.10 [Redis] Redis에 대해서 (Redis란, 특징, 영속성, 자료구조, 아키텍처) (0) 2023.12.10 [SQL] 집계 함수를 쓰기 어려울 때 over()와 서브 쿼리 중 뭐를 사용해야 할까? (0) 2023.10.13 [SQL] JOIN 정리 (0) 2022.07.14