[카프카 핵심 가이드] Chapter 06. 카프카 내부 메커니즘

Falco·2023년 12월 3일
1
post-thumbnail

카프카의 내부 작동 방식에 대해 알아봅시다.

  • 카프카 컨트롤러
  • 카프카에서 복제가 작동하는 방식
  • 카프카가 프로듀서와 컨슈머의 요청을 처리하는 방식
  • 카프카가 저장을 처리하는 방식

이러한 동작과정을 알아야 카프카를 튜닝할 때 도움이 될 수 있습니다.

6.2 컨트롤러

  • 컨트롤러는 일반적인 카프카 브로커의 기능에 더해서 파티션 리더를 선출하는 역할을 추가적으로 맡습니다.

  • 브로커들은 주키퍼의 컨트롤러 노드에 변동이 생겼을 때 알림을 받기 위해서 노드에 와치를 설정합니다.

컨트롤러 브로커가 멈추거나 주키퍼와의 연결이 끊어질 경우, Ephemeral노드가 삭제됩니다. 이후 다른 브로커가 와치를 통해 컨트롤러가 없어졌다는 것을 알아차리게 되고 주키퍼에 컨트롤러 노드를 생성합니다.

이 때 에포크 번호를 활용하여 이전 컨트롤러와의 차이점을 명시하고, 좀비 컨트롤러를 방지합니다.

6.2.1 Kraft

KRaft라 불리는 새로운 컨트롤러가 개발되었습니다. 이는 기존의 주키퍼 기반 컨트롤러의 한계점을 극복한 래프트 기반 컨트롤러 쿼럼을 의미합니다.

새로운 컨트롤러 설계의 핵심 아이디어는 카프카 그 자체에 사용자가 상태를 이벤트 스트림으로 나타낼 수 있도록 하는 로그 기반 아키텍처를 도입한다는 점입니다.

6.3 복제

카프카는 분산되고, 분할되고, 복제된 커밋 로그 서비스라고 표현되기도 합니다. 개별적인 노드에는 필연적으로 장애가 발생할 수 밖에 없고, 카프카는 이러한 상황속 신뢰성과 지속성을 보장하는 방식이기 때문입니다.

카프카는 데이터를 토픽단위로 조직화합니다. 각 토픽은 1개이상의 파티션으로 분할되며, 각 파티션은 다시 다수의 레플리카를 가질 수 있습니다. 각각의 레플리카는 브로커에 저장되는데, 대개 하나의 브로커는 수백 개에서 수천 개의 레플리카를 저장합니다.

레플리카는 2종류가 있습니다.

  • 리더 레플리카

각 파티션에는 리더 역할을 하는 레플리카가 하나씩 있습니다. 모든 쓰기 요청은 리더 레플리카로 주어지며, 일겅올 때는 리더 레플리카, 팔로워레플리카 둘다 사용합니다.

  • 팔로워 레플리카

팔로워는 클라이언트의 요청을 처리할 수 없으며, 메시지를 복제함으로써 최신상태를 유지하는 역할을 수행합니다. (리더 레플리카가 크래쉬가 날 경우 승격되기위해)


리더 레플리카는 어떤 팔로워 레플리카가 최신 상태를 유지하고 있는지 확인합니다. 팔로워 레플리카는 메시지가 도착하는 즉시 리더 레플리카로부터 모든 메시지를 복제해 옴으로써 최신 상태를 유지하지만, 다양한 원인으로 동기화가 깨질 수 있습니다. 10초이상 메시지를 동기화하지 않으면 아웃 오브 싱크 레플리카라고 불리며 더이상 리더레플리카가 될 수 없습니다.

팔로워 레플리카에게 리더 레플리카가 메시지를 전달함으로써 각각의 팔로워 레플리카가 어디까지 오프셋을 저장했는지, 언제 저장했는지를 저장합니다.

선호 리더(토픽이 처음 생성되었을 때 리더 레플리카)가 리더 레플리카로 선별되는 경우가 많습니다.

6.4 요청 처리

카프카 브로커가 하는 일의 대부분은 클라이언트, 파티션 레플리카, 컨트롤러가 파티션 리더에게 보내는 요청을 처리하는 것입니다.

카프카는 TCP로 전달되는 이진 프로토콜을 이용합니다.

언제나 클라이언트가 연결을 시작하고 요청을 전송하며, 브로커는 요청을 처리하고 클라이언트로 응답을 보냅니다. 특정 클라이언트가 브로커로 전송한 요청은 브로커가 받은 순서대로 처리됩니다. FIFO

