본문 바로가기

CS ﹒ Algorithm/Network Infra

Apache Kafka의 디자인과 네이버 데뷰

 

사내 스터디로 Naver Deview 2023의 "네이버 스케일로 카프카 컨슈머 사용하기" 영상을 보고 정리하기로 했다.

(https://www.youtube.com/watch?v=OxMdru93E6k)

 

근데 나의 카프카에 대한 이해도는 구글에 검색하면 수두룩하게 나오는 카프카에 대한 정보들 수준이라.. ㅎ

영상이 너무 난해하게 느껴져서 레퍼런스까지 함께 정리하게 되었다.

 

아래 글은 모두 내 개인적인 공부를 위한 것이고, 카프카에 대해 알고 싶다면 레퍼런스를 직접 읽도록 하자.

 

https://kafka.apache.org/documentation/#design

 

Apache Kafka

Apache Kafka: A Distributed Streaming Platform.

kafka.apache.org

 

 

 

이미지 출처 (https://kafka.apache.org/34/documentation/streams/architecture)

 

1. Design

 

1. Motivation

 

카프카는 실시간 로그 집계 같은 대용량 데이터 처리를 위한 분산 스트리밍 플랫폼이다.

따라서 높은 처리량, 낮은 지연 시간을 목표로 설계되었으며, 내결함성을 가지고 있다.

 

늘 듣던 뻔한 말이다.

 

 

2. Persistance

 

내가 카프카에 대해 가지고 있던 의문점이 하나 있었다.

카프카의 안정성은 여러가지 이유가 있지만, 그 중 하나는 지속적인 영속화에서 나온다.

그런데 카프카는 모든 파티션에 대해 별도의 로그를 저장하게 된다.

이쯤에서 갸우뚱하게 되는 것이다.. "그러면 안정적일 수는 있겠지만, 속도면에서는 비효율적이지 않나?"

그러나 아파치는 이렇게 말한다.

 

"Don't fear the file system!"

 

주절주절 복잡한 이야기들이 적혀있지만, 이를 요약하자면 다음과 같다.

 

(1) 현대 OS는 메인 메모리를 디스크 캐싱에 적극적으로 활용하고 있어서 메모리가 부족한 경우에도 디스크 캐싱을 위해 사용 가능한 메모리 공간을 적극적으로 활용하고 있다고 한다.

* 디스크 캐싱이란? 메모리에서 보조 기억장치의 주소를 저장해놓고 빠르게 읽고/쓰기 위한 기술

 

(2) Kafka는 RDBMS에서 주로 사용되는 BTree 같은 랜덤 엑세스 데이터 구조가 아닌, 로깅 솔루션과 같이 파일에 단순히 읽기/쓰기만을 제공하는 Persistance Queue를 구성하는 방식을 채택하고 있어 모든 작업의 시간 복잡도가 O(1)으로 성능상 이점이 있다.

(B+Tree 자료 구조는 O(logN+K)의 시간 복잡도를 가지고 있으므로 데이터가 늘어날수록 탐색에 필요한 시간이 증가함)

 

(3) Kafka 자체가 Java 기반의 JVM 위에서 구축되기 때문에 Java 자체의 메모리 사용량이 높다는 것을 고려해야 한다. 따라서 File System과 Page Cache를 적절하게 활용하여 Memory 공간을 확보하는 것이 더 효율적이다.

 

(4) 하드 드라이브 처리량이 과거와 달리 엄청나게 증가했다. 따라서, Random Access 방식이 아닌 선형적 쓰기의 경우 현대 운영체제에서 빠르게 동작한다.

 

 

3. Efficiency

 

어려운 내용들이 적혀있어서 100% 이해하지는 못했다.

적당히 줄였으니 자세한 내용은 레퍼런스에서 읽자.

 

(1) Kafka는 작은 데이터들의 잦은 I/O로 인한 불필요한 네트워크 통신 비용, 연산 비용을 줄이기 위해 메세지를 묶어서 보낼 수 있는 "메세지 세트"라는 추상화를 사용한다.

서버에서 메세지를 로그에 추가할 때 한 번에 Chunk로 추가하고, Consumer는 한 번에 큰 크기의 Chunk를 가져온다.

(이는 마치 Cache의 동작과 유사하다고 생각되는데, 실제로 우리의 컴퓨터는 어떤 데이터를 불러올 때 당장 CPU가 필요로하는 데이터만 불러오는 것이 아닌, 공간 지역성에 의거해 인근 데이터까지 한 번에 불러와 캐싱하기 때문이다.)

 

(2) Consumer, Broker, Producer가 모두 표준화된 이진 메세지 형식을 사용하기 때문에 불필요한 바이트 복사 과정이 발생하지 않는다.

 

(3) 데이터를 페이지 캐시로 읽어오고, 애플리케이션이 Kernal에서 버퍼로 데이터를 읽어오고, 이를 다시 Kernal 영역에 작성하고 Kernal이 NIC 버퍼로 복사하여 전송하는 불필요한 과정을 'snedfile' 시스템 호출을 이용해 페이지 캐시에서 즉시 네트워크로 데이터를 전송하는 방식을 채택했다.

 

(4) 다수의 Consumer가 하나의 Topic을 구독하는 경우, 데이터를 페이지 캐시에 한 번만 복사하고 각 Consumer가 소비할 때마다 재사용하여 빠른 속도로 데이터를 전송할 수 있다.

 

결론 : 디스크 I/O를 최소화하고, 불필요한 과정은 최대한 생략하여 성능을 향상시켰다.

 

 

 

4. Producer

 

(1) 모든 Kafka의 노드는 서버의 활성 상태를 탐지하여 Producer가 중간 routing 계층을 거치지 않고 빠른 속도로 broker에 데이터를 전송할 수 있게 한다.

 

(2) Producer는 데이터를 배치 처리하여 효율적으로 전송한다.

(이러한 처리는 추가적인 지연 시간을 야기할 수 있으나 보다 나은 처리량을 얻을 수 있다고 한다.)

 

 

5. Consumer

 

엄청 길다. 하지만 꽤나 흥미로운 내용들이므로 직접 읽어보는 걸 추천한다.

난 서론은 모두 제외하고 최대한 축약했다.

 

(1) Kafka는 Broker의 전송 시점에 Consumer의 소비 여부를 체크하거나, Consumer가 응답하는 것을 확인하여 소비 여부를 체크하지 않는다.

Topic은 파티션 세트로 나뉘어, 주어진 시간에 하나의 Consumer만 소비할 수 있다.

브로커에서는 오직 Offset이라는 정수 데이터 하나만으로 Consumer가 읽어야할 데이터의 위치를 파악할 수 있으며, 부차적으로 만약 이전의 데이터가 필요하다면 Offset을 조절하는 것만으로 이전 데이터를 다시 읽어들일 수 있다.

 

(2) Hadoop 같은 관계형 데이터 웨어 하우스 같은 오프라인 시스템에서 이점이 있다는데, 내가 하둡에 대한 지식이 없는 상태라 이해 안됨. 직접 읽어보삼.

 

(3) Consumer group은 기본적으로 dynamic membership이 설정되어 있어서, consumer가 group에 join하거나 leave할 때마다 rebalance가 발생하여 partition이 재할당된다.

문제는 이는 어플리케이션의 불안정 상태를 불러 일으킬 수 있기 때문에, static membership 기능을 제공하여 group member들이 unique한 id를 가지고 rebalance 없이 동일한 partition을 계속해서 처리할 수 있게 해준다.

 

 

 

6. Message Delivery Symantic

 

내가 이해한대로 작성했는데, 정확하지 않다.

레퍼런스를 읽자.

 

(1) Producer 관점

- 카프카는 메세지 커밋 시 로그에 저장되므로 한 번 커밋된 메세지는 해당 파티션을 복제한 브로커 중 하나라도 살아있다면 손실되지 않는다.

- V0.11.0.0부터 Producer는 Transaction과 유사한 의미 체계를 사용하여 여러 Topic Partition에 메세지를 보낼 수 있으며, 모든 메세지가 성공적으로 저장되거나 아무것도 저장되지 않는다. (정확히 한 번 전달됨을 보장한다.)

 

(2) Consumer 관점

- 2차 커밋은 Offset을 이용해서 동작한다. 예를 들어, Kafka Consumer API를 사용해 데이터베이스에 데이터를 쓰는 경우, 컨슈머가 성공적으로 데이터를 쓰고 커밋하면 Offset을 조정함으로써 데이터를 안전하게 처리하고 중복 데이터를 방지할 수 있다.

- 트랜잭션의 격리 단계 (가령, read_uncommited 같은)을 지원하므로 다른 Consumer가 Output Topic에 생성된 데이터를 커밋 완료 시점에 보게 하거나, 그 이전에 보게 가시성을 조절할 수 있다.

 

(3) Kafka Connect

- Data source(RDBMS, NoSQL, File System..)과 Kafka를 연결해주는 역할로, Kafka와 Datasource의 데이터 통합을 보다 간편하게 해준다.

 

 

 

7. Replication

 

(1) Kafka는 각 Topic의 Partition Log를 복제해서 보관한다. 따라서 특정 Broker에 장애가 발생해도 메세지를 사용할 수 있다. (즉, 최소한 하나의 동기화 복제본이 살아있는 한 커밋된 메세지가 손실되지 않는다.)

 

(2) 모든 Produce 작업은 Partition의 Leader에게 전달되고, Consume은 Partition의 Leader 혹은 Follower 모두에게 할당될 수 있다.

이 때, Follower의 Log는 Leader의 Log와 동일하며, 모두 동일한 Offset과 동일 순서의 메세지를 가진다.

(단, 리더는 아직 복제되지 않은 메세지를 포함하고 있을 수 있음.)

 

(3) Follower는 Leader의 메세지를 Consume하고 로그에 저장한다.

 

(4) 노드의 장애를 확인하기 위해 각 노드의 Heartbeat을 체크하는 방식을 사용한다.

혹은 Follower가 살아있더라도 Leader에게 뒤쳐지는 경우에도 Follower를 제거할 수 있다.

(아마 데이터 동기화가 똑바로 진행되지 않는 상태를 의미하는 듯)

 

(5) Leader에 장애가 발생할 경우, 팔로워 중 가장 최신 데이터를 가진 팔로워를 리더로 선출해야 한다. Kafka의 Partition에 대한 Write 작업은 모든 동기화 복제본이 Write를 수신하기 전까지 커밋된 것으로 간주하지 않으므로 모든 Follower는 동일한 데이터를 가지고, 따라서 이런 장애 극복 알고리즘을 위해 불필요한 연산이나 추가 용량을 필요로 하지 않는다.

(내가 정확히 이해한 게 맞는지 확실하지 않음.)

 

(6) 만약 모든 노드가 죽는다면, Kafka의 데이터 안정성에 대한 보증은 끝장이다. 이 경우에는 복제본 중 어떤 것이라도 다시 살아나기를 기다리고, 그 복제본에 데이터가 남아있기를 기도해야 한다.

 

 

8. Log Compaction

 

로그 압축은 말 그대로 메세지를 압축해서 디스크 공간을 절약하기 위한 기술이다.

카프카는 메세지를 계속해서 순차적으로 저장하는 구조로 만들어져 있기 때문에, 로그의 크기가 계속해서 증가한다.

 

또한, 당연하게도 로그를 압축하더라도 기존의 offset과 메세지 순서는 절대 변경되지 않고 그대로 보존된다.

라는 내용이 아주 길게 써있는데 어려우니까 직접 읽어보삼.

 

 

9. Quotas

 

(1) Consumer/Producer가 너무 많은 데이터를 소비/생성하게 될 경우 브로커 리소스를 독점하고 네트워크 장애를 유발할 수 있다. 이를 방지하기 위해 할당량을 지정할 수 있다.

 

(2) User / Client-id Group에 대해 정의할 수 있으며, 사용자에 대한 할당량은 Zookeper, ID 할당량은 /config/clients에 기록되고 브로커는 이에 대한 재정의를 즉시 적용한다.

이를 통해 전체 클러스터를 재시작할 필요 없이 할당량을 변경할 수 있다.

 

(3) 네트워크 대역폭(bytes/s)과 요청 속도 할당량(Thread%)로 할당량을 제어할 수 있다.

 

(4) 브로커는 할당량을 위반을 감지할 경우 클라이언트를 할당량 이하로 만드는데 필요한 지연 시간을 계산하고 즉시 지연과 함께 응답을 반환하며, 지연이 끝나기 전까지 클라이언트에 대한 채널을 Mute한다.

 

(5) byte-rate와 thread utilization을 검사하기 위해 여러개의 small windows를 사용한다.

 

 

 

 

 

 

2. Naver Deview - Kafka

 

이론적인 부분들은 위와 겹치다보니 모두 제외했다.

 

1. Consumer Group

 

(1) Consumer Group에서 중요한 기능은 무엇이 있을까요.

- Partition Assignment : Consumer에 Partition을 할당

- Offset Commit : 파티션에 대한 읽기 작업이 어디까지 진행됐는지 저장

 

(2) Partition Assignment의 원리

- 서버에서 Consumer에게 할당하는 것이 아님

- Coordinator

  > Consumer Group의 변경 감지

  > Topic Partition의 변경 감지

  > Consumer Group Leader와 다른 Follower들간의 Communication 중개

- Consumer Group Leader

  > 현재 구독 중인 topic의 파티션들을 consumer들에게 할당

 

(3) 왜 이런 구조를 채택했는가

- Broker를 재시작할 필요 없이 더 유연하고 확장 가능한 파티션 할당을 지원

 

(4) Consumer Coordination의 핵심 설정

- max.poll.interval.ms : Consumer가 레코드를 처리하는 것에 사용하는 최대 시간

- Session.timeout.ms : Consumer가 백그라운드에서 heartbeat 없이 동작하는 것으로 판정받을 수 있는 최대 시간

- partition.assignment.strategy : 내가 원하는 할당 알고리즘을 설정하는 것은 물론, 커스텀 데이터를 보내거나 받을 수 있다.

 

 

2. Cloud 환경에서의 Consumer Group

 

(1) 문제점

- 물리적 장비의 자원을 여러 pod가 나눠서 사용

- Noisy Neighbors : 가상화 환경에서 서로 PC 자원을 공유할 때, 다른 컨테이너에서 발생하는 과도한 리소스 사용으로 인해 자신의 성능이 저하되는 현상

- Network Hiccup : 네트워크의 혼잡으로 인해 전송이 지연되거나 데이터 패킷이 손실되는 현상

- Pod Rescheduling : 예기치 않은 상황에서 pod가 중단되거나 다운되는 경우, 다른 노드로 재배치되는 현상. 오버헤드가 발생.

 

(2) 해결책

- group.instance.id

  > "Static Group Membership"

  > 재시작 이전에 할당되어 있던 파티션들을 재할당함으로써 불필요한 Partition Rebalance를 예방

- session.timeout.ms

  > Consumer 프로세스가 Broker와 신호를 주고받지 않아도 rebalance를 발생시키지 않는 최대 시간

  > 무조건 길게할 경우 지연이 생길 수 있고, 짧게 한다면 불필요한 rebalance가 발생하므로 환경에 맞게 설정

- Follower Replica로부터 읽기 설정

  > 장애 극복에 요긴하나, 모든 복제본에게 메세지를 복제하고 처리하기 위한 네트워크 트래픽/ Disk I/O로 인해 엄청난 과금이 발생될 수 있다.

 

(3) 미봉책

- replica.selector.class로 특정 Replica만 읽기용으로 사용할 수 있으나, 서비스 규모가 커질수록 일일히 이를 설정하는 것은 불가능에 가깝다.

- 클라우드 환경에서는 하드웨어 장애보다 네트워크 문제가 더 자주 발생할 가능성이 높아서 Rack-awareness를 설정하면 오히려 네트워크 병목 현상이 발생할 가능성이 높아진다.

- Partition Assignor는 rack 정보를 고려하지 않고 단순히 round lobin 방식으로 partition을 할당한다. 결국, 한 rack에 consumer가 집중되며 전체 클러스터 성능을 저하시킨다.

 

(4) 그래서 네이버는 어떻게 하고 있는데요

- Rack 설정을 고려하는 Partition Assignor를 직접 개발해서 넣어주고 있음.

 

(5) 희망편

- 기존 Consumer Protocol이 확장되어 Rack 정보를 담을 수 있게 되었다.

- 아직까지 해당 정보를 활용하는 Partition Assignor 구현체가 탑재되지는 않았으나, 개발될 것으로 예상.

 

 

 

 

 

 

마무리

 

언젠가 카프카의 이론적인 부분을 한 번 각잡고 봐야겠다는 생각을 했었는데, 사내 스터디 덕분에 하루 정도 카프카를 공부하는 시간을 가질 수 있게 되었다.

공부하면서 느낀 점은, 역시나 Network / OS / DB에 대한 이해도가 있어야 다른 기술들에 대해서도 깊은 이해를 가져갈 수 있다는 것이다..

나의 얕은 CS 지식으로는 레퍼런스의 모든 내용을 이해하기엔 힘들었다.

빨리 KCOW의 운영체제와 네트워크 강의를 완강해야할 것 같다.