[Infra] 아파치 카프카(kafka) 개념

업데이트:

아파치 카프카(kafka)의 개념

1. 아파치 카프카의 탄생

여러분들은 링크드인이라는 회사를 아시나요? 링크드인은 비즈니스 소셜 네트워크 서비스인데, 취준생이나 직장인 분들이라면 잘 아실 겁니다. 아파치 카프카는 링크드인의 데이터 통합 문제를 해결하기 위해 2010년에 개발되어 2011년에 출시되었습니다. 그리고 2012년에는 아파치 재단의 인큐베이션 프로젝트로 시작했습니다. 데이터는 단순히 트랜잭션의 기록에 머물지 않고, 페이지 전환 데이터 및 운용에 관한 정보, 애플리케이션 로그 등 비즈니스 관련 지표도 포함되어 있습니다. 링크드인에서는 웹사이트에서 생성되는 로그를 처리해 사용자의 웹사이트 활동을 추적하는 것을 목적으로 개발되었습니다. 이를 위해 사용자의 페이지뷰, 검색시 키워드 등을 수집해 서비스를 개선하는 것이 목적이었습니다. 링크드인에서는 바로 이런 데이터를 모아 실시간으로 이용자에게 제공할 수 있는 시스템이 필요했지만 전통적인 메시징 시스템으로는 어려웠습니다. 왜냐하면 전통적인 메시징 시스템의 경우 대부분 트랜잭션 데이터를 처리하도록 설계되어 있었기 때문입니다.
이를 가능하게 만든것이 카프카인데, 카프카는 대규모 메시징 시스템을 구현하기 위한 오픈소스 소프트웨어로 인기입니다.

2. 아파치 카프카

아파치 카프카(Apache Kafka)는 여러대의 분산 서버에서 대량의 데이터를 처리하는 분산 메시징 시스템입니다. 메시지(데이터)를 받고, 받은 메시지를 다른 시스템이나 장치에 보내기 위해 사용합니다. 따라서 카프카는 여러 시스템을 연결하는 중요한 역할을 합니다. 또한 카프카는 대량의 데이터를 높은 처리량(high horoughput)과 실시간으로 처리하기 위한 소프트웨어 입니다.

3. 카프카 구조

카프카로부터 요구하는 사항은 다음과 같습니다.

(1) 높은 처리량으로 실시간 처리한다. (2) 임의의 타이밍에 데이터를 읽는다. (3) 다양한 제품과시스템에 쉽게 연동한다.

위와 같은 요구사항을 만족하기 위해 카프카에서는 메시징 모델을 사용하는데, 메시징 모델은 다음과 같은 세 가지 요소로 구성됩니다.

  • Producer: 메시지 생산자, 카프카에 데이터를 만들어서 주는 역할
  • Broker: 메시지 수집/전달 역할
  • Consumer: 메시지 소비자, 카프카에서 데이터를 빼내서 소비하는 역할
  • zookeepker: 카프카의 정상 동작을 보장하기 위해 메타데이터를 관리하는 코디네이터, 노드 관리, 토픽관리, 컨트롤러 관리등 중요한 역할을 함. 2022년 초 무렵부터는 주키퍼가 삭제된 카프카 버전 릴리스.

그리고 다음과 같은 것들도 있습니다.

  • Topic: 데이터를 최종적으로 저장하는 곳, 관계형 데이터베이스로치면 테이블에 해당.
  • replication: 메시지들을 여러개로 복제해서 카프카 클러스터 내 브러크들에 분산시키는 동작을 의미.
  • partition: 하나의 토픽이 한번에 처리할 수 있는 한계를 높이기 위해 토픽 하나를 여러개로 나눠 병렬처리가 가능하게 만든 것
  • segment: 프로듀서에 의해 보로커로 전송된 메시지는 토픽의 파티션에 저장되며, 각 메시지들은 세그먼트라는 로그 파일의 형태로 브로커의 로컬 디스크에 저장됨.
  • 카프카 > 토픽 > 파티션 > 세그먼트

4. 카프카 내부 동작 원리

4.1. 카프카 레플리케이션

4.1.1 카프카 리플리케이션 개요