그렇다면 브로커는 요청을 어떻게 처리할까요?

브로커는 연결을 받는 각 포트별로 억셉터(acceptor)쓰레드를 하나씩 실행시킵니다. 억셉터 쓰레드는 연결을 생성하고, 들어온 요청을 프로세서 스레드(네트워크 쓰레드)에 넘겨 처리하도록 합니다. (프로세서 스레드의 수는 설정이 가능합니다.) 네트워크 쓰레드는 클라이언트 연결로부터 들어온 요청들을 받아서 요청 큐에 넣고, 응답 큐에서 응답을 가져다 클라이언트에게 반환합니다.

  1. 연결 (네트워크 쓰레드)
  2. 요청 큐에 넣기
  3. I/O쓰레드
  4. 응답 큐
  5. 네트워크 쓰레드에서 폴링하여 반환

이 과정은 스프링 MVC의 과정과 비슷해보일 수 있습니다. 하지만 억셉터 쓰레드가 생성되고, 프로세스 쓰레드에게 작업을 넘긴다는 점이 다릅니다.

클라이언트의 요청은 다음과 같습니다.

  • 쓰기 요청

카프카 브로커로 메시지를 쓰고 있는 프로듀서가 보낸 요청

  • 읽기 요청

카프카 브로커로부터 메시지를 읽어오고 있는 컨슈머나 팔로워 레플리카가 보낸 요청

  • 어드민 요청

토픽 CRUD 어드민 요청


쓰기 및 읽기 요청 모두 리더 레플리카로 전송되어야 합니다. 카프카 클라이언트는 메타데이터 요청을 통해 토픽 목록, 파티션 목록, 레플리카 목록 등을 반환하며 이를 통해 리더를 맡고 있는 브로커에 읽거나 쓰는 요청을 수행합니다.

클라이언트는 해당 정보를 보통 캐시해 두었다가 정보를 활용하여 파티션의 리더 역할을 맡고 있는 브로커에게 바로 쓰거나 읽습니다.

6.4.1 쓰기 요청

acks설정 변수는 쓰기 작업이 성공한 것으로 간주되기 전 메시지에 대한 응답을 보내야 하는 브로커의 수를 의미합니다.

  • acks=1 : 리더만이 메시지를 받았을 때
  • acks=all : 모든 인-싱크 레플리카들이 메시지를 받았을 때
  • acks=0 : 메시지가 보내졌을 때

파티션의 리더 레플리카를 가지고 있는 브로커가 해당 파티션에 대한 쓰기 요청을 받게되면 몇가지 유효성 검증을 수행합니다.

  1. 데이터를 보내고 있는 사용자에게 권한이 있는가?
  2. acks 설정 값이 올발느가? (0, 1, or all)
  3. acks 설정값이 all일 경우 충분한 인-싱크 레플리카가 존재하는가?

브로커는 새 메시지를 로컬 디스크에 씁니다. 리눅스의 경우 메시지는 파일시스템 캐시에 쓰여지며, 리눅스의 경우 이는 파일 시스템에 캐시되는데 진짜 디스크에 언제 반영될지는 보장이 없습니다. 카프카는 데이터가 디스크에 저장될 때까지 기다리지 않습니다. 즉, 메시지의 지속성을 위해 복제에 의존합니다.

6.4.2 읽기 요청

읽기 요청또한 파티션 리더를 맡고 있는 브로커에게 전송됩니다. 파티션 리더는 요청이 유효한지 확인하고, 메시지를 가져와 클라이언트에게 전송합니다.

메시지를 가져올 때 제로카피(Zero-Copy) 최적화를 사용하는 것으로 유명합니다. 리눅스의 파일시스템캐시에서 읽어온 메시지를 중간 버퍼를 거치지 않고 바로 네트워크 채널로 보내는 것을 의미합니다. 이 방식을 통해 데이터를 복사하고 메모리 상에 버퍼를 관리하기 위한 오버헤드가 사라집니다.

컨슈머는 모든 인-싱크 레플리카에 복제된 메시지들만을 읽을 수 있습니다. 따라서 어떠한 이유로 브로커들 사이의 메시지 복제가 늦어지면, 새 메시지가 컨슈머에 도달하는 데 걸리는 시간도 길어집니다.

컨슈머가 많은 수의 파티션으로부터 이벤트를 읽어오는 경우가 있을 때, 카프카는 읽기 세션 캐시를 활용합니다. 이는 모든 메타데이터를 캐시하며, 성능을 향상시킵니다.

6.5 물리적 저장소

