Apache-Kafka

Apache-Kafka

최고의 메시지 브로커


✅ Apache-Kafka ?


카프카는 대규모 데이터의 흐름을 제어하기 위한 메시지 브로커이다.

보통 스프링 부트 애플리케이션을 개발하여 사용할 경우 로그백(Logback)을 이용해 로깅을 하는데

기록된 로그를 보통 애플리케이션이 돌아가는 서버에 저장하게 된다.

이 경우 애플리케이션의 로그를 보고 싶다면 해당 서버에 ssh 등을 통해 직접 접속한 후 로그 파일이 저장된 위치에 찾아들어가 로그를 일일이 까 봐야 한다.


# 🤢
tail -f app-2021.05.17.log 


혹은 서버끼리 데이터의 교환이 일어날 경우 시스템의 규모가 작다면 이를 어느 정도 제어할 수 있으나,

시스템의 규모가 커진다면 이 데이터의 흐름을 제어하긴 커녕 파악하는 것 부터 매우 어렵다.



이러한 문제들을 해결하기 위해 각 서버 사이에 메시지 브로커를 두는데 이 용도로 사용되는 것이 카프카이다.



카프카는 데이터를 저장하는데 리소스를 절약하기 위해 기본적으로 데이터베이스를 사용하거나,

캐시 메모리를 구현하여 사용하지 않는다. 단지 페이지 캐시파일 시스템(=하드디스크)만을 사용한다.

페이지 캐시는 하드디스크를 사용할 경우 느린 입출력 속도를 보완하기 위해 사용하며, 이 페이지 캐시는

리눅스 명령어 free를 입력하여 cache탭을 보면 알 수 있다.



📗 카프카의 특징


  • 하드디스크 기반 저장 방식
    • 중요 리소스 사용 최소화(캐시 메모리, RAM)
  • 확장에 유연한 구조
    • 다수의 브로커로 상황에 맞게 스케일업, 스케일아웃
  • 내결함성
    • 장애 허용 시스템
  • 고가용성
    • 자동화된 시스템으로 가동중지 시간이 최소화됨
  • 저지연성(10ms 미만)
    • 대규모 병렬 처리로 인한 고성능


📗 카프카의 활용사례


  • 사용자 활동 추적
    • 페이지 열람, 클릭이벤트 추적 등
  • 알람 시스템
    • 슬랙, 라인 등과 연동
  • 메트릭 수집
    • 모니터링 및 경고(알람과 연동)
  • 로그 수집
    • 모니터링 및 경고(알람과 연동)
  • 커밋 로그
    • 카프카에 대한 데이터 변경사항 수집
  • 스트림 처리
    • 데이터 변환 처리
  • 시스템 종속성 분리
    • 모든 시스템이 서로가 아닌 오로지 카프카만을 의존하게 만듦
  • 다른 데이터 기술과 통합
    • RDBMS, 하둡, 스파크, 엘라스틱서치 등과 유연한 통합


✅ 카프카의 구조



카프카는 크게 클러스터, 브로커, 프로듀서, 컨슈머로 이루어져 있다.

그리고 브로커는 다시 토픽으로 이루어져 있으며,

토픽파티션으로 이루어져 있다.

마지막으로 파티션레코드로 이루어져 있다.


✅ 프로듀서 & 컨슈머


카프카에 데이터를 전달하는 주체를 프로듀서라고 칭하며,

카프카에서 데이터를 가져다 사용하는 주체는 컨슈머라고


프로듀서는 필수 옵션으로 직렬화 방식을 정해줘야 하며, 컨슈머는 역직렬화 방식을 정해줘야 한다.

프로듀서는 카프카에 메시지를 직렬화하여 전달하고, 카프카는 이 데이터를 보관하는데 이를 레코드라고 부른다.


이 레코드에는 실질적인 유의미한 데이터가 들어있으며, 이 데이터를 사용하기 위해서는 프로듀서가 데이터를 보내며 직렬화한 방식과 동일한 방식으로 컨슈머가 역직렬화 해줘야 한다.

예를 들어 프로듀서에서 StringSerializer로 직렬화해서 보낸 값은 컨슈머도 StringSerializer로 역직렬화 해야 올바른 데이터를 얻을 수 있다.


컨슈머는 자신이 구독한 토픽에서 마지막으로 소비한 메시지의 오프셋을 __consumer_offsets 토픽에 저장한다.

따라서 컨슈머가 장애로 중단되었다가 다시 재가동되더라도 마지막으로 처리했던 부분부터 다시 작업을 시작할 수 있다.


📙 컨슈머 그룹


컨슈머는 그룹 단위로 지정하여 운영할 수 있다.

특정 토픽에 대해 컨슈머 그룹으로 구독할 수 있으며,