카프카 클러스터는 데이터 파이프라인에서 가장 중앙에서 허브 역할을 합니다. 만약 카프카 클러스터가 하드웨어 문제나 점검 등으로 정상적인 동작을 못한다면 카프카와 연결된 전체 파이프 라인에 영향을 미칠수 있고 이는 매우 심각한 문제로 이어질 수 있습니다. 따라서 카프카는 초기 설계 단계부터 일시적인 하드웨어 이슈등으로 브로커 한 두대에서 장애가 발생하더라도 중앙 데이터 허브로서 안정적인 서비스가 운영될 수 있도록 구상되었습니다. 이를 위해 토픽 생성 시 필수값으로 replication factor라는 옵션을 설정해야 합니다.

카프카 내부 동작 원리에서 가장 중요한 부분 중 하나는 레플리케이셔 동작입니다.
카프카 뿐만 아니라 일반적인 분산 시스템에서는 애플리케이션의 고가용성을 위해 내부적으로 레플리케이션 동작을 하게 되는데, 그러나 이는 말만큼 쉽지 않습니다.

4.2. 리더와 팔로워

토픽 상세보기 명령어를 실행하면 출력 내용 중 리더(leader)라는 부분을 볼 수 있습니다. 카프카는 내부적으로 레프레케이션들을 리더와 팔로워로 구분하고 역할을 분담합니다. 리더는 레플리케이션 중 하나가 선정되며, 모든 읽기와 쓰기는 리더를 통해서만 가능합니다. 즉, 프로듀서는 모든 레플리케이션에 메시지를 보내는 것이 아니라 리더에게만 메시지를 전소앟ㅂ니다. 또한 컨슈머도 오직 리더로부터 메시지를 가져옵니다.

그렇다면 팔로워는 뭐하는 걸까요? 팔로워는 지속적으로 파티션의 리더가 새로운 메시지를 받았는지 확인하고 새로운 메시지가 있다면 해당 메시지를 러더로부터 복제합니다.

그리고 리더에 문제가 발생할 경우를 대비해 언제든 새로운 리더가 될 준비를 합니다.

5. 카프카 프로듀서의 내부 동작 원리

카프카 클라이언트인 프로듀서의 기본 역할은 소스에 있는 메시지들을 카프카 토픽으로 전성하는 것입니다. 프로듀서가 전송하려는 메시지들은 프로듀서의 send() 메소드를 통해 시리얼라이저, 파티셔너를 거쳐 카프카로 전송됩니다.

5.1. 파티셔너

카프카의 토픽은 성능 향상을 위해 병렬 처리가 가능하도록 하기 위해 파티션으로 나뉘고, 최소 하나 또는 둘 이상의 파티션으로 구성됩니다. 그리고 프로듀서가 카프카로 전송한 메시지는 해당 토픽 내 각 파티션의 로그 세그먼트에 저장됩니다. 따라서 프로듀서는 토픽으로 메시지를 보낼 때 해당 토픽의 어느 파티션으로 메시지를 보내야할지를 결정해야하는데, 이 때 사용하는 것이 파티셔너(partitioner)입니다. 프로듀서가 파티션을 결정하는 알고리즘은 기본적으로 메시지의 키를 해시(hash)처리해 파티션을 구하는 방식을 사용합니다. 따라서 메시지의 키값이 동일하면 해당 메시지들은 모두 같은 파티션으로 전송됩니다.

5.2. 프로듀서의 배치

카프카에서는 토픽의 처리량을 높이기 위한 방법으로 토픽을 파티션으로 나눠 처리하며, 카프카 클라이언트인 프로듀서에서는 처리량을 높이기 위해 배치 전송을 권장합니다. 따라서 프로듀서에서는 카프카로 전송하기 전 배치(batch) 전송을 위해 토픽의 파티션별로 레코드들을 잠시 보관합니다.

  • buffer.memory: 카프카로 메시지 전송을 위해 담아두는 프로듀서의 버퍼 메모리 옵션, 기본값은 32MB
  • batch.size: 배치 전송을 위해 메시지 묶는 단위 설정, 기본값은 16KB
  • linger.ms: 배치 전송을 위해 버퍼 메모리에서 대기하는 메시지들의 최대 대기시간. 기본값은 0ms

프로듀서의 배치 전송 방식은 단건의 메시지를 전송하는 것이 아니라 한 번에 다량의 메시지를 묶어서 전송하는 방법입니다. 이러한 배치 전송은 불필요한 I/O를 줄일 수 있어서 매우 효율적이며, 더불어 카프카의 요청 수를 줄요주는 효과도 있습니다. 예를 들어 프로듀서가 1000개의 메시지를 카프카로 전송한다면, 1000번의 요청과 응답이 발생하지만, 프로듀서가 100개의 메시지를 배치로 처리한다면 카프카와 프로듀서에서는 단 10번의 요청과 응답만 발생하게 됩니다.

