Kafka Docker Compose

kafka echo system container 포트 구성

현재 kafka 스택으로 구성한 container 요소들은 아래와 같습니다.

  • zookeeper
    • port : 2181:2181
  • broker-1
    • port : 19092:19092
  • broker-2
    • port : 29092:29092
  • broker-3
    • port : 39092:39092
  • kafka-manager
    • port : 9090:9090
  • schema-registry
    • port : 9397:9397
  • connector
    • port : 9398:9398

zookeeper
zookeeper 는 borker-1, broker-2, broker-3 들의 설정과 heartbeat 체크 등의 역할을 수행합니다. 동물원을 관리하는 사람이라고 생각하면 이해가 빠릅니다.

broker-1, broker-2, broker-3
실제 토픽에 데이터를 전송하고, 수신하는 역할을 담당합니다.
거래나 무역을 할 때 브로커를 통해 거래 하는 것을 떠올린다면 이해가 쉽습니다.

kafka-manager
broker, zookeeper, topic 등을 관리하는 어드민 컨테이너입니다.

schema-registry
schema 는 데이터 송수신 시 사용하는 데이터의 형식을 의미합니다. 카프카를 오랫동안 사용하다보면 결국 AVRO 를 사용하게 됩니다. 카프카는 기본적으로 AVRO Schema 표준 규격을 따르는데, AVRO 는 Apache 에서 정한 여러가지 표준 규격 들 중 하나입니다.

schema registry 는 수신측과 송신측에서 스키마 버전 등을 통해 스키마가 맞는지 확인할 수 있는 하나의 중간 서버라고 보시면 됩니다.

제 생각에는 카프카 팀에서 Avro Schema 를 스키마 표준 규격으로 선택한 이유는 카프카 메시징 기술 개발 시 자사(Apache)의 표준 규격을 찾던 중 가장 먼저 찾았다거나 가장 쉽게 이해할 수 있어서 선택한 것 같다고 생각하고 이해했습니다. 뭔가 스키마가 뛰어나서 선택한 것이 아니라 필요에 의해 같은 회사의 표준 규격을 선택했다고 이해하면 쉽습니다.

아파치의 Avro Schema 표준 규격의 단점 중 하나는 시간 타입을 Long 타입으로 변환해야 한다는 점입니다. 따라서 Avro Schema 표준에 따라 데이터 전송시 시간 데이터를 전송해야 한다면 시간 데이터를 Long 타입으로 변환하고 Timezone 데이터도 함께 보내주는 것이 나을 듯 합니다. 이때 Timezone String 타입으로 전송하거나 개발팀에서 정한 코드값으로 보내는 방식을 상황에 맞게 선택하면 될 것 같습니다.

connnector
connector 는 json 형식의 데이터를 받으면 원하는 DB에 맞게끔 데이터를 변환 후에 Mysql, Oracle, PostgreSQL, Cassandra 등 여러가지 종류의 DB에 데이터를 저장하는 역할을 담당합니다. 따라서 백엔드 개발자는 단순히 connector 서버에 json 스키마를 전송하고 성공했는지 실패했는지 결과를 알 수있는 코드를 작성하면 됩니다.

물론 여기에 따라 Database 스키마의 DDL 역시 Avro Schema 표준 자료형에 호환되게끔 작성해야 하고, JSON 스키마 역시 Avro Schema 표준 자료형에 맞게끔 작성해야 한다는 점을 기억해야 합니다.

만약 개발팀 내에서 실시간 접속량이 큰 서비스를 비교적 짧은 기간에 개발해야 할 경우 (프로모션 등등) 동시성 문제에 관련된 개발 까지 하기에는 시간이 역부족이라는 생각이 들 경우 Connector 를 통해 개발을 수행하는 경우도 좋은 선택지 중에 하나가 될 듯 합니다. 대신 Connector 인스턴스 구성을 조금 더 가용성 있게 클러스터링을 한다던가 하는 설정이 필요합니다.

kafka docker-compose 의 용도

제가 kafka docker-compose 를 작성한 이유는 개발환경(운영환경이 아닙니다.)에서 사용하기 위해 작성했습니다.

간혹 어떤 분들은 운영에 카프카를 설치하는게 쉽다고 착각하는 것 같습니다. 운영에 필요한 Kafka 는 DevOps 또는 인프라 팀에서 일반적으로 셋업하고 운영해나가는게 통상적입니다. 스케일 아웃, 스케일 인, 네트워크 구조 개편, 네트워크 관리 등을 인프라팀 또는 DevOps 팀에서 관리하는 것이 효율적이기 때문입니다.

