riot 프로젝트를 진행하면서 카프카 클러스터를 구축했었는데
새로 산 맥북에서 기존의 환경을 구축하려고 했더니 쉽게 되지 않았다.
관련 내용을 해결했던 과정을 쓰고자 한다.
기존에 도커를 활용하여 카프카 클러스터링을 구축했었는데
기존에 구성했던 카프카 클러스터링의 docker-compose.yml은 다음과 같다.
version: '3.3'
services:
zookeeper:
image: confluentinc/cp-zookeeper:latest
ports:
- "2181:2181"
- "2888:2888"
- "3888:3888"
healthcheck:
test: echo stat | nc localhost 2181
interval: 10s
timeout: 10s
retries: 3
environment:
- ZOOKEEPER_SERVER_ID=1
- ZOOKEEPER_CLIENT_PORT=2181
- ZOOKEEPER_TICK_TIME=2000
- ZOOKEEPER_INIT_LIMIT=5
- ZOOKEEPER_SYNC_LIMIT=2
- ZOOKEEPER_SERVERS=zookeeper:2888:3888
kafka1:
image: confluentinc/cp-kafka:latest
healthcheck:
test: ps augwwx | egrep [S]upportedKafka
depends_on:
- zookeeper
ports:
- "9091:9091"
environment:
- KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://localhost:9091
- KAFKA_LISTENERS=PLAINTEXT://0.0.0.0:9091
- KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181
- KAFKA_BROKER_ID=1
- BOOTSTRAP_SERVERS=kafka1:9091,kafka2:9092,kafka3:9093
- ZOOKEEPER=zookeeper:2181
kafka2:
image: confluentinc/cp-kafka:latest
healthcheck:
test: ps augwwx | egrep [S]upportedKafka
depends_on:
- zookeeper
ports:
- "9092:9092"
environment:
- KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://localhost:9092
- KAFKA_LISTENERS=PLAINTEXT://0.0.0.0:9092
- KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181
- KAFKA_BROKER_ID=2
- BOOTSTRAP_SERVERS=kafka1:9091,kafka2:9092,kafka3:9093
- ZOOKEEPER=zookeeper:2181
kafka3:
image: confluentinc/cp-kafka:latest
healthcheck:
test: ps augwwx | egrep [S]upportedKafka
depends_on:
- zookeeper
ports:
- "9093:9093"
environment:
- KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://localhost:9093
- KAFKA_LISTENERS=PLAINTEXT://0.0.0.0:9093
- KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181
- KAFKA_BROKER_ID=3
- BOOTSTRAP_SERVERS=kafka1:9091,kafka2:9092,kafka3:9093
- ZOOKEEPER=zookeeper:2181
kafdrop:
image: obsidiandynamics/kafdrop
restart: "no"
ports:
- "9000:9000"
environment:
KAFKA_BROKER_CONNECT: "kafka1:9091"
depends_on:
- kafka1
- kafka2
- kafka3
실행해 보니 kafdrop은 mac os에서는 제대로 지원이 되지 않는 것 같았다.
그래서 먼저 kafdrop을 다른 걸로 대체하기로 하였다.
모니터링하기 위한 다른 ui 도구를 찾다가 해당 kafka-ui를 사용하기로 하였다.
https://hub.docker.com/r/provectuslabs/kafka-ui
Docker
hub.docker.com
kafka-ui는 카프카 클러스터링 관리를 위한 무료 오픈소스 웹 UI이다.
깃허브에는 자세한 사용 방법도 나와있다.
https://github.com/provectus/kafka-ui
kafka-ui:
image: provectuslabs/kafka-ui
ports:
- "10000:8080"
restart: always
environment:
- KAFKA_CLUSTERS_0_NAME=local
- KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS=kafka1:9091,kafka2:9092,kafka3:9093
- KAFKA_CLUSTERS_0_ZOOKEEPER=zookeeper:2181
kafdrop을 제거하고 kafka-ui를 도커 컴포즈에 추가하였다. 다시 도커 컴포즈를 실행하였다.
kafka-ui는 제대로 실행 됐지만 kafka와의 연결은 실패하였다.
관련 에러를 수정하기 위해 웹 서핑을 하다가 다른 내용을 봐버렸다.
카프카 클러스터링 시에 주키퍼도 클러스터의 안정성을 위해 3개 이상의 노드로 구성하는 것이 권장된다는 글을 보았다.
궁금해서 챗지피티에 검색해 보았는데 챗지피티의 답변은 다음과 같다.
주키퍼 앙상블을 3개 이상의 노드로 구성하는 것은 다음과 같은 이점을 가지게 됩니다:
높은 가용성: 주키퍼 앙상블 내에서 한 노드의 장애가 발생해도 다른 노드가 여전히 서비스를 제공하므로 전체 시스템의 가용성이 유지됩니다.
분산 투표 시스템: 주키퍼는 분산 투표 알고리즘을 사용하여 결정을 내립니다. 이때 홀수 개의 노드가 필요한데, 3개 이상의 노드로 구성하면 다수결을 쉽게 결정할 수 있습니다.
분산 데이터 저장: 주키퍼는 분산 데이터베이스처럼 작동하여 클라이언트의 상태 정보를 저장하고 유지합니다. 노드가 추가되거나 제거될 때에도 데이터의 일관성과 안정성을 유지할 수 있습니다.
일반적으로 카프카 클러스터 내의 주키퍼 앙상블은 3개 혹은 5개 노드로 구성하는 것이 일반적인 모범 사례입니다.
다음과 같은 답변을 받아서 우선 주키퍼를 3개의 노드로 구성하기로 하였다.
zookeeper-1:
image: confluentinc/cp-zookeeper:latest
hostname: zookeeper-1
ports:
- "12181:12181"
environment:
ZOOKEEPER_SERVER_ID: 1
ZOOKEEPER_CLIENT_PORT: 12181
ZOOKEEPER_TICK_TIME: 2000
ZOOKEEPER_INIT_LIMIT: 5
ZOOKEEPER_SYNC_LIMIT: 2
ZOOKEEPER_SERVERS: zookeeper-1:12888:13888;zookeeper-2:22888:23888;zookeeper-3:32888:33888
zookeeper-2:
image: confluentinc/cp-zookeeper:latest
hostname: zookeeper-2
ports:
- "22181:22181"
environment:
ZOOKEEPER_SERVER_ID: 2
ZOOKEEPER_CLIENT_PORT: 22181
ZOOKEEPER_TICK_TIME: 2000
ZOOKEEPER_INIT_LIMIT: 5
ZOOKEEPER_SYNC_LIMIT: 2
ZOOKEEPER_SERVERS: zookeeper-1:12888:13888;zookeeper-2:22888:23888;zookeeper-3:32888:33888
zookeeper-3:
image: confluentinc/cp-zookeeper:latest
hostname: zookeeper-3
ports:
- "32181:32181"
environment:
ZOOKEEPER_SERVER_ID: 3
ZOOKEEPER_CLIENT_PORT: 32181
ZOOKEEPER_TICK_TIME: 2000
ZOOKEEPER_INIT_LIMIT: 5
ZOOKEEPER_SYNC_LIMIT: 2
ZOOKEEPER_SERVERS: zookeeper-1:12888:13888;zookeeper-2:22888:23888;zookeeper-3:32888:33888
다음과 같이 구성하였다.
실행하였더니 정상적으로 컨테이너 실행은 되었으나 여전히 다음과 같이 호스트 머신과 카프카와의 연결이 되지 않았다.
또한 로컬 환경의 인텔리제이에서 카프카와의 연결을 구현한 스프링 부트 모듈을 실행을 해봤을 때도 다음과 같은 타임아웃 에러가 발생했다.
https://github.com/hamwoojo/riot-api
java.util.concurrent.TimeoutException: null
at java.base/java.util.concurrent.CompletableFuture.timedGet(CompletableFuture.java:1960) ~[na:na]
at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2095) ~[na:na]
at org.apache.kafka.common.internals.KafkaFutureImpl.get(KafkaFutureImpl.java:180) ~[kafka-clients-3.1.2.jar:na]
at org.springframework.kafka.core.KafkaAdmin.initialize(KafkaAdmin.java:214) ~[spring-kafka-3.0.4.jar:3.0.4]
at org.springframework.kafka.core.KafkaAdmin.afterSingletonsInstantiated(KafkaAdmin.java:183) ~[spring-kafka-3.0.4.jar:3.0.4]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:974) ~[spring-beans-5.3.26.jar:5.3.26]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:920) ~[spring-context-5.3.26.jar:5.3.26]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.26.jar:5.3.26]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.10.jar:2.7.10]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:731) ~[spring-boot-2.7.10.jar:2.7.10]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) ~[spring-boot-2.7.10.jar:2.7.10]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) ~[spring-boot-2.7.10.jar:2.7.10]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1303) ~[spring-boot-2.7.10.jar:2.7.10]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1292) ~[spring-boot-2.7.10.jar:2.7.10]
at riot.api.data.engineer.DataEngineerApplication.main(DataEngineerApplication.java:10) ~[classes/:na]
해당 원인은 다음과 같았다.
결론부터 말하면, 호스트 머신과 컨테이너 간의 통신을 하기 위해서는 localhost가 아니라 host.docker.internal 도메인을 사용해야 한다.
host.docker.internal은 macOS 환경에서 Docker 컨테이너 내부에서 호스트 머신을 가리키는 도메인 이름이다.
macOS에서 Docker를 사용할 때, 이 도메인을 사용하여 컨테이너 내부와 호스트 머신 간의 통신을 수행할 수 있다.
최종적으로 수정한 docker-compose.yml 파일은 다음과 같다.
version: '3.7'
services:
zookeeper-1:
image: confluentinc/cp-zookeeper:latest
hostname: zookeeper-1
ports:
- "12181:12181"
environment:
ZOOKEEPER_SERVER_ID: 1
ZOOKEEPER_CLIENT_PORT: 12181
ZOOKEEPER_TICK_TIME: 2000
ZOOKEEPER_INIT_LIMIT: 5
ZOOKEEPER_SYNC_LIMIT: 2
ZOOKEEPER_SERVERS: zookeeper-1:12888:13888;zookeeper-2:22888:23888;zookeeper-3:32888:33888
zookeeper-2:
image: confluentinc/cp-zookeeper:latest
hostname: zookeeper-2
ports:
- "22181:22181"
environment:
ZOOKEEPER_SERVER_ID: 2
ZOOKEEPER_CLIENT_PORT: 22181
ZOOKEEPER_TICK_TIME: 2000
ZOOKEEPER_INIT_LIMIT: 5
ZOOKEEPER_SYNC_LIMIT: 2
ZOOKEEPER_SERVERS: zookeeper-1:12888:13888;zookeeper-2:22888:23888;zookeeper-3:32888:33888
zookeeper-3:
image: confluentinc/cp-zookeeper:latest
hostname: zookeeper-3
ports:
- "32181:32181"
environment:
ZOOKEEPER_SERVER_ID: 3
ZOOKEEPER_CLIENT_PORT: 32181
ZOOKEEPER_TICK_TIME: 2000
ZOOKEEPER_INIT_LIMIT: 5
ZOOKEEPER_SYNC_LIMIT: 2
ZOOKEEPER_SERVERS: zookeeper-1:12888:13888;zookeeper-2:22888:23888;zookeeper-3:32888:33888
kafka1:
image: confluentinc/cp-kafka:latest
healthcheck:
test: ps augwwx | egrep [S]upportedKafka
depends_on:
- zookeeper-1
- zookeeper-2
- zookeeper-3
ports:
- "9091:9091"
environment:
- KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://host.docker.internal:9091
- KAFKA_LISTENERS=PLAINTEXT://0.0.0.0:9091
- KAFKA_ZOOKEEPER_CONNECT=zookeeper-1:12181,zookeeper-2:22181,zookeeper-3:32181
- KAFKA_BROKER_ID=1
- BOOTSTRAP_SERVERS=host.docker.internal:9091,host.docker.internal:9092,host.docker.internal:9093
- ZOOKEEPER=zookeeper-1:12181
kafka2:
image: confluentinc/cp-kafka:latest
healthcheck:
test: ps augwwx | egrep [S]upportedKafka
depends_on:
- zookeeper-1
- zookeeper-2
- zookeeper-3
ports:
- "9092:9092"
environment:
- KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://host.docker.internal:9092
- KAFKA_LISTENERS=PLAINTEXT://0.0.0.0:9092
- KAFKA_ZOOKEEPER_CONNECT=zookeeper-1:12181,zookeeper-2:22181,zookeeper-3:32181
- KAFKA_BROKER_ID=2
- BOOTSTRAP_SERVERS=host.docker.internal:9091,host.docker.internal:9092,host.docker.internal:9093
- ZOOKEEPER=zookeeper-1:12181,zookeeper-2:22181,zookeeper-3:32181
kafka3:
image: confluentinc/cp-kafka:latest
healthcheck:
test: ps augwwx | egrep [S]upportedKafka
depends_on:
- zookeeper-1
- zookeeper-2
- zookeeper-3
ports:
- "9093:9093"
environment:
- KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://host.docker.internal:9093
- KAFKA_LISTENERS=PLAINTEXT://0.0.0.0:9093
- KAFKA_ZOOKEEPER_CONNECT=zookeeper-1:12181,zookeeper-2:22181,zookeeper-3:32181
- KAFKA_BROKER_ID=3
- BOOTSTRAP_SERVERS=kafka1:9091,kafka2:9092,kafka3:9093
- ZOOKEEPER=zookeeper-1:12181,zookeeper-2:22181,zookeeper-3:32181
kafka-ui:
image: provectuslabs/kafka-ui
container_name: kafka-ui
ports:
- "10000:8080"
restart: always
environment:
- KAFKA_CLUSTERS_0_NAME=local
- KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS=host.docker.internal:9091,host.docker.internal:9092,host.docker.internal:9093
- KAFKA_CLUSTERS_0_ZOOKEEPER=zookeeper-1:12181,zookeeper-2:22181,zookeeper-3:32181
localhost를 host.docker.internal로 수정하였다.
그리고 호스트 머신의 ip를 찾아야 하는데 해당 명령어는 다음과 같다.
ifconfig | grep "inet "
ip를 찾았으면 hosts 파일에 등록한다.
sudo vi /etc/hosts
{ip} host.docker.internal
해당 내용을 제일 마지막 줄에 추가해 준다.
다시 도커 컴포즈를 실행해 보자.
kakfa-ui를 확인해 보니 제대로 연결된 걸 확인했다. 인텔리제이에서 스프링 부트 모듈을 실행해 보자.
해당 모듈에는 topic이 자동으로 생성되게 해 놓았는데, 정상적으로 실행이 완료된다면 5개의 토픽이 생길 것이다.
정상적으로 토픽이 생성된 걸 확인할 수 있다.
마지막으로 토픽에 제대로 메시지가 들어오는지 확인하기 위해서
리그 오브 레전드 게임의 item 정보를 가져오는 api를 호출하였다.
아이템 정보가 잘 가져와지는 걸 확인했다. 얼마 안 걸릴 줄 알았는데 구축하는데 생각보다 오랜 시간이 걸렸다.
아무래도 mac을 처음 써봐서 mac 관련 단축키나 기능을 배우는 데도 시간이 걸려서 그랬던 것 같다.
다음엔 해당 프로젝트에 스웨거(Swagger)를 붙이거나, 아니면 Logstash와 ElasticSearch를 macOS에 구축해보려고 한다.
'Side Project > Riot Project' 카테고리의 다른 글
Spring Boot 프로젝트 DB 마이그레이션 flyway 적용하기 (0) | 2023.08.06 |
---|