728x90
이번 포스트부터 카프카에 대해서 정리를 하겠습니다. 우선 카프카가 만들어질 때 고려된 점을 디자인에 반영한 그 특징에 대해서 정리해보겠습니다.
1. 카프카 디자인 특징
- 링크드인에서 ActiveMQ를 사용하다가 사용자 증가로 한계점을 느끼고 카프카를 설계하게 됨
- 분산된 데이터 파이프라인을 통합, 처리량에 중점을 둠
- 높은 처리량과 빠른 메세지 전송, 운영 효율화 등을 위해 분산 시스템, 페이지 캐시, 배치 전송 처리 등의 기능을 구현
1) 분산 시스템
분산 시스템은 다음과 같은 장점이 있음
- 단일 시스템보다 더 높은 성능
- 분산 시스템 중 하나의 서버 또는 노드 등이 장애가 발생하면 다른 서버 또는 노드가 대신 처리함
- 시스템 확장이 용이함
2015년 기준 링크드인은 가장 사용량이 높은 클러스터의 경우 60대의 브로커로 분산 처리를 하고 있었음
2) 페이지 캐시
- OS는 물리적 메모리에 애플리케이션이 사용하는 부분을 할당하고 남은 잔여 메모리 일부를 페이지 캐시로 유지해 OS의 전체적인 성능 향상을 높이게 됨 → 잔여 메모리를 이용해 디스크에 읽고 쓰기를 하지 않고 페이지 캐시를 통해 읽고 쓰는 방식을 사용하면 처리 속도가 매우 빠르기 때문에 전체적인 성능을 향상시킬 수 있음
- 덕분에 카프카 구성할 때는 가장 저렴한 SATA 디스크를 사용해도 무방
https://en.wikipedia.org/wiki/Page_cache
JVM 힙 사이즈
1)카프카는 자바 기반의 JVM을 사용하는 애플리케이션으로서 자바 기반 애플리케이션들은 시작할 때 메모리가 할당되는 영역인 heap이 만들어짐
2) 카프카는 기본값으로 1GB의 heap memory를 사용하도록 설정되어 있고, 설정 파일에서 이 값을 변경할 수 있음.
JMX 설정 변경 방법을 참고해 KAFKA_HEAP_OPTS="Xmx6G -Xms6G"로 추가하면 됨
3) 카프카는 초당 메시지 단위, 메가비트 단위를 처리함에 있어 5GB의 힙 메모리면 충분하고, 남아 있는 메모리는 페이지 캐시로 사용하기를 권장
3) 배치 전송 처리
서버와 클라이언트 사이 또는 서버 내부적으로 데이터를 주고 받는 과정에서는 I/O가 발생하고, 작은 I/O가 빈번하게 일어나면 속도 저하의 원인(네트워크 왕복의 오버헤드)이 됨 → 작은 I/O를 묶는 배치 처리가 필요
2. 데이터 모델
1) topic의 이해
- 카프카 클러스터는 topic에 데이터를 저장하는데, 말 그대로 각 데이터 주제별로 구분하여 보관한다고 생각하면 됨(데이터를 구분하기 위한 단위)
- 249자 미만으로 영문, 숫자, ',','_','-'를 조합하여 자유롭게 이름을 지음
- 토픽 이름에 접두어로 서비스명을 추가하면 구분하기 편함
- sbs-news
- sbs-video
- kbs-news
- kbs-video
2) partition의 이해
- 카프카에서의 partition이란 토픽을 분할한 것
- 파티션 수가 증가함에 따라 병렬처리가 가능해지고, 빠른 전송이 가능해짐
- 파티션 0부터 시작함
무조건 파티션 수를 늘려야 하나?
- 파티션 수가 늘어나면 오히려 카프카에 좋지 않은 영향을 미칠 수도 있음
- 파일 핸들러의 낭비
- 각 파티션은 브로커의 디렉토리와 매핑되고, 저장되는 데이터마다 2개의 파일(인덱스와 실제 데이터)이 있음
- 모든 디렉토리의 파일들에 대해 파일 핸들을 열게 됨 → 파일 핸들 수 역시 많아지게 되어 리소스를 낭비하게 됨
- 장애 복구 시간 증가
- 각 파티션마다 리플리케이션이 동작하게 되며, 하나는 파티션의 리더이고 나머지는 파티션의 팔로워가 됨
- 브로커가 다운되면 해당 브로커에 리더가 있는 파티션은 일시적으로 사용할 수 없게 되므로, 카프카는 리더를 팔로워 중 하나로 이동시켜 클라이언트 요청을 처리할 수 있게 함, 이와 같은 장애 처리는 컨트롤러로 지정된 브로커가 수행하고 컨트롤러는 카프카 클러스터 내 하나만 존재하며, 다운 될 경우 다른 브로커가 그 역할을 대신 함 → 파티션의 리더를 선출하는데 소요되는 시간이 x partition 수 만큼 총 소요시간이 되고, 장애시간이 그만큼 길어짐
- 파일 핸들러의 낭비
내 토픽의 적절한 파티션 수는?
- 토픽의 파티션 수를 정할 때 원하는 목표 처리량의 기준을 잡아야 함
ex) 프로듀서 입장에서 4개의 프로듀서를 통해 각각 초당 10개의 메시지를 카프카의 토픽으로 보낸다고 하면, 카프카 토픽에서 초당 40개의 메시지를 받아줘야 함, 해당 토픽에서 파티션 1로 했을 때 초당 10개만 받아준다면, 파티션을 4로 늘려서 목표 처리량을 처리할 수 있도록 변경 - 카프카에서는 컨슈머도 있기 때문에 컨슈머의 입장도 고려해야 함
ex) 컨슈머 입장에서 8개의 컨슈머를 통해 각각 초당 5개의 메시지를 카프카의 토픽에서 가져올 수 있다고 한다면, 해당 토픽의 파티션 수는 컨슈머 수와 동일하게 8개로 맞추어 컨슈머마다 각각의 파티션에 접근할 수 있게 해야 함 - 파티션을 늘리는 건 가능하지만, 줄이는건 불가능하므로, 적게 시작 한 후 파티션을 늘려가는 운영을 추천(줄이려면 토픽을 삭제해야 함)
- 카프카에서는 브로커당 약 2,000개 정도의 최대 파티션 수를 권장하고 있음
3) offset과 message 순서
- offset: 각 파티션마다 메시지가 저장되는 위치, 파티션 내에서 유일하고 순차적으로 증가하는 숫자(64비트 정수)
- 컨슈머가 파티션 0에서 데이터를 가져간다고 가정하면, 오프셋을 이용해 메시지의 순서를 보장함(오프셋 0,1,2,3,4,... 의 순서대로 가져감) 그리고 절대로 오프셋 순서가 바뀐 상태로는 가져갈 수 없음(순차 처리 보장)
3. 고가용성과 리플리케이션
- 카프카는 리플리케이션 기능을 제공하여 고가용성을 보장
- 토픽 자체를 리플리케이션하는 것이 아니라, 각각의 파티션을 리플리케이션 함
1) 리플리케이션 팩터와 리더, 팔로워의 역할
- replication factor는 기본값이 1로 설정되어 있으며, server.properties 파일에서 바꿀 수 있음
- default.replication.factor 항목에서 2 또는 3 등 원하는 숫자로 변경하면 복제할 숫자를 정함
- 아무런 옵션을 주지 않고 토픽을 생성할 때 적용되는 값이고, 각 토픽별로 다른 replication factor 값을 설정할 수 있음
- 운영 중에서도 토픽의 replication factor 값은 변경할 수 있음
- 클러스터 내 모든 브로커에 동일하게 설정해야 하며, config 내용을 변경한 후 브로커 1대씩 재시작을 하면 변경 내용이 적용 됨
- 리더는 원본 토픽의 파티션, 팔로워는 replication된 토픽의 파티션(주키퍼의 리더 팔로워 용어를 따라씀)
- 리더를 통해서만 모든 읽기와 쓰기가 일어남
- 팔로워는 리더의 데이터를 그대로 repliction만 하고 읽기와 쓰기에는 관여하지 않음
- 리더와 팔로워는 저장된 데이터의 순서도 일치하고 동일한 offset과 메시지들을 갖게 됨
- kafka-topich.sh 에서 --describe 명령어를 통해 리더와 팔로워가 존재하는 브로커를 확인할 수 있음
- 토픽의 사이즈 x repliation factor 수 이므로 저장소의 크기가 replication factor 수 만큼 차지하게 됨
2) 브로커가 다운됐을 때의 카프카 동작
- 리더 재선출:
- 리더 역할을 맡고 있는 브로커가 다운되면, 주키퍼(ZooKeeper)가 이를 감지
- kafka controller는 해당 파티션의 ISR(In-Sync Replica) 리스트에서 새로운 리더를 선출
- ISR 리스트에는 가장 최근까지 리더와 동기화된 복제본(replica)들만 포함되어 있기 때문에, 이 리스트에 있는 브로커 중 하나가 새로운 리더로 선출
- Replication Factor와 새로운 팔로워:
- Replication Factor가 2라고 하면, 하나의 리더와 하나의 팔로워가 있다는 의미
- 리더가 다운된 경우, 기존 팔로워 중 하나가 리더로 선출
- 새로운 리더가 선출되더라도, replication factor가 유지되려면 새로운 팔로워를 구성해야 함
- 새로운 팔로워 선출 또한 Kafka Controller가 이 작업을 수행
- 새로운 팔로워는 ISR 리스트에 있지 않은 복제본(replica) 중에서 선택되며, 새로 선정된 팔로워는 리더와 데이터를 동기화하여 다시 ISR에 추가
zookeeper의 역할
ZooKeeper는 Kafka 컨트롤러와 브로커 간의 상태를 관리하고 메타데이터를 저장하는 중앙 저장소 역할
3) 리더와 팔로워의 관리
- ISR(In Sync Replica): 현재 replication 되고 있는 replication group
- ISR에 속해 있는 구성원만이 리더의 자격을 가질 수 있음
ex) 특정 topic이 replication factor 2로 구성되어 리더는 1번 브로커, 팔로워는 2번 브로커에 위치하고 잇따면, ISR 구성원은 1,2 이고, 브로커 1이 다운되면, ISR의 구성원인 2번 브로커에 있는 팔로워가 새로운 리더로 승격하게 됨 - 리더와 팔로워의 데이터 동기화를 유지하는 역할
- ISR에 속해 있는 구성원만이 리더의 자격을 가질 수 있음
4. 모든 브로커가 다운되면
모든 브로커가 다운된, 카프카 클러스터가 다운된 상황이 발생한 경우, 2가지 선택지가 있음
- 마지막 리더가 살아나기를 기다림
- 마지막 리더에게는 메시지가 모두 저장되어 있어 나중에 살아난다면 메시지 손실 없이, 프로듀엇의 요청들을 처리하면서 서비스를 지속적으로 제공할 수 있음
- 재시작시 마지막 리더가 반드시 시작되어야 함
- 0.11.0.0이하 버전 이상 버전의 기본값
- ISR에서 추방되었지만 먼저 살아나면 자동으로 리더가 됨
- 하지만 마지막 리더가 아니므로 메시지 손실이 발생하게 됨(마지막까지 메시지를 받은 것은 마지막 리더이므로)
- 0.11.0.0 이하 버전의 기본값
- 사용자의 선택에 따라 server.properties에서 설정할 수 있음
- unclean.leader.election.enable = true(먼저 살아나면 리더가 되는 방안), false(마지막 리더 기다림)
1개의 topic, 4개의 partitions, 3개의 replication factor
5. 주키퍼 지노드 역할
주키퍼(ZooKeeper)의 **지노드(ZNode)**는 ZooKeeper가 관리하는 데이터 구조의 기본 단위입니다. ZNode는 파일 시스템과 유사한 계층적(hierarchical) 구조를 가지며, 클라이언트와 ZooKeeper 간에 데이터를 저장하고 공유하는 데 사용됩니다. 이를 통해 분산 시스템의 상태와 메타데이터를 관리할 수 있습니다.
1) ZNode의 특징
- 계층적 구조:
- ZNode는 /를 기준으로 트리 구조를 형성합니다.
- 예를 들어, /kafka/brokers/ids는 ZNode 경로입니다.
- 부모 ZNode 아래에 자식 ZNode를 생성할 수 있습니다.
- 데이터 저장:
- 각 ZNode는 데이터를 저장할 수 있지만, 데이터 크기는 일반적으로 1MB로 제한됩니다.
- ZNode는 메타데이터(노드의 상태, 버전 등)도 관리합니다.
- 임시 및 지속적 ZNode: ZNode는 두 가지 유형으로 나뉩니다.
- Persistent ZNode (지속적 ZNode):
- 클라이언트가 연결을 끊어도 ZNode는 삭제되지 않습니다.
- 명시적으로 삭제해야만 제거됩니다.
- Ephemeral ZNode (임시 ZNode):
- 클라이언트 세션이 종료되면 자동으로 삭제됩니다.
- 주로 분산 락 구현에 사용됩니다.
- Persistent ZNode (지속적 ZNode):
- 시퀀스 ZNode:
- ZNode를 생성할 때 sequence 옵션을 사용하면, ZooKeeper가 자동으로 정수 값이 증가된 이름을 부여합니다.
- 예: /lock-00000001, /lock-00000002
- 분산 시스템에서 고유 ID 생성에 유용합니다.
- Watcher:
- ZNode에 Watcher를 설정하여 변경 사항을 감지할 수 있습니다.
- 특정 ZNode에 대해 변경(생성, 삭제, 데이터 변경)이 발생하면 클라이언트가 알림을 받을 수 있습니다.
- 버전 관리:
- 각 ZNode는 데이터 변경 시 버전을 관리합니다. 이는 데이터의 일관성을 유지하는 데 도움을 줍니다.
2) ZNode 구조
/
├── controller
│ ├── 1 (현재 컨트롤러 브로커의 ID)
│ └── epoch (컨트롤러 epoch 정보)
├── brokers
│ ├── ids
│ │ ├── 1 (브로커 1의 메타데이터)
│ │ ├── 2 (브로커 2의 메타데이터)
│ │ └── ...
│ ├── topics
│ │ ├── topic1
│ │ │ ├── partitions
│ │ │ │ ├── 0
│ │ │ │ │ ├── state (파티션 0의 리더 및 ISR 정보)
│ │ │ │ └── ...
│ │ └── topic2
│ │ └── partitions
│ │ ├── 0
│ │ ├── 1
│ │ └── ...
│ └── seqid (브로커 ID를 위한 시퀀스 값)
├── consumers
│ ├── group1
│ │ ├── offsets
│ │ │ ├── topic1
│ │ │ │ ├── 0 (파티션 0의 오프셋 정보)
│ │ │ │ └── 1
│ │ │ └── ...
│ │ ├── owners
│ │ │ ├── topic1
│ │ │ │ ├── 0 (파티션 0의 소비자 정보)
│ │ │ │ └── 1
│ │ │ └── ...
│ │ └── metadata
│ └── group2
│ └── ...
├── config
│ ├── topics
│ │ ├── topic1 (topic1의 설정 정보)
│ │ ├── topic2 (topic2의 설정 정보)
│ │ └── ...
│ └── clients
│ ├── client1
│ ├── client2
│ └── ...
├── zookeeper
│ ├── quota (ZooKeeper 관련 쿼터 정보)
│ └── ...
- /controller:
- 현재 Kafka 클러스터에서 컨트롤러 역할을 맡고 있는 브로커를 관리
- controller ZNode에 저장된 값은 현재 컨트롤러 브로커의 ID를 나타냄
- controller_epoch는 컨트롤러 선출이 몇 번 이루어졌는지를 나타냄
- /brokers:
- 브로커 정보를 저장하는 주요 공간
- /brokers/ids: 현재 클러스터에 등록된 브로커들의 ID와 메타데이터를 포함
- /brokers/topics: 토픽별 파티션 및 리더, ISR(In-Sync Replica) 정보를 저장
- /brokers/seqid: 브로커 ID 자동 할당 시 사용되는 시퀀스 정보
- /consumers:
- 소비자 그룹(Consumer Group) 정보를 저장
- /consumers/[group]/offsets: 특정 소비자 그룹이 어떤 토픽의 파티션을 소비했는지와 그 오프셋 정보를 저장
- /consumers/[group]/owners: 각 파티션을 소비하고 있는 소비자 정보를 저장
- /consumers/[group]/metadata: 소비자 그룹과 관련된 메타데이터를 저장
- /config:
- Kafka 클러스터의 설정 정보를 관리
- /config/topics: 각 토픽의 구성 설정(예: replication factor, partitions, cleanup policy 등)을 저장
- /config/clients: Kafka 클라이언트와 관련된 설정 정보를 저장
- /zookeeper:
- ZooKeeper 자체의 메타데이터 및 관리 데이터를 포함
- /zookeeper/quota: ZooKeeper 클라이언트의 요청 속도 및 대역폭 제한과 관련된 데이터를 저장
참조:
도서 - 카프카, 데이터 플랫폼의 최강자
728x90
'BigData > kafka' 카테고리의 다른 글
[Kafka] ubuntu 20.04 lts 위의 docker-compose로 kafka + zookeeper + CMAK 구성하기 (0) | 2024.03.05 |
---|
댓글