개발자가 카프카 설치만 하고, 비즈니스로직은 하나도 개발하지 못하면 욕을 먹게 됩니다. 그래서 실제로 현업에서 이미 설치된 카프카 서버에 다이렉트로 붙어서 개발 코드를 줄창 작성하시는 분들이 많다는 블로그 글을 본적이 있습니다.

실제로 저 역시도 실무에서 경험했던 몇몇 분들은 아직도 docker 사용법을 몰라서 docker compose 로 Memcached, Redis 등의 가상환경을 구축하지 못한 채로 개발 서버내에 직접 통신해서 개발 서버를 지지고 볶으면서 개발하는 경우를 자주 봤었습니다. 실제로 카프카 역시도 docker-compose 또는 Embedded Kafka 를 통한 테스트를 수행하지 않고 개발작업시 개발 Kafka 서버를 굉장히 Hard 하게 사용하는 경우가 많다는 글들을 꽤 많이 봤습니다.

이번 프로젝트에서 docker-compose로 작성한 kafka 는 운영환경이 아니라 개발 작업시에 사용하기 위해 작성했습니다.

RabbitMQ vs Kafka

지금도 그런지는 모르겠지만 마켓컬리 등과 같은 커머스 기업들도 처음에는 RabbitMQ를 사용했고 조금씩 Kafka 로 마이그레이션 해나가는 중이라는 글을 본 적이 있습니다. 만약 초반에 반드시 기한 안에 무언가를 꼭 만들어내야 하는데 시간과 비용이 부족하다면 RabbitMQ를 채택하면 됩니다. 대신 RabbitMQ를 사용한다면, 통신 실패 시에 대한 예외처리 코드나 실패 요청을 기록하는 Fail Over 시의 에러 기록 코드들을 직접 작성해두셔야 합니다.

RabbitMQ 는 Kafka 에 비해 장점이 있습니다. RabbitMQ 는 클러스터링을 통해 브로커를 클러스터링 하더라도 데이터의 순서를 보장해서 FIFO 방식으로 메시징이 이뤄진다는 점입니다. 즉 순서가 보장된다는 점입니다.

카프카의 경우 브로커 여러 개로 분산환경으로 구성시 데이터의 순서가 보장되지는 않습니다. 하지만 RabbitMQ에 비해 조금 더 규모가 큰 대규모 트래픽을 수용해야 할 때 장애 발생에 대한 대응 로직을 작성하기 쉽다는 장점도 있고 Fail Over 시에 대한 여러가지 설정도 있기 때문에 RabbitMQ에 비해 안정성이 높습니다. 다만, 운영 급으로 카프카를 구성해야 한다면, 전문 인력을 채용해야 한다는 단점이 있습니다. 네이버 및 여러 테크 기업에서 Kafka 의 장애 대응을 어떻게 했는지 기술 블로그나 유튜브에서 글들을 자주 볼 수 있다는 점을 본다면, 카프카 운영은 전문 인력으로 구성되어야 하는 듯 합니다.

카프카 사용시 데이터의 순서가 보장되지 않는 점을 극복하려면, 메시지를 시간순으로 구별할 수 있도록 시간 값과 유일성을 보장하는 킷값으로 구성한다면 메시지를 수신한 후 데이터를 저장하고 이후 저장된 데이터를 시간 순으로 정렬해서 불러오는 방식을 채택해야 합니다.

간혹가다 카프카 개발을 쉽게 생각하시는 분들이 있어서… 돈이 많은 회사가 아니라면, 또는 제품 개발 기한이 충분하지 않다면… 채용여건이 충분하지 않다면… RabbitMQ를 사용해 초기버전의 코드를 먼저 만들어두는 것이 낫지 않나 하는 생각에 적어봅니다.

이번 사이드프로젝트에서 KAFKA 기반의 메시징을 선택한 이유

RabbitMQ 로 이미 상용 서비스를 개발해본 경험이 있습니다. RabbitMQ로는 이미 자유자재로 메시징 코드를 작성하는 것이 가능하고, 로드를 최소화해서 백엔드 애플리케이션의 구조를 구성하는 것을 실무에서 이미 경험했습니다.

하지만, 사이드프로젝트의 경우 기존에 해봤던 것 말고 새로운 기술을 적용해가면서 스터디를 하는 것이 필요했습니다. 그래서 실무에서 사용해보진 않았던 Kafka 를 직접 스터디해서 적용해봐야 겠다는 생각으로 Kafka 를 선택했습니다.