5.3. 중복없는 전송

예를 들어, 온라인 구매 사이트에서 중복 구매되면 큰일이다.

6. 카프카 컨슈머의 내부 동작 원리

컨슈머의 주요 억할은 카프카에 저장된 메시지를 가져오는 것입니다.

6.1. 컨슈머의 동작 중 가장 핵심은 바로 오프셋 관리입니다.

컨슈머는 카프카에 저장된 메시지를 꺼내오는 역할을 하기 떄문에 컨슈머가 메시지를 어디까지 가져왔는지를 표시하는 것은 매우 중요합니다. 예를 들어, 코드 배포로 인해 컨슈머가 일시적으로 동작을 멈추고 재시작 하는 경우나, 컨슈머가 구동 중인 서버에서 문제가 발생해 새로운 컨슈머가 기존 컨슈머의 역할을 대신하는 경우에는 기존 컨슈머의 마지막 메시지 위치부터 새로운 컨슈머가 메시지를 가져올 수 있어야만 장애로부터 빠르게 복구될 수 있습니다. 카프카에서는 메시지의 위치를 나타내는 위치를 오프셋(offset)이라고 부르는데, 이 오프셋은 숫자 형태로 나타냅니다.

7. 카프카 운영과 모니터링

7.1. 카프카 구성

카프카 서버 수량, 하드웨어, 배치 고려

7.2. 카프카 모니터링 시스템 구성

프로메테우스, 그라파나, 카프카 익스포터

8. 카프카 버전 업그레이드

8.1. 카프카 버전 업그레이드 준비

먼저 다음과 같은 방법으로 카프카의 현재 버전을 확인합니다.

$ /usr/local/kafka/bin/kafka-topics.sh --version

8.2. 최신버전 카프카 다운로드

카프카 다운로드는 모든 브로커에서 진행해야합니다.

$ cd /usr/local/
$ ll

lrwxrwxrwx 1 root root 27 1월 01 00:08 kafka -> /usr/local/kafka_2.12-2.1.0
lrwxrwxrwx 1 root root 27 1월 01 00:08 kafka_2.12-2.1.0
lrwxrwxrwx 1 root root 27 1월 01 00:08 kafka_2.12-2.6.0

현재 디렉토리에는 kafka_2.12-2.10으로 링크가 걸려있습니다. 이후 2.6버전으로 업그레이드시 kafka 디렉토리의 링크만 2.6으로 변경 예정입니다.

8.3. 설정

현재 운영중인 2.1 버전의 카프카 설정과 새롭게 설치한 2.6 버전의 카프카 설정이 일치해야하므로 2.1 버전의 설정 파일을 2.6 버전 설정 파일 경로로 복사합니다.

$ sudo cp kafka_2.12-2.1.0/config/server.properties kafka_2.12-2.6.0/config/server.properties

8.4. 브로커 버전 업그레이드

브로커 버전 업그레이드는 한대씩 순차적으로 합니다. 먼저 peter-kafka01에 접속한 후 브로커를 종료합니다. 브로커 한대를 종료하면 종료된 브로커가 갖고 있던 파티션의 리더들이 다른 브로커로 변경됩니다. 따라서 카프카 클러스타가 현재 운영 중인 상태라면 클라이언트는 일시적으로 리더를 차지 못하는 에러가 발생하거나 타임아웃 등이 발생하게 됩니다. 이는 장애 상황이 아닌 당연한 현상으로서, 카프카 클라이언트 내부적으로 재시도 로직이 있으므로 모든 클라이언트는 변경된 새로운 리더가 있는 브로커를 바라보게 될 것입니다.

그리고나서 카프카를 종료합니다.

$ cd /usr/local/
$ sudo systemctl stop kafka-server

그리고나서 2.1버전으로 연결되어 있는 심볼릭 링크를 2.6 버전으로 변경합니다.

$ sudo rm -rf kafka
$ sudo ln -sf kafka_2.12-2.6.0 kafka
$ ll

그리고나서 브로커를 실행합니다.

$ sudo systemctl start kafka-server

위와 같은 방법으로 2번 브로커의 업그레이드 작업을 진행합니다. 순서를 정리하면 다음과 같습니다.

  1. 2번 브로커 접속
  2. /usr/local 경로로 이동
  3. 브로커 종료
  4. kafka 링크 삭제
  5. kafka 링크 2.6 버전으로 재생성
  6. 설정 파일 복사 및 옵션 설정
  7. 브로커 시작