컨슈머 그룹에 소속된 컨슈머는 각자 파티션을 1개 이상 맡아 레코드를 소비한다.

이때 토픽에 속한 파티션은 컨슈머 그룹에 속한 컨슈머중 한 개에만 할당될 수 있다.

따라서 3개의 파티션을 가진 토픽을 4개의 컨슈머로 이루어진 컨슈머 그룹으로 운영할 경우 1개의 컨슈머는 아무런 파티션도 할당받지 못하고 유휴 상태로 남기 때문에 이 컨슈머는 불필요하게 스레드만 차지하게 된다.



✅ 클러스터 & 브로커


브로커는 카프카가 실행되는 단일 서버를 말한다.

그리고 클러스터는 브로커의 논리적인 묶음을 말한다.


클러스터는 보통 세 개의 브로커로 구성되는데 이 이유는 장애에 대응하기 위해서다.

카프카는 장애로 브로커가 다운되는 경우를 대비해 브로커를 복제해두고 이를 클러스터라는 논리적인 개념으로 묶어서 운영하게 된다.

이 경우 브로커의 특정 파티션이 리더 역할을 하며, 나머지 파티션은 팔로워 역할을 한다.

오로지 리더만이 프로듀서, 컨슈머와 직접 통신하고, 리더는 자신을 바라보는 팔로워에 데이터를 복제한다.



그리고 리더의 데이터를 완벽하게 복제한 팔로워들을 ISR(In-Sync-Replicas)로 묶였다고 표현하며,

이 ISR로 묶인 팔로워는 위기상황에 리더로 선출될 자격을 갖는다.


만일 리더가 장애로 인해 다운된다면 ISR로 묶인 팔로워 중에 새로운 리더가 선출되어 이전 리더가 하던 작업을 이어받는다.

이러한 특징으로 인해 카프카는 장애 허용 시스템이라고도 불린다.


단점은 말 그대로 서버를 세 개 사용하는 것이기 때문에 물리적인 리소스 또한 3배로 증가한다는 것이지만,

장애에 안전하다는 강점이 워낙 강력하기 때문에 이러한 단점을 감수하고 사용한다.


카프카의 장애 = 전체 시스템의 장애가 될 수 있다


브로커 한 개로 운영할 수도 있지만 이 경우 브로커가 다운된다면 전체 시스템의 장애로 이어지므로 추천하지 않는다.

브로커는 다시 토픽, 파티션, 레코드로 이루어진다.


✅ 토픽



토픽은 레코드의 메인 주제이다. RDBMS테이블과 비슷한 위치를 갖는다.


예를 들어 APP서버의 데이터를 주로 전송하고 싶다면 토픽 이름을 APP서버와 관련되게 명명하고, APP서버의 데이터를 해당 토픽에만 보내고 꺼내면 된다.

토픽은 최소 한 개 이상의 파티션을 소유하며, 이 파티션에는 프로듀서가 보낸 데이터들이 저장된다.


📙 토픽 이름 제약조건


  • 빈 문자열 토픽 이름은 지원하지 않는다
  • 토픽 이름은 마침표 하나(.) 또는 마침표 둘(..)로 생성될 수 없다.
  • 토픽 이름의 길이는 249자 미만으로 생성되어야 한다
  • 토픽 이름은 영어 대소문자와 숫자 0부터 9 그리고 마침표(.), 언더바(_), 하이픈(-) 조합으로 생성할 수 있다. 이외의 문자열이 포함된 토픽 이름은 생성 불가하다
  • 카프카 내부 로직 관리 목적으로 사용되는 2개 토픽(__consumer_offsets, __transaction_state)과 동일한 이름으로 생성할 수 없다
  • 카프카 내부적으로 사용하는 로직 때문에 토픽 이름에 마침표(.)언더바(_)가 동시에 들어가면 안 된다. 생성은 할 수 있지만 사용 시 이슈가 발생하기 때문에 마침표(.)언더바(_)가 들어간 토픽 이름을 사용하면 WARNING 메시지가 발생한다.
  • 이미 생성된 토픽 이름의 마침표(.)언더바(_)로 바꾸거나 언더바(_)마침표(.)로 바꾼 경우 신규 토픽 이름과 동일하다면 생성할 수 없다. 예를 들어, to.pic이라는 이름의 토픽이 생성되어 있다면 to_pic이라는 이름의 토픽을 생성할 수없다


✅ 파티션



파티션은 데이터를 병렬 처리하기 위한 단위이다.

파티션은 큐와 같은 구조로 선입선출(FIFO)을 보장한다.

