카프카 브로커는 프로듀서로부터 전달받은 데이터를 토픽의 파티션에 저장하고, 컨슈머가 요청하면 파티션에 저장된 데이터를 전달한다. 전달받은 데이터는 파일 시스템에 저장되고, config/server.properties
에서 log.dir
옵션에 정의된 디렉토리에 저장된다.
Kafka-tutorial에서 진행했던 server를 보면 아래와 같이 나타난다. 아래를 보면 알겠지만 topic_kafka_test-0
와 같이 토픽이름과 파티션번호의 조합으로 하위 디렉토리를 생성하여 데이터를 저장한다.
$ ls /tmp/kafka-logs/
__consumer_offsets-0 ...
__consumer_offsets-17 __consumer_offsets-30 __consumer_offsets-44 replication-offset-checkpoint
__consumer_offsets-18 __consumer_offsets-31 __consumer_offsets-45 topic_kafka_test-0
__consumer_offsets-19 __consumer_offsets-32 __consumer_offsets-46 topic_kafka_test-1
__consumer_offsets-2 __consumer_offsets-33 __consumer_offsets-47 topic_kafka_test-2
__consumer_offsets-20 __consumer_offsets-34 __consumer_offsets-48 topic_kafka_test-3
__consumer_offsets-21 __consumer_offsets-35 __consumer_offsets-49 topic_kafka_test-4
각 파티션에는 아래와 같은 데이터를 확인할 수 있으며 각 파일의 내용은 아래와 같다.
log
: 메시지와 메타데이터를 저장partition.metadata
와 같이 나누어 저장하는 것으로 보임)index
: 메시지의 오프셋을 인덱싱한 정보timeindex
: 메시지에 포함된 timestamp 값을 기준으로 인덱싱한 정보$ ls /tmp/kafka-logs/topic_kafka_test-0
00000000000000000000.index 00000000000000000000.timeindex partition.metadata
00000000000000000000.log leader-epoch-checkpoint
카프카는 파일 시스템에 저장하지만 페이지 캐시를 사용하여 디스크 입출력 속도를 높여서 느린 부분을 해결하였다. 페이지 캐시는 OS에서 파일 입출력 성능 향상을 위해 만들어 놓은 메모리 영역을 의미한다. (자세한 설명은 여기를 참고)
페이지 캐시를 통해 한번 접근한 파일의 내용은 메모리의 페이지 캐시 영역에 저장하여, 동일한 파일의 접근이 일어나면 메모리에서 읽는 방식이다.
데이터 복제는 카프카를 장애 허용 시스템(fault tolerant system)으로 동작하도록 하는 원동력이다. 복제를 통해 클러스터로 묶인 브로커 중 일부에 장애가 발생하여도 데이터를 유실하지 않고 안전하게 사용하게 된다.
카프카의 데이터 복제는 파티션 단위로 이루어지고, 토픽을 생성할 때 파티션의 복제 개수도 같이 설정되는데, 선택하지 않으면 브로커에 설정된 옵션을 따른다. 복제 개수는 1~ 브로커의 개수만큼 설정 가능하다.
복제된 파티션의 경우, 리더와 팔로워로 구성되는데 프로듀서나 컨슈머와 직접적으로 통신하는 파티션을 리더, 나머지 복제 데이터를 가지고 있는 파티션을 팔로워라고 한다. 여기서 팔로우 파티션들은 리더 파티션의 오프셋과 자신의 오프셋을 비교하여 차이가 발생하는 경우 리더 파티션으로부터 데이터를 가져와서 자신의 파티션에 저장하는데, 이를 복제라고 한다.
복제 시 나머지 브로커에도 파티션의 데이터가 복제되므로 복제 개수만큼 저장 용량이 증가한다는 단점이 있으나, 데이터를 안전하게 사용할 수 있다는 장점이 있다.
리더 파티션에 장애가 발생할 겨우, 다른 팔로워 파티션 중 하나가 리더 파티션 지위를 넘겨받아 데이터가 유실되지 않고 컨슈머나 프로듀서와 데이터를 주고받을 수 있다.
If replica 1, partition 1
- broker 사망 시 복구 불가
If replica 2
- broker 사망해도 복제본에서 복구 가능
- folower broker가 leader broker 승계
한 개의 브로커는 컨트롤러 역할을 하는데, 이는 다른 브로커들의 상태를 체크하고 브로커가 클러스터에서 빠지는 경우 해당 브로커에 존재하는 리더 파티션을 재분배 한다.
컨트롤러 역할을 하는 브로커에 장애가 생기면 다른 브로커가 컨트롤러 역할을 한다.
카프카의 경우, 컨슈머가 데이터를 소비해도 토픽의 데이터는 삭제되지 않으며 컨슈머나 프로듀서가 데이터 삭제를 요청할 수도 없다. 브로커 만이 데이터 삭제할 수 있는데, 데이터 삭제는 파일 단위로 이루어지며 이것을 로그 세그먼트라고 한다. 로그 세그먼트에는 다수의 데이터가 들어가 있어, 일반적인 DB처럼 특정 데이터를 선별해 삭제 불가능하다. 브로커에 log.segment.bytes
또는 log.segment.ms
값이 주어지게 되면 세그먼트 파일이 닫히게 된다. 너무 작은 용량으로 하게 될경우 부하가 발생할 수 있으므로 주의해야 한다. log.retention.bytes
또는 log.retention.ms
옵션에 따라 닫힌 세그먼트 파일을 관리하고 이 기간/용량이 넘으면 삭제한다.
컨슈머 그룹은 토픽이 특정 파티션으로부터 데이터를 가져가서 처리하고 어느 레코드 까지 가져갔는디 확인하기 위해 오프셋을 커밋한다. 이 때 커밋한 오프셋은 _consumer_offset
토픽에 저장되고, 이를 토대로 컨슈머 그룹은 다음 레코드를 가서 처리한다.
한 대의 브로커는 코디네이터의 역할을 수행한다. 코디네이터는 컨슈머 그룹의 상태를 체크하고, 파티션을 컨슈머와 매칭되도록 분배하는 역할을 한다. 리밸런스라는 과정을 거치는데 만약 컨슈머가 컨슈머 그룹에서 빠지면 매칭되지 않은 파티션을 정상 동작하는 컨슈머로 할당하여 끊임 없이 데이터가 처리되도록 도와준다.
카프카의 메타데이터를 관리하는데 사용된다.
카프카 클러스터로 묶인 브로커들은 동일한 경로의 주키퍼 경로로 선언해야 같은 카프카 브로커 묶음이되며, 만약 클러스터를 여러 개로 운영한다면 한 개의 주피터에 다수의 카프카 클러스터를 연결해서 사용할 수 있다.
# connect zookeeper
# znode를 조회하고 수정할 수 있음
bin/zookeeper-shell.sh kafka_tutorial:2181
Connecting to kafka_tutorial:2181
Welcome to ZooKeeper!
JLine support is disabled
WATCHER::
WatchedEvent state:SyncConnected type:None path:null
ls /
get /brokers/ids/0
get /controller
__consumer_offset
은 컨슈머 오프셋을 저장하기 위한 용도로 사용되는 토픽ls /brokers/topics
Zookeeper에서 다수의 카프카 클러스터 사용 방법
주키퍼의 서로 다른 znode에 카프카 클러스터를 설정하면 된다.
znode는 주키퍼에서 사용하는 데이터 저장 단위로, 파일 시스템 처럼 znode 간에 계층 구조를 가진다.
2개 이상의 카프카 클러스터를 구축할 때 root node가 아닌 한 단계 아래의 znode를 카프카 브로커 옵션으로 지정하도록 한다. 서로 다른 하위 znode에 연결한 카프카 클러스터는 각 클러스의 데이터에 영향을 미치지 않고 정상 동작한다.