[More Kafka]2. 주키퍼, 카프카 도커로 설치하기

[More Kafka]2. 주키퍼, 카프카 도커로 설치하기


이 글은 카프카 핵심가이드 챡과 추가조사를 하며 공부하며 정리하는 글입니다.
책 내용의 일부를 발췌하여 작성하기도 합니다. 책을 구매해서 보시는 편이 낫습니다. 2장은 카프카 설치와 구성하기 입니다. 저는 책과 다르게 도커 기반으로 구성하는 것을 실습합니다. 로컬에서 구성하고 최종 목표로 GCP로 옮겨서 실습합니다.

기본 구성요건

  • JDK8 설치하기
  • 도커 설치 for mac 설치하면 docker compose도 자동 설치됨.

주키퍼는 왜 사용을 하는가?

  • 카프카와 같은 분산 처리 시스템 서버들의 메타데이터를 통합 관리한다.
    • 즉 카프카는 주키퍼를 사용해서 브로커, 토픽, 파티션에 관한 메타데이터 정보를 저장
  • 아래 그림처럼 어플리케이션이 클라이언트가 되어서 주키퍼 서버들과 통신한다.
  • 주키퍼는 아래 그림과 같이 서버 여러 대를 앙상블(클러스터)로 구성
  • 기본적으로 상태 정보들은 주키퍼의 znode에 키-값 형태로 저장
  • 이 지노드에 저장된 key-value를 이용하여 분산 애플리케이션은 서로 데이터를 주고 받는다.

주키퍼 성능은?

  • 일반적으로 주키퍼 서버 3대로 앙상블을 구성한 경우 최대 초당 약 80,000개 처리
  • 5대 이상의 앙상블인 경우는 최대 초당 140,000개를 처리할 수 있다.
  • 앙상블의 구성 숫자가 많을 수록 많은 노드에서 장애를 일으켜도 끊김없는 서비스가 가능하다.
  • 사실 초당 140,000개 이상을 처리하지 않음에도 더 많은 홀수 서버(노드)를 구성하는 것은 성능 저하로 이어질 수 있으니, 적절한 수를 판단하자.

주키퍼 기타

  • 주키퍼는 confluent zookeeper가 있고, 일반 os(open source)인 zookeeper가 있다. 기본적인 zookeeper의 활용에 있어서는 큰 차이가 없다.
  • confluent는 os kafka 를 유료화된 버전으로 다양한 툴과 관리를 제공함. 첨언. 우리 회사는 confluent 리셀링하고 있으니 kafka 힘들면…연락주세요ㅎㅎㅎ

주키퍼 설치하기

주키퍼 environment

  • tickTime : (중요 옵션) Zookeeper가 사용하는 시간에 대한 기본 측정 단위(밀리초)
    • default: 2000
  • maxClientCnxns : (중요 옵션) 서버에 허용되는 최대 클라이언트 연결 수. 0 은 무제한
    • default: 60
  • dataDir : (필수)스냅샷 저장 경로
  • dataLogDir : (옵션) 트랜잭션 로그의 데이터 저장 경로
    • default: 지정하지 않으면 dataDir 에 저장
  • autopurge.snapRetainCount : 주키퍼 자동제거 기능. 활성화 시키면 최근 스냅샷과 트랜잭션 로그를 각각 dataDir 및 dataLogDir에 유지하고 나머지는 삭제.
  • autopurge.purgeInterval : 제거 작업이 트리거되어야하는 시간 간격 (시간)입니다. 자동 제거를 활성화하려면 양의 정수 (1 이상)로 설정하십시오.
    • default: 0
  • clientPort : (필수) Zookeeper 리스닝 포트
    • default: 2181
  • 참고, 2888,3888포트(default)는 앙상블 노드끼리 연결에 사용+리더 선출에 사용

카프카 설치하기

version: '3'

networks:
	localDev:

