본문 바로가기
Cloud/MSA

카프카 니 도대체 뭔데

by 민휘 2023. 7. 1.

MSA 실습을 듣다가 카프카가 너무 어려워서 데브원영 님의 카프카 재생목록을 추가로 듣고 요약해보았다.

데브원영님 감사합니다 🥹

 

📝강의 - 아파치 카프카

 

📝강의 - 아파치 카프카

빅데이터에서 가장 많이 쓰이는 아파치 카프카에 대한 강의입니다. [아파치 카프카 애플리케이션 프로그래밍 with 자바] 책 구매하러가기 👉http://www.yes24.com/Product/Goods/99122569

www.youtube.com

 

목차

- What is Apache Kafka?

- Topic

- Producer

- 고가용성

- Consumer

- Consumer Lag

- Partitioner

- Kafka Streams

- Kafka Connect

 

What is Apache Kakfa?

 

  • 소스 애플리케이션과 타깃 애플리케이션의 일대일 연결이 매우 복잡해짐
  • 데이터 전송 시 프로토콜 파편화 심각

  • 소스 애플리케이션과 타겟 애플리케이션을 커플링을 약하게!
  • 소스 애플리케이션은 아파치 카프카의 데이터를 전송
  • 타겟 애플리케이션은 카프카에서 데이터를 가져옴

 

  • 소스 애플리케이션에서 보내는 데이터 포맷 타입에 제한이 거의 없음
  • 토픽 : 큐. 생산자가 큐에 데이터를 넣고, 컨슈머가 큐에서 데이터를 가져감.
  • 생산자와 컨슈머는 라이브러리로 제공되므로 애플리케이션에서 구현 가능함.
  • 고가용성 : 서버가 죽는 상황에도 데이터를 손실없이 복구 가능
  • 낮은 지연과 높은 처리량으로 대용량 데이터 처리

 

Topic

 

  • 토픽 : 다양한 포맷의 데이터가 들어가는 공간.
  • 생산자가 큐에 데이터를 넣고, 컨슈머가 큐에서 데이터를 가져감.
  • 목적에 따라 이름을 지정해서 추후 유지보수 시 편리하게 관리할 수 있음.

 

  • 하나의 토픽은 여러개의 파티션으로 구성될 수 있다.
  • 우선 하나의 파티션이 있고, 생산자와 소비자가 하나씩 붙은 경우를 보자. 생산자가 토픽에 발행한 데이터는 큐에 쌓이고, 컨슈머는 오래된 순으로 데이터를 가져간다.
  • 이때 컨슈머가 데이터를 가져가도라도 데이터가 큐에서 삭제되지 않는다!
  • 컨슈머가 새로 붙으면 기존에 큐에 있던 데이터를 가져간다. 단, 컨슈머 그룹이 달라야하고 auto.offset.reset=earlier 옵션이 있어야 한다.
  • 이처럼 동일한 데이터를 다른 컨슈머가 재사용할 수 있는데, 이는 카프카를 사용하는 아주 중요한 이유이다!

 

  • 파티션이 두개인 경우를 알아보자.
  • 생산자에서 데이터를 발행할 때, 키가 null이면 기본 파티셔너를 사용하여 라운드 로빈으로 할당한다. 만약 키가 있고 기본 파티셔너를 사용한다면, 키의 해시를 구해서 특정 파티션에 할당한다.

  • 파티션은 늘릴 수는 있지만 줄일 수는 없다! 파티션을 늘릴 때는 주의해야한다.
  • 파티션 늘리는 이유 : 파티션을 늘리면 컨슈머 개수를 늘려서 데이터 처리를 분산시킬 수 있다.
  • 파티션의 레코드 삭제 타이밍 : 레코드가 보존되는 시간과 크기를 지정할 수 있다.

 

Producer

  • 토픽에 해당하는 메시지 생성
  • 특정 토픽으로 데이터 publish
  • 처리 실패 시 재시도

  • 라이브러리 추가
  • 카프카 브로커와 클라이언트의 호환성이 안 맞을 수도 있으므로 버전 확인해서 사용할 것

  • Properties configs : 프로듀서 설정. 부트스트랩 서버 - 로컬 호스트의 카프카 서버, 카프카 브로커 주소 목록, key-value 직렬화 설정
  • KakaProducer : 카프카 프로듀서 인스턴스. 레코드(데이터)를 전송한다.
  • ProducerRecord : 전송할 데이터 객체. click_log 토픽, key 없이 login이라는 value 전송

  • key가 null이고, 파티션이 하나라면 : 큐에 차곡차곡 쌓임
  • key가 null이고, 파티션이 두개라면 : 라운드 로빈 방식으로 쌓임

  • key가 존재하고, 파티션이 두개일 때 : 하나의 파티션에 동일한 레코드가 쌓임
  • 이때 파티션이 늘어난다면? : 키와 파티션의 일관성이 깨진다. 추후에 생성하면 안된다!

 

