3장. 카프카 디자인

Posted by yunki kim on February 5, 2024

1. 카프카 디자인 특징

카프카는 처리량에 중점을 두고 설계된 만큼, 높은 처리량, 빠른 메시지 전송, 운영 효율화 등을 위해 분산 시스템, 페이지 캐시, 배치 전송 처리 등의 기능이 구현되어 있다. 이 기능들을 하나씩 알아보자.

1. 1 분산 시스템

카프카는 분산 시스템이기에 유동적으로 서버를 늘릴 수 있다. 분산 시스템의 장점은 다음과 같다.

  • 단일 시스템보다 더 높은 성능을 얻을 수 있다
  • 분산 시스템 중 하나의 서버 또는 노드 등이 장애가 발생하면 다른 서버 또는 노드가 대신 처리한다
  • 시스템 확장이 요이하다

분산 시스템에 대한 상세 내용은 분산시스템을 참고하자.

1.2 페이지 캐시

OS는 물리적 메모리에서 애플리케이션에 할당하고 남은 메모리 일부를 페이지 캐시로 사용해 OS의 전체적인 성능을 향상시킨다. 이런 방식을 사용하면 디스크 I/O를 하는 대신 메모리에서 I/O를 하기에 속도가 훨씬 빠르다. 카프카는 페이지 캐시를 사용한다. 때문에 디스크 중에서 가장 저렴한 SATA를 사용해도 무방하다.

1.3 배치 전송 처리

서버-클라이언트 또는 서버 내부적으로 데이터를 주고받을 때 I/O가 발생한다. 작은 I/O들이 많이 발생하면 네트워크 왕복에서 많은 시간이 소요된다. 카프카는 작은 I/O들을 묶어서 처리할 수 있게 배치 작업을 진행하기에 작업 속도가 향상된다.

2. 카프카 데이터 모델

카프카가 고성능, 고가용성 메시징 애플리케이션으로 발전하는 데 토픽과 파티션이 큰 역할을 했다.

  • 토픽: 메시지를 받을 수 있게 논리적으로 묶은 개념
  • 파티션: 토픽을 구성하는 데이터 저장소로, 수평 확장이 가능한 단위이다.

2.1 토픽의 이해

카프카 클러스터는 토픽(topic)이라는 곳에 데이터를 저장한다. 토픽은 데이터를 구분하기 위해 사용되며 토픽 이름은 249자 미만 영문, 숫자, ‘.’, ‘-’. ‘_’를 사용해 만들 수 있다.

2.2 파티션의 이해

파티션은 토픽을 분할한 것을 의미한다. 즉, 하나의 토픽은 여러 개의 파티션으로 이루어져 있다. 메시징 큐 시스템에서는 메시지 순서가 보장되어야 한다. 때문에 메시지 처리가 완료된 후 다음 메시지를 처리해야 한다. 따라서 효율적으로 메시지를 전송하고 속도를 높이기 위해 하나의 토픽에 대해 파티션을 늘려야한다. 그렇게 하면 여러 프로듀서가 서로 다른 파티션에게 같은 토픽과 관련된 메시지를 보낼 수 있다.

kafka partition

필요하다면 토픽의 파티션 수를 늘리는 것이 좋지만, 무분별하게 늘리는 것 역시 좋지 않다. 파티션 수의 증가가 미칠 수 있는 영향은 다음과 같다.

  • 파일 핸들러의 낭비
    • 각 파티션은 브로커의 디렉토리와 매핑되고, 저장되는 데이터마다 2개의 파일(인덱스와 실제 데이터)이 있다. 카프카에서 모든 데렉터리의 파일들에 대해 파일 핸들을 열게 된다. 결국 파티션 수가 많아지면 파일 핸들 수 역시 많아져 리소스를 낭비하게 된다.
  • 장애 복구 시간 증가
    • 카프카는 고가용성을 위해 레플리케이션을 지원한다. 카프카는 여러 개의 브로커로 이루어져 있고, 토픽은 여려 개의 파티션으로 이루어져 있다. 여기서, 토픽 하나에 대한 여러 개의 파티션은 서로 다른 브로커에 존재하게 된다. 이때, 동일 토픽의 파티션 하나는 리더이고 나머지는 팔로워이다. 토픽으로 통하는 모든 데이터에 대한 읽기, 쓰기는 오직 리더를 통해서만 이루어진다.

kafka topic, partion structure

  • 이 상황에서 브로커가 다운된다면, 리더 파티션을 사용할 수 없기에 카프카는 리더를 팔로워 중 하나로 이동시킨다. 이런 장애 처리는 컨트롤러로 지정된 브로커가 수행한다. 컨트롤러가 다운된다면 나머지 브로커 중 하나가 컨트롤러 역할을 대신한다.
  • 위와 같은 리더 - 팔로워 간의 이동, 컨트롤러 브로커 재선정에는 당연하겠지만 시간이 소요된다. 만약 컨트롤러가 각 파티션 별로 새로운 리더 선출에 5ms가 소요되고, 1,000개의 파티션에 대해 리더를 선출해야 한다면 소요되는 시간은 5초 동안 장애가 발생하는 것이다(리더가 동작하기 전까지는 읽기, 쓰기가 안되므로).

이렇듯 파티션 수를 무작정 늘리는 것이 좋은 해결책은 아니다. 적절한 값으로 설정해서 운영해야 한다.

