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
'BackEnd' 카테고리의 다른 글
K6란? 성능 테스트 도구 소개 및 실습 결과 분석 (0) | 2025.06.20 |
---|---|
[MQ]메시지 큐 3개 비교: Amazon SQS vs Apache Kafka vs RabbitMQ (0) | 2025.06.17 |
[MQ] Kafka란? (0) | 2025.06.17 |
[MQ] SQS란? (0) | 2025.06.17 |
[Monitoring]Promtail + Loki 설치 및 연결(Docker 기반) (0) | 2025.06.14 |