kafka(카프카) docker-compose로 클러스터 구축
docker-compose를 통해 다중 브로커를 만들어서 kafka를 실행하려 한다.
아래는 내가 작성한 docker-compose이다
1. kafka-docker.yml
version: '3.8'
services:
zookeeper-1:
image: confluentinc/cp-zookeeper:latest
ports:
- '32181:32181'
environment:
ZOOKEEPER_CLIENT_PORT: 32181
ZOOKEEPER_TICK_TIME: 2000
kafka-1:
image: confluentinc/cp-kafka:latest
ports:
- '9092:9092'
depends_on:
- zookeeper-1
environment:
KAFKA_BROKER_ID: 1
KAFKA_ZOOKEEPER_CONNECT: zookeeper-1:32181
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL
KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka-1:29092,EXTERNAL://localhost:9092
KAFKA_DEFAULT_REPLICATION_FACTOR: 3
KAFKA_NUM_PARTITIONS: 3
kafka-2:
image: confluentinc/cp-kafka:latest
ports:
- '9093:9093'
depends_on:
- zookeeper-1
environment:
KAFKA_BROKER_ID: 2
KAFKA_ZOOKEEPER_CONNECT: zookeeper-1:32181
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL
KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka-2:29093,EXTERNAL://localhost:9093
KAFKA_DEFAULT_REPLICATION_FACTOR: 3
KAFKA_NUM_PARTITIONS: 3
kafka-3:
image: confluentinc/cp-kafka:latest
ports:
- '9094:9094'
depends_on:
- zookeeper-1
environment:
KAFKA_BROKER_ID: 3
KAFKA_ZOOKEEPER_CONNECT: zookeeper-1:32181
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL
KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka-3:29094,EXTERNAL://localhost:9094
KAFKA_DEFAULT_REPLICATION_FACTOR: 3
KAFKA_NUM_PARTITIONS: 3
그리고, 카프카에 메세지들을 모니터링 하기 위해서 kafka-ui파일도 작성하자.
kafka-ui-docker.yml
version: '2'
services:
kafka-ui:
image: provectuslabs/kafka-ui
container_name: kafka-ui
ports:
- "8989:8080"
restart: always
environment:
- KAFKA_CLUSTERS_0_NAME=local
- KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS=kafka-1:29092,kafka-2:29093,kafka-3:29094
- KAFKA_CLUSTERS_0_ZOOKEEPER=zookeeper-1:22181
그런데, 내가 처음에 chat-gpt한테 받은 kafka-docker.yml이 있었는데 지피티에게 받은 도커컴포즈 파일을 띄우면 아래와 같은 에러가 발생했다.
version: '3.8'
services:
zookeeper:
image: confluentinc/cp-zookeeper:7.3.0
container_name: zookeeper
environment:
ZOOKEEPER_CLIENT_PORT: 2181
ZOOKEEPER_TICK_TIME: 2000
ports:
- "2181:2181"
kafka1:
image: confluentinc/cp-kafka:7.3.0
container_name: kafka1
ports:
- "9092:9092"
environment:
KAFKA_BROKER_ID: 1
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 3
KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true"
depends_on:
- zookeeper
kafka2:
image: confluentinc/cp-kafka:7.3.0
container_name: kafka2
ports:
- "9093:9093"
environment:
KAFKA_BROKER_ID: 2
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9093
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 3
KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true"
depends_on:
- zookeeper
kafka3:
image: confluentinc/cp-kafka:7.3.0
container_name: kafka3
ports:
- "9094:9094"
environment:
KAFKA_BROKER_ID: 3
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9094
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 3
KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true"
depends_on:
- zookeeper
volumes:
kafka_data:
그리고 다음은 에러이다.
kafka1 | [2024-09-18 08:40:53,761] WARN [Controller id=1, targetBrokerId=2] Connection to node 2 (localhost/127.0.0.1:9093) could not be established. Broker may not be available. (org.apache.kafka.clients.NetworkClient)
kafka1 | [2024-09-18 08:40:53,761] WARN [RequestSendThread controllerId=1] Controller 1's connection to broker localhost:9093 (id: 2 rack: null) was unsuccessful (kafka.controller.RequestSendThread)
kafka1 | java.io.IOException: Connection to localhost:9093 (id: 2 rack: null) failed.
kafka1 | at org.apache.kafka.clients.NetworkClientUtils.awaitReady(NetworkClientUtils.java:70)
kafka1 | at kafka.controller.RequestSendThread.brokerReady(ControllerChannelManager.scala:292)
kafka1 | at kafka.controller.RequestSendThread.doWork(ControllerChannelManager.scala:246)
kafka1 | at kafka.utils.ShutdownableThread.run(ShutdownableThread.scala:96)
kafka1 | [2024-09-18 08:40:53,761] INFO [Controller id=1, targetBrokerId=2] Client requested connection close from node 2 (org.apache.kafka.clients.NetworkClient)
kafka1 | [2024-09-18 08:40:53,762] INFO [Controller id=1, targetBrokerId=3] Node 3 disconnected. (org.apache.kafka.clients.NetworkClient)
해당 에러로그는 네트워크 연결 문제로 나타나는데, kafka2,kafka3의 브로커에 연결이 실패하는 에러이다.
이 에러의 원인은
- 연결 실패:
- Connection to node 2 (localhost/127.0.0.1:9093) could not be established. 라는 메시지가 나타나며, kafka1 브로커가 kafka2 브로커 (localhost:9093 포트에서 실행 중인 브로커)와 연결하려고 했지만 실패했습니다.
- Node 3 disconnected. 라는 메시지는 kafka1 브로커가 kafka3 브로커와도 연결이 끊어졌다는 의미입니다.
- 가능한 원인:
- 브로커가 올바르게 실행되지 않았거나 충돌한 경우일 수 있습니다.
- 브로커 간 포트 충돌 또는 네트워크 문제가 있을 수 있습니다. localhost:9093에서 kafka2 브로커가 제대로 실행되지 않았거나 접근이 불가능한 상황일 수 있습니다.
- Zookeeper와의 연결 또는 브로커 설정에서 잘못된 네트워크 구성이 있을 수 있습니다.
해결 방법:
- 각 브로커가 올바르게 실행 중인지 확인:
- 각 Kafka 브로커(kafka1, kafka2, kafka3)가 실행 중인지 확인하고, 각 브로커의 로그를 확인하여 오류가 발생했는지 확인합니다.
- docker-compose logs kafka2 및 docker-compose logs kafka3 명령어를 사용해 kafka2, kafka3의 로그를 확인하세요.
- 포트 및 네트워크 설정 확인:
- 각 브로커가 적절한 포트에서 실행 중인지 확인하세요. 예를 들어, kafka2는 localhost:9093에서 실행되어야 하고, kafka3는 localhost:9094에서 실행되어야 합니다.
- KAFKA_ADVERTISED_LISTENERS 환경 변수가 올바르게 설정되었는지 확인하세요. 예를 들어, localhost 대신 컨테이너 간 네트워크를 고려하여 IP 주소 또는 컨테이너 이름을 사용하는 것이 좋습니다.
- 컨테이너 네트워크 설정 확인:
- Kafka 브로커들이 동일한 네트워크에 속해 있어야 합니다. docker-compose 설정에서 네트워크 설정을 확인하거나, 기본적으로 설정된 네트워크가 올바르게 동작하는지 확인하세요.
- Zookeeper와의 연결 확인:
- Zookeeper가 모든 브로커에 대해 정상적으로 실행 중인지 확인하고, Zookeeper 로그에서 에러가 발생했는지 확인하세요.
이렇게 나눌 수 있었다.
그리고, 맨 위의 정상 작동하는 도커 컴포즈와 지피티의 도커컴포즈 파일은 아래와 같은 차이점을 볼 수 있었다.
당신이 제공한 코드와 제가 제공한 코드의 차이점은 몇 가지 중요한 설정과 포트 구성에 있습니다. 아래에서 주요 차이점들을 분석해 보겠습니다.
1. Zookeeper 설정 차이
- 당신의 코드: confluentinc/cp-zookeeper:latest 이미지를 사용하며, ZOOKEEPER_CLIENT_PORT를 32181로 설정했습니다.
- 내 코드: wurstmeister/zookeeper:3.4.6 이미지를 사용했으며, 기본 2181 포트를 사용했습니다.
차이점: Confluent의 cp-zookeeper 이미지와 wurstmeister 이미지 간의 차이가 있습니다. Confluent의 이미지는 Confluent Kafka 패키지와 더 호환성이 높고 더 최신입니다. 또한, 당신의 코드는 32181 포트를 사용하고 있습니다.
2. Kafka의 Listener 설정
- 당신의 코드: KAFKA_LISTENER_SECURITY_PROTOCOL_MAP 및 KAFKA_ADVERTISED_LISTENERS 설정을 통해 두 가지 리스너를 설정했습니다. 하나는 내부용(INTERNAL)이고, 다른 하나는 외부용(EXTERNAL)입니다.
- 내부 리스너는 브로커 간 통신을 위한 PLAINTEXT 프로토콜을 사용합니다.
- 외부 리스너는 클라이언트가 접근할 수 있는 PLAINTEXT 프로토콜을 사용하며, localhost로 접근할 수 있게 설정했습니다.
- 내 코드: KAFKA_ADVERTISED_LISTENERS에서 localhost를 사용하지 않고, 단일 리스너를 PLAINTEXT로 설정했습니다.
차이점: 당신의 코드는 내부 통신과 외부 클라이언트 접근을 분리하여 관리하고 있습니다. 이를 통해 브로커 간 통신과 클라이언트 통신을 분리할 수 있어 더 유연한 설정을 제공합니다.
3. 브로커 간 통신 포트 차이
- 당신의 코드: 내부 리스너를 위한 포트로 29092, 29093, 29094를 사용하고 있습니다. 각 브로커의 내부 통신에만 사용되는 포트를 별도로 지정했습니다.
- 내 코드: 모든 브로커에서 기본 Kafka 포트를 사용했습니다 (9092, 9093, 9094).
차이점: 당신의 코드는 브로커 간 통신을 위해 별도의 포트를 할당했으며, 외부 클라이언트와의 통신을 위한 포트를 분리하여 좀 더 세밀한 네트워크 설정을 제공하고 있습니다.
4. Replication Factor와 Partition 수
- 당신의 코드: KAFKA_DEFAULT_REPLICATION_FACTOR와 KAFKA_NUM_PARTITIONS 값을 명시적으로 설정했습니다.
- Replication Factor: 3 (즉, 데이터의 복제를 3개의 브로커에 분산 저장)
- Partition 수: 3 (각 토픽에 대해 3개의 파티션으로 나눔)
- 내 코드: 이러한 값들은 기본값을 사용하며, 명시적으로 설정되지 않았습니다.
해당 차이점을 유의하면서 카프카를 도커 컴포즈로 무사히 띄울 수 있었다.
**** 클러스터링을 해봤는데 도커컨테이너끼리 통신이 안되어서 아래의 도커컴포즈로 대체하여 카프카 ui와 kafka를 연동함****
version: '3.8'
services:
zookeeper-1:
image: confluentinc/cp-zookeeper:latest
ports:
- '32181:32181'
environment:
ZOOKEEPER_CLIENT_PORT: 32181
ZOOKEEPER_TICK_TIME: 2000
kafka-1:
image: confluentinc/cp-kafka:latest
ports:
- '9092:9092'
depends_on:
- zookeeper-1
environment:
KAFKA_BROKER_ID: 1
KAFKA_ZOOKEEPER_CONNECT: zookeeper-1:32181
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL
KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka-1:29092,EXTERNAL://localhost:9092
KAFKA_LISTENERS: INTERNAL://0.0.0.0:29092,EXTERNAL://0.0.0.0:9092
KAFKA_DEFAULT_REPLICATION_FACTOR: 3
KAFKA_NUM_PARTITIONS: 3
# 메시지 크기 제한을 늘리는 설정 추가
KAFKA_MESSAGE_MAX_BYTES: 200000000
KAFKA_REPLICA_FETCH_MAX_BYTES: 200000000
kafka-2:
image: confluentinc/cp-kafka:latest
ports:
- '9093:9093'
depends_on:
- zookeeper-1
environment:
KAFKA_BROKER_ID: 2
KAFKA_ZOOKEEPER_CONNECT: zookeeper-1:32181
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL
KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka-2:29093,EXTERNAL://localhost:9093
KAFKA_LISTENERS: INTERNAL://0.0.0.0:29093,EXTERNAL://0.0.0.0:9093
KAFKA_DEFAULT_REPLICATION_FACTOR: 3
KAFKA_NUM_PARTITIONS: 3
# 메시지 크기 제한을 늘리는 설정 추가
KAFKA_MESSAGE_MAX_BYTES: 200000000
KAFKA_REPLICA_FETCH_MAX_BYTES: 200000000
kafka-3:
image: confluentinc/cp-kafka:latest
ports:
- '9094:9094'
depends_on:
- zookeeper-1
environment:
KAFKA_BROKER_ID: 3
KAFKA_ZOOKEEPER_CONNECT: zookeeper-1:32181
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL
KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka-3:29094,EXTERNAL://localhost:9094
KAFKA_LISTENERS: INTERNAL://0.0.0.0:29094,EXTERNAL://0.0.0.0:9094
KAFKA_DEFAULT_REPLICATION_FACTOR: 3
KAFKA_NUM_PARTITIONS: 3
# 메시지 크기 제한을 늘리는 설정 추가
KAFKA_MESSAGE_MAX_BYTES: 200000000
KAFKA_REPLICA_FETCH_MAX_BYTES: 200000000
kafka-ui:
image: provectuslabs/kafka-ui
container_name: kafka-ui
depends_on:
- kafka-1
- kafka-2
- kafka-3
ports:
- "8989:8080"
restart: always
environment:
- KAFKA_CLUSTERS_0_NAME=local
- KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS=kafka-1:29092,kafka-2:29093,kafka-3:29094
- KAFKA_CLUSTERS_0_ZOOKEEPER=zookeeper-1:22181
참조: https://devocean.sk.com/blog/techBoardDetail.do?ID=163980