services:
	cp-zookeeper-1:
		image: confluentinc/cp-zookeeper:5.3.1
		container_name: cp-zookeeper-1
		hostname: cp-zookeeper-1
		ports:
			- 12181:12181
		environment:
			ZOOKEEPER_SERVER_ID: 1
			ZOOKEEPER_CLIENT_PORT: 12181  #클라이언트가 주키퍼 접속을 위한 포트
			ZOOKEEPER_TICK_TIME: 2000  #Tick 단위시간
			ZOOKEEPER_INIT_LIMIT: 5  #팔로워가 리더와 연결시도를 하는 최대 횟수
			ZOOKEEPER_SYNC_LIMIT: 2  #팔로워가 리더와 연결된 후, 앙상블 안에서 리더와 동기화되기 위한 제한 수.
			ZOOKEEPER_SERVERS: localhost:12888:13888 #localhost:22888:23888;localhost:32888:33888 #12888은 동기화를 위한 포트, 13888은 클러스터 구성 시, leader를 선출하기 위한 포트
			LOG_DIR: /zookeeper-logs  # The directory where ZooKeeper logs will be stored.
		volumes:
			- {저장하고 싶은 호스트 경로}:/zookeeper-logs
		networks:
			- localDev


# cp-zookeeper-2:
# image: confluentinc/cp-zookeeper:5.3.1
# container_name: cp-zookeeper-2
# hostname: cp-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: localhost:12888:13888;localhost:22888:23888;localhost:32888:33888
# LOG_DIR: /zookeeper-logs
# volumes:
# - {저장하고 싶은 호스트 경로}:/zookeeper-logs
# networks:
# - localDev

# cp-zookeeper-3:
# image: confluentinc/cp-zookeeper:5.3.1
# container_name: cp-zookeeper-3
# hostname: cp-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: localhost:12888:13888;localhost:22888:23888;localhost:32888:33888
# LOG_DIR: /zookeeper-logs
# volumes:
# - {저장하고 싶은 호스트 경로}:/zookeeper-logs
# networks:
# - localDev

cp-kafka-1:
	image: confluentinc/cp-kafka:5.3.1
	container_name: cp-kafka-1
	hostname: cp-kafka-1
	depends_on: #의존하는 컨테이너명을 명시. 해당 컨테이너 생성 후 실행됨.
		- cp-zookeeper-1
		# - cp-zookeeper-2
		# - cp-zookeeper-3
	environment:
		KAFKA_BROKER_ID: 1  #broker.id 에 설정되는 정수값(식별자).
		KAFKA_ZOOKEEPER_CONNECT: cp-zookeeper-1:12181  #,cp-zookeeper-2:22181,cp-zookeeper-3:32181 #브로커의 메타데이터를 주키퍼에 저장하기 위한 위치. 호스트에 이름을 추가하면 호스트명:포트로 작성할 수 있고, 주키퍼 앙상블시에는 모든 값을 적어줌.
		KAFKA_LISTENERS: INTERNAL://cp-kafka-1:19092,EXTERNAL://localhost:9092  # 리스너 들의 목록이고, 호스트/ip 로 구성한다. 해당 옵션을 사용하지 않으면 모든 인터페이스에서 수신 할 수 있다. 기본값. 0.0.0.0
		KAFKA_ADVERTISED_LISTENERS: INTERNAL://cp-kafka-1:19092,EXTERNAL://localhost:9092  # PLAINTEXT://{public ip혹은 hostname}(consumer나 producer에서 접속할 ip혹은 도메인):9092 kafka 브로커를 가리키는 사용 가능 주소로 초기연결시에 클라이언트에 전달되는 메타 데이터
		KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL  #브로커 간 통신에 사용할 리스너를 정의. KAFKA_ADVERTISED_LISTENERS 가 여러개인 경우 꼭 사용해야함
		KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT  # the listener name is not a security protocol, listener.security.protocol.map must also be set. PLAINTEXT는 리스너가 암호화되지 않은 것을 말함.
		KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 3  # default 3. cluster 내 broker에 토픽이 분산되어 저장된다. 싱글노드에서 테스트 할 때는 1로 설정해준다.
		KAFKA_NUM_PARTITIONS: 3  #토픽이 몇 개의 파티션으로 생성되는지. 기본 값은 1개이다. 토픽의 파티션 개수는 증가만 가능하고 감소될 수 없다. (클러스터의 브로커수와 그의 배수로 해주는 것이 권장된다.)
		KAFKA_LOG_DIRS: /kafka-logs  #오프셋을 포함한 토픽 정보가 저장되는 경로. 도커기반시 volume에서 아래와 같이 따로 잡아주어야 함.

	volumes:
		- {저장하고 싶은 호스트 경로}:/kafka-logs
	networks:
		- localDev

