요새 kafka로 인해서 정말 거짓말 안하고 흰머리가 많이 생기는 거 같다.
오늘은 kafka를 이용하여 주문 서비스랑 상품 서비스에서 사용을 했는데
connect도 잘 실행중이겠지 하고 봤는데 서버가 죽어있다.
결론
Kafka에 INTERNAL / EXTERNAL listener가 존재하는 이유는 Kafka 브로커에 접근하는 환경이 서로 다르기 때문이다.
Kafka는 하나의 포트/ 주소로 모든 환경을 처리할 수 없다.
그래서 Kafka는 두 종류로 나누어야 한다.
EXTERNAL Listener
Docker 밖(호스트)에서 접근할 때 필요
Spring Boot, CLI, Postman 등
접근 주소 : localhost:9092
INTERNAL Listener
Docker 안(컨테이너 내부)에서 접근할 때 필요
Kafka Connect, Schema Registry, 다른 Kafka 컨테이너
접근 주소 : kafka:19092
접근하는 주체가 다르면 Kafka가 노출해주는 주소도 달라야 한다.
그래서 Listener를 2개 쓰는 것이다.
왜 필요한가?
1. Docker 내부에서 localhost는 "컨테이너 자신"이다.
Kafka Connect가 컨테이너 안에서 localhost:9092로 kafka를 찾으면
-> 자기 자신 connect 컨테이너를 바라본다.
-> Kafka를 찾지 못하고 빌드에 실패한다.
그래서 Docker 내부에서는 반드시 컨테이너 이름(kafka)로 접근해야한다.
kafka:9092
2. Docker 외부에서 kafka:9092는 존재하지 않는다.
Spring Boot는 맥북에서 바로 실행된 애플리케이션이다.
맥북에서는 kafka:9092로 찾으면
-> kafka로 시작하는 도메인이 없다.
-> 연결 실패.
그래서 Spring Boot에서는 localhost:9092로 사용을 해야한다.
localhost:9092
3. Kafka는 둘 다 들어오는 요청을 동시에 처리할 수 있어야 한다.
Spring Boot -> localhost:9092
Kafka Connect -> kafka:19092
Kafka가 이 두 요청을 동시에 받아줘야 하므로 Listener를 두 개 만들어야 한다.
멀티 리스너 구조
Kafka는 listener를 여러 개 선언해서 각 환경별로 서로 다른 주소를 노출할 수 있는 구조를 가지고 있다.
1) 실제 브로커가 바인딩하는 포트
INTERNAL://0.0.0.0:19092
EXTERNAL://0.0.0.0:9092
2) 클라이언트에게 광고(adverties)하는 주소
INTERNAL://kafka:19092
EXTERNAL://localhost:9092
내부 / 외부 접근을 구분하는 이유
컨테이너 내부와 외부는 "Kafka를 바라보는 시선"이 다르기 때문에 Kafka도 각각에 맞는 주소를 따로 제공해야 한다.
Kafka listener 구조는 이 문제를 해결하기 위해 존재한다고 한다.
docker-compose.yml
networks:
ecommerce:
external: true
services:
zookeeper:
image: confluentinc/cp-zookeeper:7.5.0
container_name: zookeeper
ports:
- "2181:2181"
environment:
ZOOKEEPER_CLIENT_PORT: 2181
networks:
- ecommerce
kafka:
image: confluentinc/cp-kafka:7.5.0
container_name: kafka
ports:
- "9092:9092"
depends_on:
- zookeeper
environment:
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
# INTERNAL(컨테이너) + EXTERNAL(호스트) 리스너 설정
KAFKA_LISTENERS: INTERNAL://0.0.0.0:19092,EXTERNAL://0.0.0.0:9092
KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka:19092,EXTERNAL://localhost:9092
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
networks:
- ecommerce
connect:
image: confluentinc/cp-kafka-connect:7.5.1
container_name: kafka-connect
depends_on:
- kafka
ports:
- "8083:8083"
environment:
CONNECT_BOOTSTRAP_SERVERS: kafka:19092
CONNECT_REST_PORT: 8083
CONNECT_REST_ADVERTISED_HOST_NAME: kafka-connect
CONNECT_REST_ADVERTISED_PORT: 8083
CONNECT_GROUP_ID: "connect-cluster"
CONNECT_CONFIG_STORAGE_TOPIC: connect-configs
CONNECT_OFFSET_STORAGE_TOPIC: connect-offsets
CONNECT_STATUS_STORAGE_TOPIC: connect-status
CONNECT_CONFIG_STORAGE_REPLICATION_FACTOR: 1
CONNECT_OFFSET_STORAGE_REPLICATION_FACTOR: 1
CONNECT_STATUS_STORAGE_REPLICATION_FACTOR: 1
CONNECT_KEY_CONVERTER: "org.apache.kafka.connect.storage.StringConverter"
CONNECT_VALUE_CONVERTER: "org.apache.kafka.connect.json.JsonConverter"
CONNECT_PLUGIN_PATH: "/usr/share/confluent-hub-components"
volumes:
- .:/usr/share/confluent-hub-components
networks:
- ecommerce
Kafka Internal / External 구조
┌───────────────────────────────┐
│ HOST │
│ (Mac / Local PC) │
└───────────────────────────────┘
▲
│ EXTERNAL Listener
│ localhost:9092
│
┌──────────────────────────────────────┐
│ Docker Network │
│ (ecommerce) │
└──────────────────────────────────────┘
│
│ INTERNAL Listener
│ kafka:19092
▼
┌───────────────────────────────┐
│ Kafka │
│ INTERNAL://kafka:19092 │
│ EXTERNAL://localhost:9092 │
└───────────────────────────────┘
▲ ▲
│ │
│(컨테이너 ㅡ내부) │ (호스트 외부)
│ │
┌─────────────┐ ┌───────────────────┐
│ Connect │ │ Spring Boot │
│ kafka:19092 │ │ localhost:9092 │
└─────────────┘ └───────────────────┘
POINT
Docker 내부 컨테이너는 kafka:19092로 접근
PC 외부 애플리케이션은 localhost:9092로 접근
Kafka는 두 요청을 모두 받기 위해 INTERNAL과 EXTERNAL Listener 두 개를 제공
정리
아주 쉽게 비유하자면 Kafka는 회사 건물이고, Spring Boot는 일반 방문자, Kafka Connect는 직원이라고 했을 때,
직원(Internal)은 뒷문으로 들어올 수 있지만, 방문객(external)은 정문으로 들어온다.
문은 서로 다르지만, 둘 다 동일한 회사(kafka)로 접근해야 한다.
그래서 두 개의 문을 개방하는 것이다. internal, external.
'TIL & STUDY' 카테고리의 다른 글
| 깃허브 액션으로 CI/CD 자동 배포 만들기 (0) | 2026.06.24 |
|---|---|
| Nginx SSL 인증서 갱신 기록 (key.pem | cert.pem | dec_key.pem) (0) | 2026.06.18 |
| TIL 19 : docker zookeeper & kafka & kafka connect setting (0) | 2025.11.21 |
| TIL 18 : 도커로 주키퍼 & 카프카 실행 및 토픽 생성 테스트 (0) | 2025.11.19 |
| TIL 17 : 블로킹과 논블로킹 (0) | 2025.11.08 |