고가용성

 

  • 카프카가 설치된 서버 단위
  • 세개 이상의 브로커로 구성하는 것을 권장함
  • 파티션이 한개이고 replica가 1인 토픽이 있다면, 브로커 3대 중 1대에 토픽 데이터가 저장된다.

  • 원본 파티션 한개, 레플리카 두개로 총 세개 존재
  • 레플리카 수는 브로커 개수보다 적어야한다.

  • 원본 파티션을 leader 파티션, 레플리카의 파티션을 follower 파티션이라고 한다.
  • leader와 follwer를 합쳐서 ISR, 즉 In Sync Replica라고 부른다.

  • leader 파티션이 있던 브로커에 장애가 발생하여 데이터가 날아가더라도, 다른 follower 파티션이 데이터가 남아있으므로 데이터 유지가 가능하다.
  • leader가 죽은 경우, 다른 follower 파티션이 leader 역할을 승계한다.

  • leader : producer가 생산한 데이터를 보내는 브로커의 파키션
  • ack=0 : 프로듀서가 브로커에 데이터를 전송하고, 응답값을 받지 않는다. leader에 데이터가 잘 전달됐는지, 복제가 됐는지 확인 불가능하다. 속도는 빠르지만 데이터 유실 가능성이 있다.
  • ack=1 : leader에 데이터가 잘 전달됐는지 응답하지만, 복제가 됐는지 확인 불가능하다. 만약 리더가 데이터를 받은 직후 장애가 난다면 복제가 잘 됐는지 알 수 없으므로 데이터 유실 가능성이 있다.
  • ack=all : leader에 데이터가 잘 전달됐는지, 복제가 됐는지 확인한다. 데이터 유실은 없지만, 복제가 될 때까지 기다려야하므로 시간이 오래 걸린다.

  • replica 수가 많아지면 고가용성이 높아진다.
  • replica 수가 많아지면 그만큼 중복되는 데이터가 많아지고, 브로커 리소스 사용량이 많아진다.
  • 따라서 카프카에 들어오는 데이터량과 저장시간을 고려해서 레플리카 수를 정해야한다.

 

Consumer

 

다른 메시징 서버와의 차이

  • 다른 메시징 서버는 컨슈머가 데이터를 가져오면 그 데이터가 큐에서 삭제된다.
  • 하지만 카프카는 컨슈머가 데이터를 가져와도 토픽에 데이터가 남아있다.
  • 이러한 특징은 카프카를 데이터 파이프라인으로 운영하는데 매우 중요하다.

  • 폴링 : 컨슈머가 토픽 내부 파티션에 저장된 데이터를 가져온다.
  • 컨슈머 역할 : 폴링 및 데이터 처리, 파티션의 데이터 번호인 offset 위치 기록, Consumer group으로 병렬 처리

  • 생산자와 마찬가지로, 브로커와 클라이언트 호환성 확인!

  • Properties : 컨슈머 옵션, 부트 스트랩 서버, 컨슈머 그룹 아이디! key value 직렬화
  • KafkaConsumer : 컨슈머 인스턴스로 데이터를 읽고 처리한다.
  • consumer.subscribe(토픽 이름들) : 특정 토픽의 데이터를 가져온다
  • comsumer.assign(topicPartition) : 특정 토픽의 특정 파티션에 있는 데이터를 가져온다
  • poll loop : 브로커로부터 연속적으로, 컨슈머가 허락하는 만큼 데이터를 가져온다. poll에서 설정한 시간만큼 데이터를 가져온다. 데이터는 ConsumerRecord로 발라낸다. 처리에 하둡이나 엘라스틱 서치에 데이터를 넣는 코드를 넣기도 한다.

  • offset : 파티션 내에서 데이터가 가지는 고유한 번호. 토픽별로 파티션별로 별개로 지정된다. 컨슈머가 데이터를 어느 지점까지 읽었는지 확인하는 용도로 사용한다.
  • __consumer_offsets 토픽 : 컨슈머가 데이터를 읽기 시작하면 오프셋을 커밋한다. 커밋한 오프셋은 이 토픽에 저장된다.
  • 고가용성 : 만약 컨슈머가 죽더라도 오프셋 정보가 토픽에 남아있으므로, 컨슈머를 재실행하더라도 시작위치부터 다시 복구하여 실행할 수 있다.

  • 파티션이 두개있는 상황
  • 컨슈머 1개 : 두개의 파티션에서 가져감
  • 컨슈머 2개 : 각 컨슈머가 각각의 파티션에서 가져감
  • 컨슈머 3개 : 세번째 컨슈머는 할당받을 파티션이 없으므로 동작하지 않음.
  • 여러 파티션을 가진 토픽으로 컨슈머를 병렬처리 하고 싶다면, 컨슈머 개수는 파티션 개수보다 적거나 같아야 한다.

  • 각기 다른 컨슈머 그룹에 속하는 컨슈머들은 다른 컨슈머 그룹에 영향을 미치지 않는다. 컨슈머 그룹은 다른 컨슈머 그룹과 함께 토픽을 동시에 읽을 수 있다. (병렬 처리)
  • 데이터 시각화 및 분석을 위해 엘라스틱에 데이터를 전달하는 컨슈머 그룹과, 데이터 백업 용도로 하둡에 데이터를 저장하는 컨슈머 그룹이 있다고 가정하자.
  • __consumer_offsets 토픽은 컨슈머 그룹별로 offset을 나누어 저장하기 때문에, 엘라스틱 서치 그룹이 특정 오프셋을 읽고 있어도 하둡 그룹이 데이터를 읽는데 영향을 미치지 않는다.

 