cp-kafka-2:
	image: confluentinc/cp-kafka:5.3.1
	container_name: cp-kafka-2
	hostname: cp-kafka-2
	depends_on:
		- cp-zookeeper-1
		# - cp-zookeeper-2
		# - cp-zookeeper-3
	environment:
		KAFKA_BROKER_ID: 2
		KAFKA_ZOOKEEPER_CONNECT: cp-zookeeper-1:12181  #,cp-zookeeper-2:22181,cp-zookeeper-3:32181
		KAFKA_LISTENERS: INTERNAL://cp-kafka-2:29092,EXTERNAL://localhost:9093
		KAFKA_ADVERTISED_LISTENERS: INTERNAL://cp-kafka-2:29092,EXTERNAL://localhost:9093
		KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL
		KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT
		KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 3
		KAFKA_NUM_PARTITIONS: 3
		KAFKA_LOG_DIRS: /kafka-logs
	volumes:
		- {저장하고 싶은 호스트 경로}:/kafka-logs
	networks:
		- localDev

cp-kafka-3:
	image: confluentinc/cp-kafka:5.3.1
	container_name: cp-kafka-3
	hostname: cp-kafka-3
	depends_on:
		- cp-zookeeper-1
		# - cp-zookeeper-2
		# - cp-zookeeper-3
	environment:
		KAFKA_BROKER_ID: 3
		KAFKA_ZOOKEEPER_CONNECT: cp-zookeeper-1:12181  #,cp-zookeeper-2:22181,cp-zookeeper-3:32181
		KAFKA_LISTENERS: INTERNAL://cp-kafka-3:39092,EXTERNAL://localhost:9094
		KAFKA_ADVERTISED_LISTENERS: INTERNAL://cp-kafka-3:39092,EXTERNAL://localhost:9094
		KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL
		KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT
		KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 3
		KAFKA_NUM_PARTITIONS: 3
		KAFKA_LOG_DIRS: /kafka-logs

	volumes:
		- {저장하고 싶은 호스트 경로}:/kafka-logs
	networks:
		- localDev
  • cp-kafka는 Apache Kafka® 2.3.0, the latest stable version of Kafka 이다.

KAFKA_LISTENER vs KAFKA_ADVERTISED_LISTENERS

  • 카프카 브로커들은 주키퍼 안에 그들을 등록하고, listeners 설정을 사용하면서 서로 커뮤니케이션을 함. 그래서 내가 셋팅한 listeners 설정대로 모든 내부 클러스터커뮤니케이션이 발생함. 그러나 내가 복잡한 네트워크를 구성했다고 가정하면(아마도 로컬 구성 환경이 아닌 상황) 예를 들어서 내부 네트워크와 외부 IP를 가지는 클라우드에서 나의 클러스터가 구성된 상황이라면, 이러한 경우에 나는 advertised.listeners(confluent kafka에서는 KAFKA_ADVERTISED_LISTENERS 임)에다가 {외부IP}://{외부 전용 포트} 를 셋팅해줘야함.
  • 예를 들어서 만약에 내부 아이피가 10.168.4.9그리고 포트가 9092 이며, 외부 IP 25.196.212.10 그리고 포트가 3101 일때, 다음과 같이 정의 할 수 있음.
  • listeners=PLAINTEXT://10.168.4.9:9092
  • advertised.listeners = PLAINTEXT://35.196.212.10:3101
  • 그래서 브로커는 이 밸류를 주키퍼에 등록하고, 그 때 외부에서 나의 카프카 클러스터에 연결을 원할 때, 외부 접속요청자는 내가 정의한 “advertised. listeners” 설정에 의해서 연결 할 수 있다.
  • Bonus Tip:
    Kubernetes 클러스터 내에서 Kafka를 실행하는 경우에도 마찬가지다. 이 경우 워커 노드에서 NodePort를 열고 외부에서 Kafka 클러스터와 통신 할 수 있도록 node_ip (알기로는 pod를 외부에 노출하는 4가지 방법중 하나.)및 포트를“advertised.listeners”로 설정 할 수 있다.

  • 차이점 알아보기(영어 원문)