데이터 엔지니어 기술 블로그

[🧙Kafka] 카프카 개념 - 카프카 내부 이해하기 본문

데이터 엔지니어링

[🧙Kafka] 카프카 개념 - 카프카 내부 이해하기

jun_yeong_park 2021. 4. 13. 17:08
반응형

About

카프카의 내부를 다 이해할 필요는 없지만 어느정도 알아두면 좀 더 최적화하거나 문제가 발생했을 때 해결하는데 도움이 된다.

 

 

내부 매커니즘

클러스터와 주키퍼

주키퍼에서의 카프카

 

컨트롤러

브로커 중 하나이고 리더를 선출해야하는 역할을 가지고 있다. 클러스터를 시작할 때의 첫 번째 브로커가 컨트롤러로 임명된다. 모든 브로커는 시작될 때 주키퍼에서 /controller 노드가 없으면 생성하려고 한다. 컨트롤러 브로커에 문제가 생기면 카프카의 Watch가 브로커들에게 컨트롤러가 사라졌다고 알려준다. 그러면 브로커들은 /controller 노드를 생성하려고 시도한다. 첫 번째로 생성한 노드가 컨트롤러가 되며 새로운 세대 번호를 받게 된다. 브로커들은 새로운 세대 번호를 알게 되고, 이전 세대 번호의 브로커에서 명령이 오면 그 명령은 무시한다.

 

파티션 리더였던 브로커가 문제가 생기게 되면 그 파티션들에게 새로운 리더가 필요하다는 것을 알려준다. 새로운 리더로 필요한 파티션들을 확인하고 리더가 될 브로커를 결정한다. 컨트롤러는 파티션들의 새로운 리더와 팔로워의 정보를 제공한다.

 

복제

 

카프카의 데이터는 토픽으로 구성되며 토픽은 파티션을 여러개 가질 수 있다. 각 파티션은 가용성을 위해 복제(replication)될 수 있다. 

 

파티션이 리플리카가 5개가 있다고 할 때 리더 리플리카와 팔로워 리플리카로 나눌 수 있다. 이 파티션으로 들어오는 모든 요청은 리더 리플리카가 받고 처리한다. 그 외에 복제되는 것들을 팔로워 리플리카라고 한다. 리더 리플리카가 문제가 생길 경우 팔로워 리플리카 중 하나가 리더 리플리카가 된다.

 

팔로워 리플리카가 문제가 생긴 뒤, 리더 리플리카에 문제가 생겼을 때 문제가 생긴 팔로워 리플리카는 리더가 될 수 없다.

 

팔로워는 리더에게 일정 간격마다 fetch 명령을 보낸다. fetch 명령을 보낼 때 오프셋을 함께 보내는데, 순서대로 보내게 된다. 이것을 timeout(replica.lag.time.max.ms) 시간 이상 하지 않게 되면 out-sync 리플리카로 간주한다. out-sync와 반대로 계속 동기화가 되는 상태를 in-sync라고 한다.

 

각 파티션은 preferred leader(선호 리더) 를 갖는다. 선호 리더는 맨 처음 토픽이 생성될 때 파티션의 리더였던 리플리카들을 말한다. 

요청 처리

카프카는 TCP로 전송되는 이진 프로토콜을 가지고 있어서 다양한 언어에서 카프카를 사용할 수 있게 해준다. 요청은 다음과 같은 헤더를 가진다.

1. Request Type ID: Produce는 0번이고 Fetch는 1번이다. 이와 같이 요청의 고유 번호가 여기에 들어간다.

2. Request Version: 프토토콜 API 버전을 타나낸다.

3. cID(Correlation ID): 사용자가 지정하는 요청의 고유 식별번호로 문제가 생겼을 때 이 요청 번호로 찾을 수 있다.

4. Client ID: 요청한 클라이언트가 어떤 클라이언트인지 구분하는 값이다.

 

브로커는 스레드를 여러개 가지고 있다. 리스닝하는 각 포트에 대해 acceptor 스레드를 가지고 있고, processor 스레드(네트워크 스레드)에서 그 다음을 처리하는데 개수를 구성할 수 있다. request queue에 요청을 넣고 response queue에서 요청을 가져와서 클라이언트에게 전송한다. 요청 처리 스레드가 request queue에 있는 요청을 가져와서 처리하고 response queue에 넣어준다.

 