Consumer Lag

  • Consumer lag : 프로듀서가 데이터를 넣는 속도가 컨슈머가 가져가는 속도보다 빨라진다면, 두 오프셋 간에 차이가 발생한다.
  • 모니터링의 필요성 : lag의 숫자를 통해 현재 해당 토픽에 대해 파이프라인으로 연계된 프로듀서와 컨슈머의 상태를 알 수 있다. 주로 컨슈머의 상태를 본다.
  • 토픽에 여러 파티션이 존재하는 경우, 컨슈머 그룹에 대해 lag가 여러개 존재한다.

  • 컨슈머 단위에서 lag을 모니터링하는 것은 아주 위험하고 운영요소가 많이 들어간다. 왜냐면 컨슈머 상태에 디펜던시가 걸리기 때문이다. 컨슈머가 비정상 종료되면 더이상 lag 정보를 보낼 수 없으므로 lag를 측정할 수 없다. 그리고 컨슈머를 추가 개발할 때마다 lag를 수집하는 로직을 개발해야한다.
  • 오픈소스 Burrow : 컨슈머 lag 모니터링을 도와주는 독립적인 애플리케이션. 클러스터 외부에 존재한다. 멀티 카프카 클러스터를 지원하고, 슬라이딩 윈도우로 컨슈머의 상태를 표현하여 운영할 수 있고, HTTP Api를 통해 정보를 제공받을 수 있다.

 

Partitioner

  • 프로듀서가 데이터를 보내면 무조건 파티셔너를 거쳐 브로커로 데이터가 전송된다. 파티셔너는 데이터를 토픽에 어떤 파티션으로 보낼지 결정한다. 레코드에 포함된 메시지 키 또는 메시지 값에 따라서 파티션의 위치가 결정된다.

  • 디폴트 파티셔너는 UniformStickyPartitioner이다. 메시지 키가 있을 때는 해시값으로 변환하여 파티션에 할당한다.
  • 동일한 키를 가진 레코드는 동일한 파티션으로 들어가기 때문에 순서를 지켜 데이터를 처리할 수 있다.
  • 예를 들어 서울의 연속적인 온도 측정 값을 “서울” 키로 넣고 레코드를 지속적으로 보내면, 항상 동일한 파티션에 데이터가 순서대로 들어가기 때문에(큐) 컨슈머가 서울이라는 데이터를 순서를 지켜 처리할 수 있다.
  • 메시지 키가 없으면 라운드 로빈으로 파티션에 들어간다. 이때 배치 단위로 데이터를 보내도 라운드 로빈으로 들어간다. 즉, 적절히 분배된다.
  • 직접 개발한 커스텀 파티셔너를 사용할 수도 있다.
  • 카프카가 제공하는 Partitioner 인터페이스를 구현하여 커스텀 파티셔너를 만들면, 메시지 키 또는 메시지 값 또는 토픽 이름으로 어느 파티션에 데이터를 넣을 것인지 지정할 수 있다.
  • 예를 들어 VIP 고객의 데이터를 빠르게 처리하고 싶다면, 10개의 파티션 중 8개의 파티션이 VIP 고객의 데이터를 처리하도록 파티셔너를 구현하면 된다. 마치 AMQP 메시징 시스템에서 우선순위 큐를 구현하는 것과 비슷하다.

 

Kafka Streams

 

