1. 카프카 기초 다지기
1) 카프카를 구성하는 주요 요소
- 주키퍼(Zookeeper) : 아파치 프로젝트 애플리케이션으로 카프카의 메타데이터(metadata) 관리 및 브로커의 정상상태 점검(health check) 을 담당 합니다.
- 카프카(Kafka) or 카프카 클러스터(Kafka cluster) : 아파치 프로젝트 애플리케이션으로 여러 대의 브로커를 구성한 클러스터를 의미 합니다.
- 브로커(broker) : 카프카 애플리케이션이 설치된 서버 또는 노드를 의미 합니다.
- 프로듀서(producer) : 카프카로 메시지를 보내는 역할을 하는 클라이언트로 총칭합니다.
- 컨슈머(consumer) : 카프카에서 메시지를 꺼내가는 역할을 하는 클라이언트를 총칭합니다.
- 토픽(topic) : 카프카는 메시지 피드들을 토픽으로 구분하고, 각 토픽의 이름은 카프카 내에서 고유합니다.
- 파티션(partition) : 병렬 처리 및 고성능을 얻기 위해 하나의 토픽을 여러 개로 나눈 것을 의미합니다
- 세그먼트(segment) : 프로듀서가 전송한 실제 메시지가 중개인의 로컬 디스크에 저장되는 파일을 말합니다.
- 메시지(message) 또는 레코드(record): 프로듀서가 브로커로 전송하거나 컨슈머가 읽어가는 데이터 조각을 말합니다.
2) 리플리케이션
리플리케이션(replication)이란 각 메시지들을 여러 개로 복제해서 카프카 클러스터 내 브로커들에 분산시키는 동작을 의미 합니다. 이렇게 카프카 클러스터내 분산시키는 동작으로 브로커가 하나 종료되어도 카프카가 안정성을 유지할 수있다.
카프카 클러스터 설치 완료 후 토픽을 생성하였을 때 아래와 같은 옵션을 이용하여 생성.
--partition 1, --replication-factor 3
=> 원본을 포함하여 3개의 replication이 있음을 의미.
# replication-factor 옵션 : 카프카 내에서 몇 개의 리플리케이션을 유지하는지를 설정하는 옵션
위의 그림에서 test-overview01 토픽은 설정된 --replication-factor 옵션 값에 의해서 각 브로커에 배치된 상태를 보여주는 것.
그리고 조금 더 정확하게 말해서 카프카에서 토픽이 리플리케이션이 된 것이 아니라 토픽의 파티션이 리플리케이션(복제)이 된 것이라고 표현할 수 있음
replication-factor의 값이 높아지면 안정성이 높아지지만, 그만큼 브로커의 리소스 사용이 많아지며, 복제에 대한 오버헤드가 발생될 수 있으므로 적절한 팩터수를 설정해서 사용을 해야 한다.
테스트나 개발환경 : 리플리케이션 팩터 수 1로 설정
운영(로그성 메시지로 약간의 유실 허용가능) : 리플리케이션 팩터 수 2로 설정
운영(유실 허용 안됨) : 리플리케이션 팩터 수 3으로 설정
안정성을 높이기 위해 팩터 수를 4, 5 등등 높일 수 있는데 3이 가장 안정성이 있다고 한다. 팩터수가 많으면 디스크 공간이 많이 사용하니깐...
3) 파티션
파티션이란?
하나의 토픽이 한 번에 처리할 수 있는 단계를 높이기 위해서 토픽을 여러 개로 나눠 병렬 처리가 가능하게 만든 것을 파티션(partition)이라고 합니다.
이렇게 하나를 여러 개로 나누면 분산 처리가 가능해지며, 파티션 수만큼 컨슈머를 연결할 수 있게 됨.
토픽 1 -> 파티션 1개
토픽 2 -> 파티션 3개..
등 생성하기 나름에 따라 다르게 할 수 있다.
파티션 수를 정하는 기준은 모호한 경우가 많은데. 한번 늘리면 파티션을 다시 줄일 수 없다는 걸 명심해서 설정을 해야 한다. 대체로 초기에 2~4 정도로 생성하고 컨슈머의 LAG를 모니터링한 뒤에 조금씩 늘리는 방식을 선택한다고 한다.
여기서 컨슈머의 LAG 이란 '프로듀서가 보낸 메시지수(카프카에 남아 있는 메세지 수) - 컨슈머가 가져간 메세지 수'를 의미
컨슈머의 지연이 없다면(모든 메시지를 가져갔다면) 5-5=0 이 되게 됨.
4) 세그먼트
카프카에서 전송한 메시지는 어디에 저장이 되나?
프로듀서를 이용해 보낸 메세지는 토픽의 파티션 0에 저장되어 있다.
이처럼 프로듀서에 의해 브로커로 전송된 메시지는 토픽의 파티션에 저장되며, 각 메시지들은 세그먼트(segment)라는 로그 파일의 형태로 브로커의 로컬 디스크에 저장되게 됨.
각 파티션마다 n개의 세그먼트 로그파일이 존재한다.
카프카가 설치된 디렉터리의 logs디렉토리로 이동하여 살펴보면 생성한 토픽 이름으로 디렉토리가 있는걸 확인 할 수 있다. 그리고 디렉토리 이름 끝에 "-0"는 0번 파티션을 의미하게 됩니다. 그 디렉터리 아래 여러 가지 파일이 존재함.
```bash
[root@kafka1]# ls -al | grep overview
drwxr-xr-x. 2 root root 4096 Apr 20 14:57 test-overview01-0
[root@kafka1]# cd test-overview01-0
[root@kafka1]# ls -alrt
total 16
-rw-r--r--. 1 root root 10485760 Apr 20 14:52 00000000000000000000.index
-rw-r--r--. 1 root root 10485756 Apr 20 14:52 00000000000000000000.timeindex
-rw-r--r--. 1 root root 8 Apr 20 14:57 leader-epoch-checkpoint
drwxr-xr-x. 2 root root 4096 Apr 20 14:57 .
-rw-r--r--. 1 root root 81 Apr 20 14:57 00000000000000000000.log
drwxr-xr-x. 20 root root 4096 Apr 20 17:41 ..
```
hexdump를 보기 위해서 xxd 명령어를 이용하면 이전 테스트에서 전송한 First message라는 메시지를 확인 할 수 있으며, 브로커의 로컬디스크에 안전하게 저장된 것을 확인할 수 있다.
[root@kafka1]# xxd 00000000000000000000.log
0000000: 0000 0000 0000 0000 0000 0045 0000 0000 ...........E....
0000010: 02d5 73b5 9400 0000 0000 0000 0001 8045 ..s............E
0000020: 8bb7 1f00 0001 8045 8bb7 1fff ffff ffff .......E........
0000030: ffff ffff ffff ffff ff00 0000 0126 0000 .............&..
0000040: 0001 1a46 6972 7374 206d 6573 7361 6765 ...First message
0000050: 00
[root@kafka1]# strings 00000000000000000000.log
First message
# 메세지 전송 과정 정리
1) 프로듀서는 카프카의 test-overview01 토픽으로 메세지를 전송합니다.
2) test-overview01 토픽은 파티션이 하나뿐임으로, 프로듀서로 부터 받은 메세지를 파티션 0의 세그먼트 로그 파일에 저장한다.
3) 브로커의 세그먼트 로그 파일에 저장된 메시지는 컨슈머가 읽어간다.
카프카의 핵심 기능
- 여러 우수한 기업들이 서둘러서 도입하는 이유: 높은 처리량, 빠른 응답 속도, 안정성 때문!
- 어떻게 높은 처리량을 갖고, 카프카의 특성 핵심기능이 뭔지? 알아보자!
1) 분산시스템
카프카는 분산시스템이다.
최초로 구성하는 클러스터에 브로커를 추가하는 방식이여서 확장이 가능하고 카프카 브로커 추가는 온라인 상태에서 매우 쉽다. 이런 확장이 용이한 구조가 카프카의 장점, 그리고 분산 시스템이므로 성능이 좋고, 장애 대응이 좋다는 점.
2) 페이지 캐시
"페이지 캐시"의 이용으로 높은 처리량을 얻을 수 있었다!
운영체제에서 페이지 캐시를 통해 성능을 높이는데 카프카도 Os의 페이지 캐시를 활용하는 방식으로 설계된다.
3) 배치 전송 처리
카프카에서는 배치 전송을 권장한다.
프로듀서~ 컨슈머 사이 통신하며 엄청나게 많은 메시지를 주고받는데 이때 통신을 묶어 처리하여 오버헤드를 줄이면 빠르게 처리할 수 있으니깐 배치로 전송하는 게 이득이다.
예를 들어, 온라인 상품 구매 프로세스에서 상품의 재고 수량 업데이트 작업과 구매 로그를 저장소로 보내는 작업을 한번 생각해보면, 상품의 재고 수량 업데이트 작업은 지연 없이 실시간으로 처리되어야 하지만, 구매 로그를 저장소로 보내는 작업은 이미 로그가 서버에 기록되어 있기 때문에 실시간 처리보다는 배치 처리를 이용하는 것이 더욱 효율적일 거다.
4) 압축전송
압축 전송을 권장?
압축타입 - gzip, snappy, lz4, zstd ...
압축을 안하면 압축하는데 드는 시간을 줄이니깐 오히려 좋지 않을까? 라는 생각을 해보는데 상황에 따라 다를 것 같으니깐 .. 이 부분은 pass 해보겠다.
5) 토픽, 파티션, 오프셋
토픽 : 여기에 카프카가 데이터를 저장함.
병렬처리를 위해 토픽을 위에 말한것처럼 여러개의 파티션으로 나눈다.
카프카는 하나의 토픽이라도 파티셔닝을 통해 높은 처리량을 수행하도록 하는데, 이 파티션의 메시지가 저장되는 위치를 "오프셋" 이라고 한다. 오프셋은 순차적으로 증가하는 64비트 형태다.
아래 그림은 하나의 토픽이 3개의 파티션으로 나누어지고, 프로듀서로 부터 전송된 메시지들은 쓰기 동작이 각 파티션 별로 이루어진다는 걸 알 수 있다. 각 파티션 마다 순차적으로 증가하는 숫자가 오프셋이고, 이 오프셋을 통해 메시지의 순서를 알고, 컨슈머가 마지막 읽은 위치를 알 수 있다.
6) 고가용성 보장
카프카는 분산 시스템 -> 하나의 서버나 노드가 다운되어도 다른 서버 또는 노드가 대신하여 안정적인 서비스가 가능
이러한 고가용성을 보장하기 위해서 카프카는 리플리케이션(복제) 기능을 제공 하고 있으며, 토픽 자체를 복제하는 것이 아닌 토픽의 파티션을 복제를 하는 방식으로 한다.
토픽을 생성할 때 옵션으로 리플리케이션 팩터 수를 지정할 수 있으며, 이 숫자에 따라서 리플리케이션들이 존재함.
원본과 리플리케이션을 구분하기 위해서 카프카에서는 리더(leader) 와 팔로워(follower) 라고 부른다.
표에서와 같이 리더의 숫자는 1을 유지 하게 되고 리플리케이션 팩터의 수에 따라서 팔로워 수만 증가
팔로워수가 많다고 좋은건 아님 -> 디스크 공간 소비되니깐. 그래서 적절한 팩터 수 를 유지하는게 좋다. 일반적으로 3으로 구성한다고함.
리더(leader)는 프로듀서, 컨슈머로 부터 오는 모든 읽기와 쓰기 요청을 처리하며, 팔로워는 오직 리더로부터 복제를 하게 된다고 한다.
7) 주키퍼의 의존성
주키퍼란?
- 하둡의 서브 프로젝트 중 하나로 출발해 2011년에 아파치의 탑 레벨 프로젝트로 승격됨.
- 많은 분산 애플리케이션에서 코디네이터 역할을 하는 애플리케이션으로 사용됨.
- 여러 대의 서버를 앙상블(Ensemble, 클러스터)로 구성하고, 살아 있는 노드 수가 과반수 이상 유지된다면 지속적인 서비스가 가능한 구조
- 주키퍼는 반드시 홀수로 구성해야 함. (살아있는 노드가 과반수 이상 유지되면 지속적인 서비스가 가능하니깐.)
- 지노드(znode)를 이용해 카프카의 메타 정보가 주키퍼에 기록되며, 주키퍼는 이러한 시노드를 이용해 브로커의 노드 관리, 토픽 관리 ,컨트롤러 관리 등 매우 중요한 역할을 함
한편, 최근 들어 카프카를 사용하면서 주키퍼의 사용과정에서의 한계성이 드러나기 시작하였고, 그에 따라 주키퍼의 의존성을 벗어나고자 카프카에서는 주키퍼에 대한 의존성을 제거 하려고 kraft를 사용하려고 하지만 아직은 개발단계라는 걸 알 수 있음.
Apache Kafka 2.8 버전부터 주키퍼 대신 kraft 를 사용할 수 있으나 아직은 개발 단계라는 걸 알 수 있다.
Note
KRaft is in early access and should be used in development only. It is not suitable for production.
https://developer.confluent.io/learn/kraft/
3. 프로듀서 디자인
프로듀서 ?
- 카프카의 토픽으로 메세지를 전송하는 역할
- 프로듀서는 여러 옵션을 제공하며, 원하는 형태에 따라 옵션을 변경 하면서 다양한 방법으로 카프카로 메세지를 전송
- ProducerRecord : 카프카로 전송하기 위한 실제 데이터
- 레코드는 토픽, 파티션, 키, 밸류(value) 로 구성
(1) 프로듀서가 카프카로 레코드를 전송할 때, 특정 토픽으로 메세지를 전송하며, 레코드에서 토픽과 밸류(메세지 내용)은 필수이고, 특정 파티션을 지정하기 위한 레코드의 파티션 과 특정 파티션에 레코드들을 정렬하기 위한 레코드의 키는 선택사항 (옵션)
(2) 다음으로 각 레코드들은 프로듀서의 send() 메소드를 통해서 시리얼라이저(serializer, 직렬화) 그리고 파티셔너를 거치게 됨
(3) 만약 프로듀서 레코드의 선택사항인 파티션을 지정 하였다면 파티셔너는 아무 동작도 하지 않고 지정된 파티션으로 레코드를 전달, 파티션을 지정하지 않은 경우에는 키를 가지고 파티션을 선택해 레코드를 전달하는데 기본적으로 라운드 로빈(round robin) 방식으로 동작
(5) send() 메소드 동작 이후 레코드들을 프로듀서가 카프카로 전송하기 전에 배치 전송을 하기 위해서파티션 별로 잠시 모아둠. 전송이 실패되면 재시도 동작이 이루어지게 되고 지정한 횟수만큼 재시도가 실패하면 최종 실패로 전달하게 되고, 전송이 성공되면 성공에 대한 메타데이터를 리턴
4. 컨슈머
1) 컨슈머의 기본 동작
프로듀서가 카프카의 토픽으로 메세지를 전송하면 해당 메세지들은 브로커들의 로컬 디스크에 저장된다.
그리고 컨슈머를 이용해 토픽에 저장된 메세지를 가져올 수 있다.
- 컨슈머 그룹은 하나이상의 컨슈머들이 모여 있는 그룹을 의미하고 컨슈머는 반드시 컨슈머 그룹에 속하게 된다.
- 컨슈머 그룹은 각 파티션의 리더에게 카프카 토픽에 저장된 메세지를 가져오기 위한 요청을 보낸다. 이때 파티션 수와 컨슈머(하나의 컨슈머 그룹 안에 있는 컨슈머 수)는 일대일로 매핑 해야하는 것은 아니지만, 파티션 수보다 컨슈머 수가 많게 구현되는 것은 바람직한 구성은 아니다.
이유 : 컨슈머 수가 파티션 수보다 많다면 더 많은 수의 컨슈머들은 그냥 대기 상태로 존재하기 때문에 더 빠르게 메세지를 가져오거나 처리량을 늘어나지 않기 떄문.
-> 컨슈머 그룹내에서 리밸런싱 동작을 통해 장애가 발생한 컨슈머의 역할을 동일한 그룹에 있는 다른 컨슈머가 그 역할을 대신 수행 하므로 굳이 장애 대비를 위한 추가 컨슈머 리소스를 할당하지 않아도 된다.
2) 컨슈머 그룹의 이해
- 컨슈머는 컨슈머 그룹 안에 속한 것이 일반적인 구조
- 하나의 컨슈머 그룹안에 여러개의 컨슈머가 구성될 수 있다.
아래의 그림 처럼 컨슈머는 토픽의 파티션과 일대일로 매핑 되어 메세지를 가져오게 된다.
peter-01 이라는 토픽이 있고 파티션 0 ,1, 2 라는 총 3개의 파티션으로 구성
우측에는 컨슈머 그룹01 이 있으며 그룹내에는 파티션수와 동일한 3개의 컨슈머가 속해 있다.
이와 같이 컨슈머들은 하나의 컨슈머 그룹 안에 속해 있으며, 그룹 내의 컨슈머들은 서로의 정보를 고유 하게 됩니다.
공유하는 정보로는 예를 들어 컨슈머01이 문제가 발생하여 종료되었다면, 컨슈머02 또는 컨슈머03은 컨슈머01이 하던일을 대신해서 peter-01 토픽의 파티션 0을 컨슘하게 된다.
Reference
'DevOps > Kafka' 카테고리의 다른 글
[Kafka] 카프카의 보안 (1) | 2022.06.08 |
---|---|
[kafka] 카프카 버전 업그레이드와 확장 (0) | 2022.05.25 |
[Kafka] 카프카의 모니터링 (0) | 2022.05.25 |
[Kafka] 카프카의 내부 동작 원리와 구현 (0) | 2022.04.26 |