다만 일반적인 큐는 데이터를 꺼내면(pop) 데이터가 삭제되지만, 카프카의 파티션은 데이터를 꺼내더라도 데이터가 보존된다는 특징이 있다.


이러한 특징으로 카프카의 데이터는 1개 이상의 컨슈머 그룹에서 다양한 목적으로 동일한 데이터를 꺼내다 사용할 수 있다.

이를 컨슈머토픽구독(Subscribe)한다고 칭하며, 각 컨슈머는 자신이 구독한 토픽의 파티션에 대해 오프셋을 갖는다.


컨슈머가 갖고 있는 오프셋과 컨슈머가 구독 중인 파티션의 오프셋의 차이를 컨슈머 랙이라고 부르며 컨슈머 랙이 크면 그만큼 처리에 지연이 발생하고 있다는 지표로 본다.


파티션의 개수는 기본적으로 1개로 설정되지만, 이 파티션을 여러 개로 늘릴 경우 병렬 처리를 할 수 있어 커다란 성능 향상을 기대할 수 있다.

다만 이 경우 데이터의 순서가 보장되지 않는다.


✅ 레코드 & 로그 세그먼트


프로듀서가 카프카에 전달한 메시지의 최소단위를 레코드라고 칭한다.

레코드는 타임스탬프, 메시지 키, 메시지 값, 오프셋으로 구성되어 있다.


카프카에레코드가 저장되는 기본 경로는 /tmp/kafka-logs이며 이 경로는 사용자가 임의로 변경할 수 있다.


레코드는 00000000000000000.log 같은 이름으로 저장돼있으며, 이를 로그 세그먼트라 칭한다.


로그 세그먼트는 카프카가 저장한 레코드를 묶은 파일의 최소 단위이다.

로그 세그먼트는 기본적으로 1GB 단위로 갱신되며 브로커의 로그 리텐션 정책에 의해 자동으로 삭제된다.

기본값으로 7일간 보존 후 삭제하게 돼있으며 이는 임의로 조절할 수 있다.

혹은 용량 단위로 제어하거나 아예 삭제하지 않고 압축하여 보관하도록 할 수도 있다.


프로듀서에서 보낸 데이터가 브로커에 저장될 때 오프셋타임스탬프가 지정되어 저장되며, 타임스탬프는 브로커가 설치된 서버의 유닉스 시간이 설정된다.

혹은 프로듀서가 레코드를 전송할 때 임의의 타임스탬프를 설정하여 보낼 수도 있다.


레코드는 수정될 수 없고 오직 삭제만 가능한데, 프로듀서나 컨슈머가 삭제를 요청할 수도 없으며 오로지 브로커만이 레코드를 삭제할 수 있다.

그리고 이 삭제마저도 로그 세그먼트 단위로만 가능하다.

따라서 특정 레코드만 디테일하게 삭제하는 것은 불가능하다.

만약 특정 레코드를 삭제하고자 해당 레코드가 속한 로그 세그먼트를 삭제해야 하므로 데이터의 유실이 발생한다.


메시지 키의 경우 해시값을 토대로 파티션에 저장된다.

메시지가 메시지 키를 갖는 경우 여러 개의 메시지가 동일한 메시지 키를 갖는다면 동일한 파티션에 저장되기 때문에 의도와 다른 파티션에 저장될 수 있으므로 주의해야 한다.

메시지 키를 갖지 않는다면 메시지 키는 null로 처리되며 데이터는 파티션에 순서대로 분배된다.


메시지에는 실질적인 데이터가 들어있다.

이 데이터를 사용하기 위해서는 프로듀서가 데이터를 보낼 때 직렬화한 방식과 동일한 방식으로 컨슈머가 역직렬화 해야 한다.


📖 데이터의 처리속도를 늘리는 방법


  • 컨슈머의 처리량을 늘린다

    • 컨슈머 애플리케이션을 내부적으로 멀티스레드로 동작하게 만들거나 컨슈머 애플리케이션이 실행되는 서버의 물리적인 사양을 스케일 업하거나, GC튜닝 등을 하는 방법이 있으나, 일반적으로 컨슈머 애플리케이션은 외부 시스템(S3, 하둡, DB 등)과 연동되어 있기 때문에 한계가 있다.


  • 파티션과 컨슈머의 개수를 늘려 병렬 처리한다

    • 한 개의 프로듀서가 초당 보내는 레코드가 1,000건이고 한개의 컨슈머가 초당 소모하는 레코드가 100건이라면 단순하게 파티션을 10개로 늘리고 10개의 컨슈머를 붙이면 최적의 성능이 나온다. 데이터의 양이 줄면 파티션과 컨슈머의 개수를 줄여 스케일 아웃하면 된다.


✅ 빅데이터 처리 솔루션




© 2022. All rights reserved.