BackEnd

Kafka + Spring Boot 완전 연동기: Apache Kafka 실패부터 Bitnami Kafka 성공까지

Raconer 2025. 6. 19. 22:41
728x90

Kafka를 처음부터 끝까지 Docker 기반으로 설정하고 Spring Boot 애플리케이션과 연동하는 과정을 공유합니다.

특히 Apache Kafka 이미지로 고생한 뒤 Bitnami Kafka로 전환해 성공한 내용을 기록합니다.


✅ 1. Spring Boot로 Kafka 연동하기

Spring에서는 spring-kafka 의존성만 추가하면 Kafka를 쉽게 연동할 수 있습니다.

// build.gradle.kts
implementation("org.springframework.kafka:spring-kafka")

Kafka 설정은 application.yml로 구성하며, 기본적인 Consumer/Producer 설정은 다음과 같습니다:

spring:
  kafka:
    bootstrap-servers: localhost:9092  # Kafka 브로커 주소
    consumer:
      group-id: my-group-test          # Consumer 그룹 ID
      auto-offset-reset: earliest      # 오프셋이 없을 때 처음부터 읽기
      enable-auto-commit: false        # 자동 커밋 비활성화 (수동 커밋 사용)
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
    producer:
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.apache.kafka.common.serialization.StringSerializer
    listener:
      ack-mode: manual_immediate       # 수신 즉시 ack 호출 시 오프셋 커밋

 


✅ Kafka 브로커란?

Kafka에서 브로커(Broker)는 Kafka 서버 단위이며, 다음 역할을 합니다:

  • Producer로부터 메시지를 수신
  • Topic/Partition 구조로 메시지 저장
  • Consumer에 메시지를 전달
  • 클러스터 내 메타데이터 관리 및 전파

즉, Kafka의 핵심 서버이며 여러 개의 브로커가 모여 하나의 클러스터를 구성합니다.


✅ 간단한 Producer / Consumer 예제

@Service
class KafkaProducer(private val kafkaTemplate: KafkaTemplate<String, String>) {
    fun send(message: String) {
        kafkaTemplate.send("my-topic", message)
            .whenComplete { result, ex ->
                if (ex != null) println("❌ 실패: ${ex.message}")
                else println("✅ 전송 성공: ${result?.producerRecord}")
            }
    }
}

@Service
class KafkaConsumer {
    @KafkaListener(topics = ["my-topic"], groupId = "my-group-test")
    fun listen(@Payload message: String, ack: Acknowledgment) {
        println("🔥 수신됨: $message")
        ack.acknowledge()
    }
}

 


🐳 2. Kafka는 Docker로 실행 (apache/kafka)

처음에는 Apache 공식 Kafka 이미지를 아래와 같이 설정했습니다.

docker run -d \
  --name kafka-container \
  -p 9092:9092 -p 9093:9093 \
  -e KAFKA_NODE_ID=1 \
  -e KAFKA_PROCESS_ROLES=broker,controller \
  -e KAFKA_CONTROLLER_QUORUM_VOTERS=1@localhost:9093 \
  -e KAFKA_LISTENERS=PLAINTEXT://localhost:9092,CONTROLLER://localhost:9093 \
  -e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://localhost:9092 \
  -e KAFKA_INTER_BROKER_LISTENER_NAME=PLAINTEXT \
  -e KAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER \
  -e KAFKA_CLUSTER_ID=KAFKACLUSTER-ID \
  -e KAFKA_LOG_DIRS=/kafka/kraft-combined-logs \
  -v ~/study/mq/kafka:/kafka/kraft-combined-logs \
  apache/kafka

하지만 예상치 못한 여러 문제가 발생했습니다.


🌀 3. Kafka의 Producer, Consumer란?

  • Producer: Kafka에 메시지를 전송
  • Consumer: Kafka 토픽에서 메시지를 수신
  • 동일 group-id를 가진 Consumer는 메시지를 파티셔닝하여 나눠서 처리함

⚠️ 4. KRaft 모드란?

Kafka는 원래 Zookeeper를 통해 클러스터 메타데이터를 관리했습니다.

Zookeeper의 역할

  • 브로커 등록 및 상태 감시
  • 컨트롤러 선출
  • 토픽/파티션 메타데이터 저장
  • 설정 값 관리

하지만 최근 Kafka는…

  • Kafka 2.8부터 KRaft 모드 도입 (실험적)
  • Kafka 3.3부터 Zookeeper 없이 KRaft만으로도 운영 가능

❌ 5. Apache Kafka 설정 실패

겪은 문제들

  • PLAINTEXT://host.docker.internal:9092 방식은 도커-호스트 간 네트워크 문제로 사용 불가
  • Producer는 정상 동작했지만 Consumer는 메시지를 수신하지 못함
  • Apache Kafka Docker에는 kafka-console-consumer.sh도 없음
  • offsets 토픽 관련 설정이 컨트롤러 내부에서 루프를 돌며 로그만 출력
  • isDone() && !isCompletedExceptionally() 상태에서 Producer는 성공했으나, Consumer는 실패

Apache Kafka Docker 이미지 문제

  • __consumer_offsets 토픽 자동 생성 시 offsets.topic.replication.factor 설정 필요
  • 하지만 Apache 이미지에서는 환경 변수 KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR 미지원
  • 이로 인해 다음 로그가 무한히 출력됨:
INFO Sent auto-creation request for Set(__consumer_offsets) to the active controller.
  • 메시지는 전송되지만 Received: 0 records 로그만 나오고 수신 안 됨

✅ 6. Bitnami Kafka로 전환 후 성공

Apache Kafka 이미지에 한계를 느끼고, Bitnami Kafka로 변경하여 문제를 해결했습니다.

# 네트워크 생성
docker network create kafka-net

# Zookeeper 컨테이너 실행
docker run -d --name zookeeper --network kafka-net \
  -e ALLOW_ANONYMOUS_LOGIN=yes \
  bitnami/zookeeper:latest

# Kafka 컨테이너 실행
docker run -d --name kafka --network kafka-net \
  -e KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181 \
  -e KAFKA_CFG_LISTENERS=PLAINTEXT://:9092 \
  -e KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://localhost:9092 \
  -e ALLOW_PLAINTEXT_LISTENER=yes \
  -p 9092:9092 \
  bitnami/kafka:latest

 


💡 Bitnami의 장점

  • 설정 키가 직관적 (KAFKA_CFG_*)
  • offsets.topic.replication.factor 적용됨
  • Spring Boot와 연동 테스트에서 정상 수신 확인됨
  • CLI 도구 포함 (kafka-console-consumer.sh, kafka-topics.sh 등)

📝 마무리

  • Apache Kafka Docker 이미지는 설정 자유도가 낮고 디버깅이 어려움
  • KRaft 모드로 설정 시 실무에서 유용하지 않을 수 있음
  • Docker 환경에서는 Bitnami Kafka 이미지가 안정성과 유연성 면에서 훨씬 실용적
  • Spring Boot에서 메시지 수신이 안 될 경우, 대부분은 offsets 관련 설정 문제일 가능성이 높음

이런 경험을 토대로 Kafka와 Spring Boot를 연동하는 경우에는 Bitnami Kafka 이미지 사용을 적극 권장합니다.

728x90