Kafka 클라이언트는 두 단계로 접속한다. 먼저 아무 브로커에 붙어(bootstrap) 클러스터 정보를 받고, 브로커가 광고한 주소(advertised listener)로 다시 연결한다. 광고 주소가 클라이언트 입장에서 닿지 않는 주소면 "첫 연결은 되는데 메시지를 못 주고받는" 이상한 증상이 생긴다.
도커에서 생기는 문제
브로커가 컨테이너 안에 있으면 접속하는 쪽에 따라 맞는 주소가 다르다.
- 같은 도커 네트워크의 컨테이너에게는
kafka:29092(도커 DNS 이름)가 맞다(컨테이너 네트워킹) - 호스트에서 도는 프로세스에게는
kafka라는 이름이 풀리지 않는다. 호스트에 게시한 포트인localhost:9092가 맞다
광고 주소가 하나뿐이면 둘 중 한쪽은 bootstrap만 성공하고 실제 연결에서 실패한다. 해결책은 리스너를 용도별로 나눠 각각 다른 주소를 광고하는 것이다.
flowchart LR
H["호스트의 프로세스"]
subgraph N["도커 네트워크"]
C["다른 컨테이너"]
subgraph K["kafka 컨테이너"]
I["INTERNAL 리스너"]
E["EXTERNAL 리스너"]
end
end
C -- "kafka:29092" --> I
H -- "localhost:9092 (게시한 포트)" --> E
environment:
# KRaft 설정(KAFKA_PROCESS_ROLES, KAFKA_CONTROLLER_LISTENER_NAMES 등)은 생략
KAFKA_LISTENERS: INTERNAL://0.0.0.0:29092,EXTERNAL://0.0.0.0:9092,CONTROLLER://0.0.0.0:29093
KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka:29092,EXTERNAL://localhost:9092
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT,CONTROLLER:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL
ports:
- "9092:9092"LISTENERS는 브로커가 실제로 바인드하는 주소,ADVERTISED_LISTENERS는 클라이언트에게 알려 주는 주소다- 컨테이너 클라이언트는
kafka:29092로, 호스트 클라이언트는localhost:9092로 bootstrap한다
그 밖의 로컬 설정
- 로컬에서 편하려고
auto.create.topics.enable=true를 켜지만, 운영에서는 끄고 토픽을 명시적으로 만든다. 토픽 이름 오타가 유령 토픽을 만드는 걸 막기 위해서다
Kafka 자체 개념은 Kafka를 보고, "안에서 보는 주소와 밖에서 보는 주소가 다르다"는 일반 원리는 컨테이너 네트워킹와 DNS와 CNAME를 본다. 이 증상을 디버깅할 때는 bootstrap과 실제 연결을 분리해 확인하는 게 첫걸음이다(디버깅 문제 정의 5단계).