Kafka advertised.listeners 정리
— “붙었다가 안 되는” 이유는 항상 여기였다
Docker + 로컬 환경에서 Kafka를 띄웠을 때
연결은 되는데 consume/produce에서 터지는 문제를 정리한 글
이 글의 전제
이 글은 아래를 이미 알고 있다고 가정한다.
- Docker bridge 네트워크 개념
- localhost / service name / host.docker.internal 차이
- 로컬 애플리케이션 ↔ Docker 컨테이너 통신 구조
👉 위 개념이 없다면, 먼저 이 글을 참고하는 걸 추천한다.
👉 https://structuring.tistory.com/208
문제 상황 요약
다음과 같은 환경에서 Kafka를 사용하고 있었다.
- Kafka: Docker 컨테이너
- Kafka Connect: Docker 컨테이너
- 애플리케이션(Spring Boot): 로컬에서 실행
증상은 항상 비슷했다.
- Kafka에 처음 붙는 것처럼 보임
- 하지만:
- consume가 안 되거나
- produce 중간에 끊기거나
- metadata 요청에서 타임아웃 발생
로그만 보면 네트워크, 버전, 설정 문제처럼 보이지만
실제 원인은 거의 항상 advertised.listeners 설정이었다.
Kafka advertised.listeners는 대체 뭘까?
Kafka 클라이언트 연결 흐름은 다음과 같다.
1. bootstrap.servers 로 Kafka에 최초 접속
2. Kafka로부터 메타데이터 수신
3. Kafka가 알려준 주소(advertised)로 다시 접속
여기서 핵심은 3번이다.
Kafka는
“나한테 다시 붙을 땐 이 주소로 와”
라는 정보를 클라이언트에게 준다.
이 주소가 바로 advertised.listeners다.
왜 Docker + 로컬 환경에서 자주 터질까?
Docker 환경에서는 접속 주체가 둘로 나뉜다.
주체Kafka를 보는 위치
| Kafka Connect (컨테이너) | Docker bridge 내부 |
| 로컬 애플리케이션 | 로컬 머신 |
즉,
- 컨테이너 기준으로 유효한 주소
- 로컬 기준으로 유효한 주소
가 서로 다르다.
그런데 advertised를 하나만 써버리면 문제가 생긴다.
❌ 잘못된 예 (가장 흔한 실수)
KAFKA_CFG_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
이 경우:
- 로컬 애플리케이션: ⭕ (localhost 접근 가능)
- Kafka Connect 컨테이너: ❌
- 컨테이너에서 localhost는 자기 자신
- Kafka 없음 → 연결 실패
❌ 반대 케이스도 동일하게 터진다
KAFKA_CFG_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092
- Kafka Connect: ⭕
- 로컬 애플리케이션: ❌
- 로컬에는 kafka라는 DNS 없음
✅ 정답 패턴: 리스너 2개 + advertised 2개
해결 방법은 단순하다.
접속 주체 기준으로 “도달 가능한 주소”를 각각 제공한다.
Docker Compose 예시 (복붙용)
services:
kafka:
image: bitnami/kafka:3.6
container_name: kafka
ports:
- "19092:19092" # 로컬 접근용
environment:
KAFKA_CFG_NODE_ID: 1
KAFKA_CFG_PROCESS_ROLES: broker,controller
# 리스너 2개
KAFKA_CFG_LISTENERS: >
INTERNAL://:9092,
EXTERNAL://:19092,
CONTROLLER://:9093
# advertised 2개 (핵심)
KAFKA_CFG_ADVERTISED_LISTENERS: >
INTERNAL://kafka:9092,
EXTERNAL://localhost:19092
# 리스너 이름 매핑
KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP: >
INTERNAL:PLAINTEXT,
EXTERNAL:PLAINTEXT,
CONTROLLER:PLAINTEXT
# 브로커 간 통신은 내부용 사용
KAFKA_CFG_INTER_BROKER_LISTENER_NAME: INTERNAL
KAFKA_CFG_CONTROLLER_LISTENER_NAMES: CONTROLLER
KAFKA_CFG_CONTROLLER_QUORUM_VOTERS: 1@kafka:9093
ALLOW_PLAINTEXT_LISTENER: "yes"
이 설정을 해석하면
대상Kafka 접근 주소
| Kafka Connect (컨테이너) | kafka:9092 |
| 로컬 Spring Boot | localhost:19092 |
- 컨테이너는 Docker DNS 기준
- 로컬은 localhost 기준
- Kafka는 각각에게 맞는 주소를 알려줌
inter.broker.listener.name은 왜 INTERNAL인가?
KAFKA_CFG_INTER_BROKER_LISTENER_NAME: INTERNAL
의미는 단순하다.
브로커끼리 통신할 땐
외부용 주소(EXTERNAL)가 아니라
내부용 주소(INTERNAL)를 써라
- 브로커 간 통신은 항상 안정적인 내부 네트워크가 기준
- 외부 주소(localhost 등)를 쓰면 멀티 브로커에서 바로 문제 발생
이걸 이해하면 바뀌는 사고 방식
이제 Kafka 문제를 이렇게 보게 된다.
- ❌ “Kafka가 불안정한가?”
- ❌ “버전 문제인가?”
- ❌ “네트워크가 이상한가?”
👉 대신:
“이 Kafka가
이 클라이언트에게
접근 불가능한 주소를 광고하고 있지 않나?”
한 줄 결론
Kafka advertised.listeners는
‘이 Kafka에 붙는 상대방이 실제로 도달 가능한 주소’여야 한다.
Docker + 로컬 환경에서는
주소는 하나가 아니라, 관점별로 나뉜다.
마무리
Docker ↔ 로컬 환경 통신을 이해했다면,
Kafka advertised는 더 이상 어려운 개념이 아니다.
Kafka는 어렵지 않고,
주소를 잘못 알려주면 그냥 안 붙을 뿐이다.
이 글은
👉 https://structuring.tistory.com/208
( Docker / 로컬 환경 통신 정리 )
을 이해한 뒤 보면 가장 자연스럽다.
'DevOps > Docker' 카테고리의 다른 글
| Docker - Healthcheck, Liveness, Readiness (0) | 2026.02.01 |
|---|---|
| Docker - Docker와 로컬 환경 통신 (0) | 2026.01.31 |
댓글