파티션은 늘리는 것은 가능하지만 토픽을 삭제하지 않는 이상 파티션을 줄일 수는 없다. 때문에 적절한 파티션 수를 측정하기 어렵다면 적은 수부터 시작해서 병목이 생길 때마다 점증적으로 늘려나가는 것이 좋다.

2.3 오프셋과 메시지 순서

각 파티션에서 메시지가 저장되는 위치를 오프셋으로 표기한다. 오프셋은 순차적으로 증가하는 64비트 숫자이며 하나의 파티션 내에서 고유하다. 컨슈머가 하나의 파티션에서 0번 오프셋 데이터를 가져갔다면, 그다음 오프셋을 가진 데이터만 해당 파티션에서 가져갈 수 있다. 즉, 0, 1, 2, 3 … 처럼 순차적으로만 오프셋 순서를 지켜서 가져할 수 있다.

3. 카프카의 고가용성과 리플리케이션

카프카는 물리적 장애가 발생하는 경우에도 고가용성을 유지하기 위해 토픽 내의 파티션을 레플리케이션(replication)한다.

3.1 레플리케이션 factor, leader, follower 역할

카프카에서는 레플리케이션 펙터라는 것으로 파티션의 복제본을 몇 개 만들지 설정할 수 있다(레플리케이션 팩터 수만큼 브로커가 필요하다). 기본은 1이며, 변경하고 싶다면 “/kafka/config/server.properties” 설정 파일에서 변경할 수 있다.

1
2
3
// 기본값은 1 이다. 해당 항목이 설정 파일에 없다면 기본값이 적용되어 있다.
// 변경을 위해선 설정 파일 맨 아래에 아래 코드를 추가하면 된다. 
default.replication.factor = 2

카프카는 레플리케이션된 파티션과 원본 파티션을 구분하기 위해 서로 다른 용어로 파티션들을 지칭한다. 원본 파티션을 리더, 복제된 파티션을 팔로워라고 한다. 모든 읽기, 쓰기는 리더를 통해서만 이루어지지만 리더와 팔로워는 저장된 데이터의 순서까지 일치하고, 동일한 오프셋과 메시지를 갖는다. 팔로워는 주기적으로 자신에게 없는 데이터를 리더로부터 가져온다. 또 한, 브로커가 정상 응답을 할 수 없을 때를 대비해서 리더와 팔로워는 서로 다른 브로커에 존재한다. 만약 이 상황에서 리더가 다운되면, 팔로워 중 하나가 리더가 되어 응답을 이어나간다.

kafka replication

레플리케이션은 고가용성 유지를 위해 필요하지만 다음과 같은 단점도 존재한다. 때문에 데이터 중요도에 따라 팩터 개수를 조절하는 것이 중요하다.

  • 팔로워 수가 늘어날수록 필요한 디스크 용량도 커진다
  • 완벽한 레플리케이션을 위해 파티션의 상태 체크 등을 이유로 브로커의 리소스를 계속 사용한다(리소스 사용량 증가)

3.2 리더와 팔로워의 관리

위에서 언급했듯 팔로워는 주기적으로 리더에게서 자신에게 없는 데이터를 가져온다. 때문에 팔로워에 문제가 있어서 리더로부터 데이터를 가져오지 못해서 데이터 정합성이 맞지 않을 수 있다. 이는 팔로워가 리더로 승격했을 때 큰 문제가 된다. 이 문제를 방지하기 위해 카프카는 ISR(In Sync Replica)라는 개념을 사용한다.

ISR은 현재 레플리케이션이 되고 있는 그룹이다. ISR을 사용한다면, ISR에 속해 있는 구성원만이 리더의 자격을 가질 수 있다. 이를 통해 레플리케이션의 신뢰성을 높인다. ISR의 동작은 다음과 같다.

  • 리더는 기존과 같이 프로듀서로부터 온 메시지의 읽기, 쓰기를 한다.
  • 팔로워는 매우 짧은 주기로 리더에 새로운 메시지가 왔는지를 확인한다.
  • 리더는 팔로워들이 주기적으로 데이터를 확인하고 있는지를 확인한다. 이때, 일정 주기(replica.lag.time.max.ms) 만큼 확인 요청이 오지 않으면, 리더는 팔로워의 이상을 감지하고, ISR 그룹에서 추방한다. 추방된 팔로워는 리더가 되지 못한다.

4. 모든 브로커가 다운된다면

카프카를 운영하면서 마주칠 수 있는 가장 최악의 상황은 모든 브로커가 다운되는 것이다. 이때, 선택할 수 있는 방법은 두 가지가 있다.

  1. 마지막에 다운된 리더가 살아나길 기다린다.
    1. 제일 마지막에 다운된 리더를 우선적으로 살리기에 메시지 손실이 없다. 다만, 알 수 없는 이유로 마지막 리더였던 브로커가 살아나지 않아 장애 시간이 길어질 수 있다.
  2. ISR에서 추방되었지만 먼저 살아나면 자동으로 리더가 된다.
    1. 먼저 다운된 브로커는 그보다 나중에 다운된 브로커에 비해 최신 메시지를 가지고 있지 않기에 메시지 손실이 발생한다. 브로커들이 정상화될 때, 이미 리더가 존재하면, 그 리더가 가진 데이터를 기준으로 동기화 작업을 진행하기 때문이다.

카프카 0.11.0.0 이하에서는 위 방법 중 2번을 기본으로 사용했다. 지금은 1번을 기본으로 사용한다. 이 설정을 바꾸고 싶다면 “/kafka/config/server.properties”에서 다음 옵션을 변경하면 된다.

출처 - 카프카, 데이터플랫폼의 최강자