"아파치 카프카 애플리케이션 프로그래밍 with 자바"
교재의 학습 내용을 정리하였습니다
우선 시작하기에 앞서 카프카란?
카프카는 파이프라인, 스트리밍 분석, 데이터 통합 및 미션 크리티컬 애플리케이션을 위해 설계된 고성능 분산 이벤트 스트리밍 플랫폼임
=> Pub-Sub 모델의 메시지 큐 형태로 동작하며 분산환경에 특화되어 있음
카프카 브로커 · 클러스터 · 주키퍼
카프카 브로커는 카프카 클라이언트와 데이터를 주고받기 위해 사용하는 주체이자, 데이터를 분산 저장하여 장애가 발생하더라도 안전하게 사용할 수 있도록 도와주는 애플리케이션이다!
=> 하나의 서버에는 한 개의 카프카 브로커 프로세스가 실행됨
(카프카 브로커 서버 1대로도 기본 기능이 실행되지만 데이터를 안전하게 보관하고 처리하기 위해 3대 이상의 브로커 서버를 1개의 클러스터로 묶어서 운영하게 됨)
카프카 클러스터로 묶인 브로커들은 프로듀서가 보낸 데이터를 안전하게 분산 저장하고 복제하는 역할을 수행함
데이터 저장, 전송
프로듀서로부터 데이터를 전달받으면 카프카 브로커는 프로듀서가 요청한 토픽의 파티션에 데이터를 저장하고
컨슈머가 데이터를 요청하면 파티션에 저장된 데이터를 전달해줌!
=> 이 때 프로듀서로부터 전달된 데이터는 파일 시스템에 저장됨
(카프카에서 정의한 디렉토리에 데이터를 저장할 수 있음)
카프카는 메모리나 데이터베이스에 저장하지 않으며 따로 캐시메모리를 구현하여 사용하지도 않음!
그럼 이렇게 파일 시스템에 저장하면 파일 입출력으로 인해 속도 이슈가 발생하지 않을까?
당연히 파일 시스템으로 지속적인 입출력을 하게 되면 메모리에 올려서 사용하는 것보다 처리 속도가 훨씬 느림..
but, 카프카는 page cache를 사용하여 디스크 입출력 속도를 높여서 이 문제를 해결하였음
=> page cache란 OS에서 파일 입출력의 성능 향상을 위해 만들어 놓은 메모리 영역을 뜻함
(애플리케이션 레벨에서 직접 메모리를 관리하지 않고, 운영체제(OS)에서 제공하는 페이지 캐시 기능을 이용한다는 뜻)
한번 읽은 파일의 내용은 메모리의 페이지 캐시 영역에 저장시키고 추후 동일한 파일의 접근이 일어나면 디스크에서 읽지 않고 메모리에서 직접 읽는 방식이라고 생각하면 됨 (컴퓨터 구조에서 많이 다뤘던 이야기)
page cache를 사용하지 않으면 카프카에서 캐시를 직접 구현해야 했을 것이고 JVM 위에서 동작하는 카프카 브로커는 지속적으로 변경되는 데이터 때문에 가비지 컬렉션이 자주 일어나 속도가 굉장히 느려질 것임
(이러한 특징 때문에 카프카 브로커를 실행하는데 힙 메모리 사이즈를 크게 설정할 필요 X)
데이터 복제, 싱크
데이터 복제(replication)는 카프카를 장애 허용 시스템(fault tolerant system)으로 동작하도록 하는 원동력임
=> 복제의 이유는 클러스터로 묶인 브로커 중 일부에 장애가 발생하더라도 데이터를 유실하지 않고 안전하게 사용하기 위함
카프카의 데이터 복제는 파티션 단위로 이루어지는데 토픽을 생성할 때 파티션의 복제 개수(replication factor)도 같이 설정되는데 직접 옵션을 선택하지 않으면 브로커에 설정된 값을 따라감
(복제 개수의 최솟값은 1(복제 없음)이고 최댓값은 브로커 개수만큼 설정하여 사용할 수 있음)
프로듀서 또는 컨슈머와 직접 통신하는 파티션을 리더, 나머지 복제 데이터를 가지고 있는 파티션을 팔로워라고 부름
팔로워 파티션들은 리더 파티션의 오프셋을 확인하여 현재 자신이 가지고 있는 오프셋과 차이가 나는 경우 리더 파티션으로부터 데이터를 가져와서 자신의 파티션에 저장하게 됨
=> 이 과정을 복제(replication)라고 부름
파티션 복제로 인해 나머지 브로커에도 파티션의 데이터가 복제되므로 복제 개수만큼의 저장 용량이 증가한다는 담점이 있지만 복제를 통해 데이터를 안전하게 사용할 수 있다는 강력한 장점 때문에 카프카를 운영할 때 2 이상의 복제 개수를 정하는 것이 중요함!
만약 위 그림처럼 3개의 브로커로 이루어진 카프카 클러스터 중 카프카 0번에 장애가 발생한다면?
브로커가 다운되면 해당 브로커에 있는 리더 파티션은 사용할 수 없기 때문에 팔로워 파티션 중 하나가 리더 파티션 지위를 넘겨 받음!
=> 데이터가 유실되지 않고 컨슈머나 프로듀서와 데이터를 주고받도록 동작함
운영 시에는 데이터 종류마다 다른 복제 개수를 설정하고 싱황에 따라서는 토픽마다 복재 개수를 다르게 설정하여 운영하기도 함
=> 데이터가 일부 유실되어도 무관하고 데이터 처리 속도가 중요하다면 1 또는 2로 설정
=> 금융 정보와 같이 유실이 일어나면 안 되는 데이터의 경우 복제 개수를 3으로 설정히여 최대 2개의 브로커에서 동시에 장애가 발생하더라도 데이터를 안정적으로 유지할 수 있도록 함
컨트롤러(controller)
클러스터의 다수 브로커 중 한 대가 컨트롤러의 역할을 함!
=> 컨트롤러는 다른 브로커들의 상태를 체크하고 브로커가 클러스터에서 빠지는 경우 해당 브로커에 존재하는 리더 파티션을 재분배한다
(카프카는 지속적으로 데이터를 처리해야 하므로 브로커의 상태가 비정상이라면 빠르게 클러스터에서 빼내야 함!)
그럼 컨트롤러를 맡은 브로커에 장애가 생기면..?
다른 브로커가 컨트롤러 역할을 또 한다...ㅋㅋ
데이터 삭제
카프카는 다른 메시징 플랫폼과 다르게 컨슈머가 데이터를 가져가더라도 토픽의 데이터는 삭제되지 않음
(컨슈머나 프로듀서가 애초에 데이터 삭제를 요청할 수 X)
=> 오직 브로커만이 데이터를 삭제할 수 있음
우선 카프카에서 브로커에 의한 데이터 삭제는 파일 단위로 이루어지는데 이 단위를 'log segment'라고 함
이 세그먼트에는 다수의 데이터가 들어 있기 때문에 일반적인 데이터베이스처럼 특정 데이터를 선별해서 삭제할 수 X
=> 세그먼트는 데이터가 쌓이는 동안 파일 시스템으로 열려있으며 카프카 브로커에 log.segment.bytes 또는 log.segment.ms 옵션에 값이 설정되면 세그먼트 파일이 닫히게 됨
(세그먼트 파일이 닫히게 되는 기본값은 1GB 용량에 도달했을 때인데 간격을 더 줄이고 싶다면 작은 용량으로 설정하면됨)
=> 작은 용량이라면 데이터들을 저장하는 동안 세그먼트 파일을 자주 여닫음으로 부하 발생할 수 있으니 주의!
세그먼트가 닫힌다는게 무슨 소리지?
세그먼트가 닫히게되면 해당 세그먼트 파일이 더 이상 새로운 데이터를 받지 않고, 읽기 전용 상태로 변하는 것을 뜻함
하나의 파티션은 물리적으로는 여러 개의 세그먼트 파일로 나누어져 저장되는데
카프카는 데이터를 지속적으로 받아들이기 때문에, 파티션의 크기가 커질 수 있기 때문에 이를 효율적으로 관리하기 위해 파티션을 여러 개의 작은 세그먼트 파일로 분할해서 저장하는거임!
다시 한 번 세그먼트에 대해 정리하자면
세그먼트는 파티션의 일부이며, 파티션 내에서 시간 또는 크기 기준으로 여러 세그먼트 파일이 생성되는거임
=> 닫힌 세그먼트 파일은 log.retentiokn.bytes 또는 log.retention.ms 옵션에 설정값이 넘으면 삭제됨
(닫힌 세그먼트 파일을 체크하는 간격은 카프카 브로커의 옵션에 설정된 log.retention.check.interval.ms를 따름)
컨슈머 오프셋 저장
컨슈머 그룹은 토픽이 특정 파티션으로부터 데이터를 가져가서 처리하고 이 파티션이 어느 레코드까지 가져갔는지 확인하기 위해 오프셋을 커밋함!
=> 커밋한 오프셋은 _consumer_offsets 토픽에 저장
(여기에 저장된 오프셋을 토대로 컨슈머 그룹은 다음 레코드를 가져가서 처리)
코디네이터(coordinator)
컨트롤러와 같이 클러스터의 다수 브로커 중 한 대는 코디네이터의 역할을 수행함
=> 코디네이터는 컨슈머 그룹의 상태를 체크하고 파티션을 컨슈머와 매칭되도록 분배하는 역할을 함
(컨슈머가 컨슈머 그룹에서 빠지면 매칭되지 않은 파티션을 정상 동작하는 컨슈머로 할당하여 끊임없이 데이터가 처리되도록 도와줌)
=> 이렇게 파티션을 컨슈머로 재할당 하는 과정을 'rebalance' 라고 부름
주키퍼란
그렇다면 위에서도 사진으로 봤을텐데 카프카 클러스터를 운영할 때 주키퍼가 하는 역할은 무엇일까?
주키퍼는 분산 시스템을 관리하고 조율하는 데 사용되는 서비스이다!
=> 주키퍼의 데이터는 트리 구조로 저장되며, znode는 이 트리의 각 노드(데이터 저장 단위)를 의미함
(주키퍼에서 znode는 파일 시스템의 디렉터리처럼 동작하며, 데이터를 저장하거나 다른 하위 znode를 가질 수 있음)
주키퍼는 Kafka 클러스터에서 메타데이터 관리, 브로커 상태 모니터링 등을 담당하며, 카프카는 주키퍼를 통해 클러스터를 관리함
=> 간단하게 주키퍼는 카프카의 메타데이터를 관리하는 데에 사용된다고 생각하면 됨
💡 주키퍼에서 다수의 카프카 클러스터를 사용하는 방법 💡
주키퍼의 서로 다른 znode에 카프카 클러스터들을 설정하면 됨
=> root znode(최상위 znode)가 아닌 한 단계 아래의 znode를 카프카 브로커 옵션으로 지정하도록 하면
각기 다른 하위 znode로 설정된 서로 다른 카프카 클러스터는 각 클러스터의 데이터에 영향을 미치지 않고 정상 동작함!
주키퍼 옵션 정의 EX
파이프라인용 카프카 클러스터 : zookeeper.connect=localhost:2181/pipeline
실시간 추천용 카프카 클러스터 zookeeper.connect=localhost:2181/recommend
이런식으로 카프카 클러스터는 주키퍼의 서로 다른 경로(znode)에 메타데이터를 저장함으로써, 독립적인 클러스터로 동작할 수 있음!
'Data Engineering > Kafka' 카테고리의 다른 글
[Apache Kafka] 토픽과 파티션, 레코드 (2) | 2024.09.16 |
---|