쓰기 요청과 읽기 요청은 모두 각 파티션의 리더 리플리카에게 전송되어야 한다. 클라이언트가 요청을 어디로 전송해야할지 알려면 metadata request 를 보내서 응답으로 토픽에서 존재하는 파티션들, 각 파티션의 리플리카와 리더 리플리카 등의 정보를 받아와야 한다. 이 요청은 어떤 브로커로 전송해도 받을 수 있다. 이 요청을 받고나면 클라이언트는 어떤 곳이 리더 리플리카인지 알 수 있으므로 요청을 처리할 수 있다.

 

쓰기 요청

브로커는 메시지를 받고 권한 등의 검사를 한 뒤에 로컬 디스크에 메시지를 쓴다. 메시지를 쓸 때까지 기다리지 않으므로 속도가 빠르다.

 

읽기 요청

클라이언트는 읽기 요청을 할 때 받고 싶은 시작 오프셋과 토픽 파티션 정보를 보낸다. 카프카는 파티션에서 메시지를 읽은 후 zero-copy 기법을 사용해서 어딘가를 거치지 않고 메시지를 전송한다.

 

클라이언트는 보통 파티션을 읽을 때, 그 파티션의 리더 리플리카가 팔로워 리플리카들에게 복제가 완료된 메시지만 읽을 수 있게 해준다. 그 이유는 아직 복제되지 않은 메시지를 돌려준다고 가정하면, 돌려준 후 복제가 완료되지 않았는데 리더 리플리카가 문제가 생기면 메시지는 사라지기 때문이다.

 

스토리지

카프카를 구성할 때 log.dirs 라는 설정을 볼 수 있는데 이 설정은 파티션이 저장될 디렉토리를 말한다. 그리고 실제 에러 로그는 log4j.properties 파일에 저장된다. 

 

파티션 할당

파티션 리플리카는 서로 다른 브로커에 할당한다. 왜냐하면 같은 브로커에 할당 시 그 브로커가 문제가 생기면 모두 날아가기 때문에 리플리케이션 한 의미가 없기 때문이다. 

 

카프카는 랙 정보가 있다면 같은 랙 안에 같은 리플리카를 두지 않으려고 한다. 그래서 가용성을 보장할 수 있다.

 

파일 관리

 

kafka retention

카프카는 데이터를 계속 보존하지 않고, 메시지큐이기 때문에 보존 기간을 설정해야 한다. 카프카에서는 파티션을 저장할 때 세그먼트 단위로 나누어서 저장한다. 세그먼트는 기간과 용량으로 설정할 수 있고 둘 중 하나가 만족된다면 새로운 세그먼트를 생성하여 쓴다. 쓰는 중인 세그먼트를 Active Segement라고 하는데 이것은 삭제가 불가능하다. 만약 로그 보관 기간을 1일로 두고 세그먼트 기간을 5일로 둔다면 5일동안 보존된다. 

 

파일 형식

프로듀서가 배치에 저장된 메시지를 보낼 때 모든 메시지가 같이 압축되어 래퍼 메시지의 값으로 전송된다. 

 

인덱스

브로커가 빨리 메시지를 찾을 수 있도록 파티션의 인덱스를 관리한다. 인덱스를 세그먼트 파일과 이 파일의 내부 위치로 오프셋을 연관시킨다. 

 

압축

- 삭제 보존 정책: 보존 기간 이전의 메시지를 삭제한다. 

- 압축 보존 정책: 가장 최근 값만 저장한다.

 

클린(Clean)

- 더티 메시지를 처리하면 클린 메시지가 된다.

 

더티(Dirty)

- log.cleaner.enabled를 활성화하면 브로커는 압축 매니저와 압축 스레드들을 시작한다. 더티 메시지의 비율이 가장 큰 파티션을 선택해서 압축한다.

- 같은 키의 가장 최근 메시지만 남기고 나머지는 삭제한다.

- 메시지를 완전히 삭제하고 싶으면 값을 null로 메시지를 보내면 된다.

 

 

 

 

 

 

반응형
Comments