카프카의 기본 저장 단위는 파티션 레플리카입니다. 파티션은 다른 브로커들 사이에 분리될 수 없으며, 같은 브로커의 서로 다른 디스크에 분할 저장되는 것도 불가능 합니다.

카프카가 데이터를 저장하기 위해 사용 가능한 디렉토리들을 어떻게 활용하는지 알아봅니다.

6.5.1 계층화된 저장소

카프카 3.0부터 파티션 데이터를 저장할 때 계층화된 저장소 기능을 사용합니다. 그 이유로써

  • 파티션별로 저장 가능한 데이터에 한도가 있습니다. (최대 보존 기한과 파티션 수는 물리적인 디스크 크기에 제한을 받음)
  • 디스크와 클러스터 크기는 저장소 요구 조건에 의해 결정됩니다. 지연과 처리량이 주 고려사항일 경우 클러스터는 필요한 것 이상으로 커지는 경우가 많습니다.
  • 클러스터의 크기를 줄이거나 키울 때, 파티션의 위치를 다른 브로커로 옮기는데 걸리는 시간은 파티션의 수에 따라 결정됩니다. 즉 작은 클러스터는 더 유연합니다.

계층화된 저장소 기능에서 카프카 클러스터는 저장소를 원격, 로컬로 나눕니다. 로컬은 기존과 동일하되, 원격은 HDFS, S3와 같은 전용 저장소 시스템을 이용합니다.

지연성관점에서 원격 저장소는 단점이 있지만, 무한한 저장 공간, 더 낮은 비용, 탄력성 뿐만 아니라 실시간 데이터를 읽는 작업분야에서 원격 저장은 더 유용합니다.

6.5.2 파티션 할당

사용자가 토픽을 생성하면, 카프카는 우선 이 파티션을 브로커 중 하나에 할당합니다. 만약 브로커가 6개있을 때 파티션이 10개, 복제 팩터가 3인 토픽을 생성하고자 한다면, 카프카는 30개의 파티션 레플리카를 브로커 6개에 할당해야 합니다. 이럴 때 다음과 같은 목표를 가집니다.

  • 레플리카들을 가능한 한 브로커 간에 고르게 분산시킵니다.(브로커 별로 5개의 레플리카)
  • 각 파티션에 대해 각각의 레플리카가 서로 다른 브로커에 배치되도록 합니다.
  • 브로커에 랙 정보가 설정되어 있다면 다른 파티션들은 서로 다른 랙에 할당해야 합니다.

카프카에서는 위 기능을 제공하며 파티션을 할당합니다. 하지만 사용 가능한 공간이나 현재 부하와 같은 점이 고려되지 않으며, 파티션 수만이 고려됩니다.

6.5.3 파일 관리

카프카는 영구히 데이터를 저장하지도, 데이터를 지우기전에 모든 컨슈머들이 메시지를 읽어갈 수 있도록 기다리지도 않습니다. 카프카는 오래되면 지운다 또는 용량이 넘어가면 지운다 라는 규칙에 의거하여 메시지를 관리합니다.

하나의 파티션은 여러 개의 세그먼트로 분할되며, 각 세그먼트는 1GB 또는 1주일치의 데이터 만큼 저장됩니다. 카프카가 파티션 단위로 메시지를 쓰는 만큼 각 세그먼트 한도가 다 차면 세그먼틀르 닫고 새로운 세그먼트가 생성됩니다. (현재 쓰여지는 세그먼트를 액티브 세그먼트라고 불리웁니다.)

6.5.4 파일 형식

세그먼트는 하나의 데이터 파일 형태로 저장되며, 파일 안에는 메시지와 오프셋이 저장됩니다.

이러한 메시지는 압착과 삭제 보존정책에 의하여 관리됩니다. 메시지는 다음과 같은 두 영역을으로 나뉘게 됩니다.

  • 클린

이전에 압착된 적 없었던 메시지들이 저장됩낟. (하나의 키에 하나으 ㅣ값)

  • 더티

마지막 압착 작업 이후 쓰여진 모든메시지들이 저장됩니다.

카프카는 압착 매니저 쓰레드와 압착 스레드를 활용하여 압착 작업을 수행합니다. 이는 더티 메시지의 비율이 높은 파티션을 골라 압착한 후 클린 상태로 만듭니다.

이러한 압착은 액티브 세그먼트에는 적용되지 않습니다. 카프카는 50%이상이 더티 레코드인 경우 압착을 수행하며 이 설정또한 변화될 수 있습니다.

profile
강단있는 개발자가 되기위하여

0개의 댓글