카프카 스트림?

  • 실시간으로 끊임없이 발생하는 데이터를 처리하기 위한 프레임워크.
  • 토픽에 있는 데이터를 낮은 지연과 함께 빠른 속도로 처리할 수 있다.
  • 이벤트 처리 기반 애플리케이션을 만들 수 있다!

 

카프카 스트림즈의 장점

  • 카프카와 완벽 호환된다. 대부분의 기업에서는 카프카를 이벤트 저장소로 사용하고, 저장된 데이터를 스파크 또는 로그 스태시와 같은 툴로 연동한다. 이런 외부 오픈소스 툴은 카프카와 잘 호환되지 않는다. 데이터 유실이나 중복 처리 없이 안전하고 빠르게 처리할 수 있다.
  • 스케줄링 도구가 필요하지 않다. 스트림즈 애플리케이션을 개발해서 배포만하면 된다. 스파크 스트리밍처럼 클러스터 관리자나 대규모 장비가 필요하지 않다.
  • 스트림즈 DSL과 프로세서 API를 제공한다. 이벤트 기반 데이터 처리에 필요한 메소드를 제공하므로 편리하다. 스트림즈 DSL에 필요한 로직이 없다면 프로세서 API를 사용하면 된다. 대부분은 스트림즈 DSL로 해결 가능하다.
  • 로컬 상태 저장소를 사용한다. 실시간으로 들어오는 데이터를 처리하는 방식은 비상태기반 처리와 상태기반 처리가 있다. 비상태기반 처리는 필터링이나 데이터를 변환하는데, 들어오는 데이터를 바로 사용하면 되므로 유실이나 중복이 발생할 가능성이 적다. 하지만 상태 기반 처리는 직접 구현하려면 엄청 어렵다. 이전 데이터를 프로세스가 메모리에 저장하고, 다음 데이터를 참조해서 처리해야하기 때문이다. 스트림즈는 어려운 처리를 돕기 위해 로컬에 상태를 저장하고, 상태에 대한 변환 정보는 카프카의 변경 로그 토픽에 저장한다. 프로세스에 장애가 발생하더라도 상태가 저장되므로 장애 복구가 가능하다.

 

Kafka Connect

 

카프카 커넥트

  • 반복적인 데이터 파이프라인을 효과적으로 배포하고 관리하는 방법을 제공하는 플랫폼이다.
  • 카프카의 공식 컴포넌트.

 

커넥터와 커넥트

  • 커넥트 : 커넥터가 동작하도록 실행하는 프로세스.
  • 커넥터 : 실질적으로 데이터를 처리하는 코드가 담긴 jar 패키지이다. 일련의 템플릿 같이 특정 동작을 하는 코드 뭉치.
  • 토픽에서 오라클 DB에 데이터를 저장하고 싶다면, 커넥터에 insert 메소드를 구현하고 커넥터를 실행한다.

 

커넥터

  • 소스 커넥터 : DB로부터 데이터를 가져와서 토픽에 넣는 역할, 즉 프로듀서 역할.
  • 싱크 커넥터 : 특정 토픽의 데이터를 오라클, mySQL, ES와 같은 특정 저장소에 저장하는 역할. 컨슈머와 비슷한 역할.

 

커넥트

  • 단일 실행 모드 커넥트 : 간단한 데이터 파이프라인이나 개발용으로 구성
  • 분산 모드 커넥트 : 실제 운영. 여러개의 카프카 커넥트 프로세스를 한개의 클러스터로 묶어서 운영. 고가용성 추구.

 

커넥터와 커넥트의 관계

  • 커넥트를 실행할 때 커넥터의 위치를 config 파일에 지정한다.
  • 커넥트를 실행하면 커넥터를 모아서 커넥트를 실행할 수 있도록 준비 상태로 만든다.
  • 커넥트에서 커넥터를 실행하려면 REST API로 요청을 보낼 수 있다.

 

파이프라인 생성

  • 커넥트를 활용하면 파이프라인을 만들 때 추가 개발과 배포, 모니터링 없이, REST API를 통해 json 설정값을 보내면 터넥트에 파이프라인이 생성된다!
  • 예를 들어 오라클 db에 데이터를 저장하는 OracleSinkConnector가 있다. 특정 토픽의 데이터를 특정 테이블로 보낼 때 설정값을 json으로 적어서 커넥트에 해당 설정을 가진 파이프라인(커넥터)을 만들어달라고 요청을 보낸다.

  • 반복적인 데이터 파이프라인이 필요하다면, 일일이 컨슈머를 만드는 것보다는 커넥트를 구축해서 반복적으로 커넥트를 실행하는 편이 낫다. json 템플릿을 사용하기 때문에 빠르게 구현이 가능하다.