본문 바로가기
DevOps/Docker

Docker - Kafka를 Docker로 띄우면 왜 로컬에서는 붙었다가 실패할까?

by 개발하는 경제학도 2026. 2. 1.

 

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

댓글