Kafka 운영 입문 — 설치·CLI·모니터링·보안 가이드

2026-05-02AWS SAA-C03 스터디

Apache Kafka 입문 정리 6편. 운영자가 자주 마주치는 모든 것을 풀어 — KRaft 모드 설치, 핵심 CLI(kafka-topics·console-producer·consumer·consumer-groups), 토픽 설정(retention/cleanup.policy), Rack Awareness와 Advertised Listeners, JMX·Prometheus·ELK 모니터링, SSL·SASL·ACL 보안, MirrorMaker 2 다중 클러스터 복제, 파티션 수·복제 팩터 선택 가이드까지 친절하게 정리.

📚 Apache Kafka 입문 정리 · 6편 / 14편 — 설치·CLI·모니터링·보안 가이드

이 글은 Apache Kafka 입문 정리 시리즈의 여섯 번째 편이자 본격적인 Kafka 운영 입문서입니다. 1~5편이 카프카가 어떻게 설계됐는지를 풀었다면, 이번 6편은 그 카프카를 실제로 띄우고 굴리는 사람의 시선 — 즉 Kafka 운영자의 시선으로 풀어 갑니다. 클러스터를 어떻게 설치하고, CLI로 무엇을 점검하고, 토픽 설정을 어떻게 잡고, 모니터링과 보안을 어디서부터 채우는지 — Kafka 운영을 맡은 사람이 매주 마주하는 일들이에요.

왜 이 단원이 처음엔 어렵게 느껴질까요

세 가지 이유가 겹쳐서 그래요.

첫째, 명령어가 한꺼번에 쏟아져요. kafka-topics.sh·kafka-console-producer.sh·kafka-console-consumer.sh·kafka-consumer-groups.sh·kafka-configs.sh·kafka-acls.sh — 비슷하게 생긴 스크립트가 줄지어 등장합니다. 한 번에 다 외우려 하면 어느 게 어떤 자리인지 헷갈려요.

둘째, 설정 키가 끝이 없어요. retention.ms·segment.bytes·cleanup.policy·min.insync.replicas·unclean.leader.election.enable·advertised.listeners — 영어 단어가 줄줄이 붙어 있고, 이게 토픽 단위 설정인지 브로커 단위 설정인지부터 분간이 안 됩니다.

셋째, 모니터링과 보안은 카프카 바깥 도구를 같이 알아야 해요. JMX·Prometheus·Grafana·ELK·SSL/TLS·SASL — 카프카만 공부한다고 끝이 아니라 자바 표준 모니터링·인증서·표준 인증 프로토콜 지식이 같이 필요합니다.

해결법은 한 가지예요. 카프카 클러스터를 "사내 중앙 우체국" 으로 비유로 잡고, Kafka 운영자를 "우체국 관리실 직원" 이라고 그려 보면 갑자기 정리가 됩니다. 우체국에 새 지점을 여는 일(설치), 게시판 운영 규칙을 정하는 일(토픽 설정), 매일 아침 우편물 처리량을 확인하는 일(모니터링), 누구한테 어떤 게시판 키를 줄지 정하는 일(보안) — 다 같은 흐름이에요. 이 글은 그 비유를 따라 풀어 갑니다.

카프카 설치 — 어디서 어떻게 띄우는가

카프카를 깔아 보지 않으면 Kafka 운영 이야기가 와닿지 않습니다. 운영체제별로 가장 단순한 길부터 정리해 둘게요.

사전 조건 — Java가 먼저

카프카는 JVM 위에서 도는 시스템이에요. 그래서 JDK 11 이상이 깔려 있어야 합니다. 카프카 3.x는 Java 11/17이 권장 라인이에요. 정확한 버전 호환은 Apache Kafka 공식 문서에서 확인하면 안전해요.

# 자바 버전 확인
java -version

# 권장 출력 예시
# openjdk version "11.0.17"

여기서 시험 함정이 하나 있어요. "JRE만 있어도 되지 않나요?"라고 생각하기 쉬운데, 카프카가 제공하는 CLI 스크립트들이 자바 도구를 호출하기 때문에 JDK가 깔려 있어야 합니다. 운영 환경에서는 JDK로 통일해 두세요.

macOS — Homebrew 한 줄

개발용 macOS라면 가장 짧은 길은 Homebrew예요.

# 설치 (KRaft 포함)
brew install kafka

# 시작 — Homebrew는 자동으로 KRaft 설정 적용
brew services start kafka

# 중지
brew services stop kafka

Homebrew로 깔면 Zookeeper 없이 KRaft 모드로 자동 실행됩니다. 4편에서 풀었던 "Zookeeper 완전 제거" 흐름이 그대로 반영돼 있어요.

Linux/Mac — 직접 다운로드

운영 서버처럼 직접 설치본을 통제해야 하는 자리는 압축본을 받아 푸는 식입니다.

# 1. 다운로드
wget https://downloads.apache.org/kafka/3.6.0/kafka_2.13-3.6.0.tgz

# 2. 압축 해제
tar -xzf kafka_2.13-3.6.0.tgz
cd kafka_2.13-3.6.0

# 3. KRaft 모드 초기화 — 클러스터 ID를 발급받아 스토리지를 포맷
KAFKA_CLUSTER_ID="$(bin/kafka-storage.sh random-uuid)"
bin/kafka-storage.sh format \
  -t $KAFKA_CLUSTER_ID \
  -c config/kraft/server.properties

# 4. 카프카 서버 시작
bin/kafka-server-start.sh config/kraft/server.properties

# 백그라운드 실행
bin/kafka-server-start.sh -daemon config/kraft/server.properties

여기서 시험 함정이 하나 있어요. KRaft 모드는 첫 실행 전에 반드시 kafka-storage.sh format을 한 번 돌려야 합니다. 옛날 Zookeeper 시절에는 없던 단계예요. "왜 안 뜨지?" 하면 대부분 이 포맷 단계를 건너뛴 겁니다.

Docker — docker-compose 한 파일로

격리된 환경이 편하면 Docker가 가장 빠릅니다. confluentinc/cp-kafka 이미지에 KRaft 환경 변수(KAFKA_PROCESS_ROLES, KAFKA_CONTROLLER_QUORUM_VOTERS, KAFKA_LISTENERS, KAFKA_ADVERTISED_LISTENERS 등)만 채우면 한 노드 클러스터가 뜹니다. 같은 compose 파일에 Kowl·AKHQ·Conduktor 같은 카프카 관리 UI를 묶어 두면 GUI까지 같이 올라와요.

카프카용 관리 UI(Kowl·AKHQ·Conduktor 등)가 여러 개 있어요. 토픽 목록·메시지 미리보기·Consumer Group 추적이 클릭으로 끝납니다. 콘솔 명령에 익숙해지기 전에 GUI로 그림을 잡는 데 좋은 보조 도구예요.

설정 파일 큰 그림 — server.properties와 KRaft 전용 설정

Kafka 운영의 절반은 server.properties 안에 어떤 키를 적느냐에 달려 있습니다. 한 번에 다 외울 필요는 없고, 그룹별로 자기 자리만 잡아 두면 돼요.

브로커 기본 — 정체성과 네트워크

# 브로커 고유 ID — 클러스터 안에서 중복 불가
broker.id=1

# 이 브로커가 수신하는 주소
listeners=PLAINTEXT://0.0.0.0:9092

# 외부 클라이언트에게 광고할 접속 주소 — 뒤에서 따로 풀게요
advertised.listeners=PLAINTEXT://broker1.example.com:9092

# 네트워크/IO 스레드 수
num.network.threads=3
num.io.threads=8

로그(데이터) 설정 — 어디에 얼마나 보관할지

# 데이터 저장 디렉토리
log.dirs=/var/kafka-data

# 자동 생성 토픽의 기본 파티션 수
num.partitions=1

# 자동 생성 토픽의 기본 복제 팩터
default.replication.factor=3

# 보존 기간 (기본 7일 = 168시간)
log.retention.hours=168

# 세그먼트 파일 크기 (기본 1GB)
log.segment.bytes=1073741824

복제 — 사고 대비

# ISR 최소 복제본 수 — 이 값을 못 채우면 acks=all 쓰기 거부
min.insync.replicas=2

# 리더 자동 재균형
auto.leader.rebalance.enable=true

# Unclean 리더 선출 — 데이터 손실 위험이라 기본 비활성화
unclean.leader.election.enable=false

여기서 시험 함정이 하나 있어요. unclean.leader.election.enable데이터 손실보다 가용성이 더 중요할 때만 켜는 옵션이에요. 5편에서 풀었던 "ISR이 비어 있을 때, ISR 밖의 복제본을 강제로 리더로 올리는 모드"인데, 그러면 ISR을 못 따라잡았던 메시지가 사라집니다. 금융·결제 같은 자리에선 무조건 false로 두세요.

KRaft 전용 설정

KRaft 모드로 띄울 땐 config/kraft/server.properties에 다음 키들이 추가로 들어옵니다.

# 이 노드가 어떤 역할을 맡는지 — broker만, controller만, 둘 다 가능
process.roles=broker,controller

# 노드 ID
node.id=1

# 컨트롤러 쿼럼 — Raft 합의에 참여할 노드들
controller.quorum.voters=1@localhost:9093

# 리스너 — 데이터용과 컨트롤러용을 분리
listeners=PLAINTEXT://localhost:9092,CONTROLLER://localhost:9093
inter.broker.listener.name=PLAINTEXT
controller.listener.names=CONTROLLER

# 메타데이터 로그 디렉토리 — KRaft가 사용하는 별도 영역
metadata.log.dir=/var/kafka-data/metadata
log.dirs=/var/kafka-data

회사 비유로 — process.roles는 "이 직원이 우편 분류만 하는지, 관리실에서 의사결정도 하는지"를 적어 둔 명함이에요. 작은 클러스터는 한 명이 둘 다 겸하지만, 큰 클러스터는 분리하는 게 안정적입니다.

핵심 CLI — Kafka 운영에서 매일 쓰는 다섯 개

카프카 CLI는 종류가 많지만, 실제로 매일 손이 가는 건 다섯 개예요. 이 다섯만 손에 익히면 Kafka 운영 작업의 80%가 처리됩니다.

kafka-topics.sh — 게시판 만들고 살피기

게시판(토픽)을 만들고, 목록 보고, 상세 보고, 파티션 늘리고, 지우는 자리예요.

# 토픽 생성 — 파티션 3, 복제본 1
kafka-topics.sh \
  --bootstrap-server localhost:9092 \
  --create \
  --topic my-topic \
  --partitions 3 \
  --replication-factor 1

# 토픽 목록
kafka-topics.sh --bootstrap-server localhost:9092 --list

# 토픽 상세 — 리더·ISR·복제본 위치까지 보임
kafka-topics.sh --bootstrap-server localhost:9092 \
  --describe --topic my-topic

# 파티션 늘리기 — 줄이는 건 불가
kafka-topics.sh --bootstrap-server localhost:9092 \
  --alter --topic my-topic --partitions 6

# 토픽 삭제
kafka-topics.sh --bootstrap-server localhost:9092 \
  --delete --topic my-topic

# Under-Replicated 파티션만 따로 보기 — 장애 점검의 첫 명령
kafka-topics.sh --bootstrap-server localhost:9092 \
  --describe --under-replicated-partitions

여기서 시험 함정이 하나 있어요. 파티션은 한 번 늘리면 줄일 수 없습니다. 2편에서 풀었던 "키 해시 → 파티션 매핑"이 깨지기 때문인데, 줄이고 싶으면 새 토픽을 만들어 데이터를 옮기는 수밖에 없어요. 처음 만들 때 적당한 수로 시작하는 게 중요합니다.

kafka-console-producer.sh — 손으로 메시지 부치기

# 기본 — 한 줄 한 메시지로 입력
kafka-console-producer.sh --bootstrap-server localhost:9092 --topic first_topic

# acks=all 강제
kafka-console-producer.sh --bootstrap-server localhost:9092 \
  --topic first_topic --producer-property acks=all

# 키:값 형태 — 키 해시로 파티션이 정해짐
kafka-console-producer.sh --bootstrap-server localhost:9092 --topic first_topic \
  --property parse.key=true --property key.separator=:

# 파일 흘려 넣기
kafka-console-producer.sh --bootstrap-server localhost:9092 \
  --topic first_topic < /path/to/messages.txt

운영에서는 보통 애플리케이션이 메시지를 부치지만, 장애 진단·스모크 테스트 자리에서는 콘솔 프로듀서가 가장 빠른 점검 도구예요.

kafka-console-consumer.sh — 손으로 메시지 받기

# 현재 시점부터 / 처음부터 / 그룹 지정
kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic first_topic
kafka-console-consumer.sh --bootstrap-server localhost:9092 \
  --topic first_topic --from-beginning
kafka-console-consumer.sh --bootstrap-server localhost:9092 \
  --topic first_topic --group my-consumer-group

# 키·값·파티션·타임스탬프까지 출력
kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic first_topic \
  --property print.timestamp=true --property print.key=true \
  --property print.value=true --property print.partition=true \
  --from-beginning

여기서 시험 함정이 하나 있어요. --from-beginning그룹이 없거나 그룹 오프셋이 없을 때만 처음부터 읽습니다. 이미 오프셋이 커밋된 그룹에 --from-beginning을 줘도 처음으로 안 돌아가요. 처음부터 다시 읽으려면 다음 절의 kafka-consumer-groups.sh --reset-offsets를 써야 합니다.

kafka-consumer-groups.sh — 그룹·오프셋·렉 점검

이 도구가 Kafka 운영자가 가장 많이 손대는 도구예요. 컨슈머가 메시지를 잘 따라가고 있는지(렉), 어디까지 읽었는지(오프셋)를 다 여기서 봅니다.

# 그룹 목록 / 상세 / 모든 그룹 / 삭제
kafka-consumer-groups.sh --bootstrap-server localhost:9092 --list
kafka-consumer-groups.sh --bootstrap-server localhost:9092 \
  --describe --group my-consumer-group
kafka-consumer-groups.sh --bootstrap-server localhost:9092 --describe --all-groups
kafka-consumer-groups.sh --bootstrap-server localhost:9092 --delete --group my-consumer-group

# 오프셋 처음으로 — 재처리
kafka-consumer-groups.sh --bootstrap-server localhost:9092 \
  --group my-consumer-group --reset-offsets --to-earliest \
  --topic first_topic --execute

# 특정 시각 / N개 뒤로 — 사고 시점 복구·짧은 재처리
kafka-consumer-groups.sh --bootstrap-server localhost:9092 \
  --group my-consumer-group --reset-offsets \
  --to-datetime 2026-04-30T00:00:00.000 --topic first_topic --execute
kafka-consumer-groups.sh --bootstrap-server localhost:9092 \
  --group my-consumer-group --reset-offsets \
  --shift-by -100 --all-topics --execute

여기서 시험 함정이 하나 있어요. --reset-offsets해당 그룹의 컨슈머가 모두 멈춰 있을 때만 적용됩니다. 살아 있는 컨슈머가 있는 상태에서 리셋을 시도하면 거부돼요. 운영에서 오프셋을 되돌리려면 컨슈머를 잠깐 내리고 작업해야 합니다.

kafka-configs.sh — 설정 동적 변경

토픽·브로커·사용자 단위 설정을 재시작 없이 바꿀 수 있는 자리예요.

# 토픽 설정 조회
kafka-configs.sh --bootstrap-server localhost:9092 \
  --entity-type topics --entity-name my-topic --describe

# 토픽 설정 변경 — 보존 1일, ISR 최소 2
kafka-configs.sh --bootstrap-server localhost:9092 \
  --entity-type topics --entity-name my-topic --alter \
  --add-config retention.ms=86400000,min.insync.replicas=2

# 설정 제거 — 기본값 복원
kafka-configs.sh --bootstrap-server localhost:9092 \
  --entity-type topics --entity-name my-topic --alter \
  --delete-config retention.ms

브로커 설정도 같은 도구로 --entity-type brokers --entity-name 1처럼 노드 단위로 동적 변경이 가능해요. 회사 비유로 — server.properties는 "사규 인쇄본", kafka-configs.sh는 "사규 일부 조항을 회의 한 번으로 갱신하는 절차"예요. 실수해도 빠르게 되돌릴 수 있어 Kafka 운영 친화적입니다.

보조 CLI 한 묶음

매일 쓰진 않지만 한 번씩 살려 주는 도구들. kafka-dump-log.sh는 세그먼트 파일 직접 들여다보기, kafka-get-offsets.sh는 토픽 현재 오프셋·특정 시각의 오프셋 조회, kafka-broker-api-versions.sh는 브로커 살아 있는지 점검, kafka-leader-election.sh는 리더 강제 재균형(특히 --election-type PREFERRED --all-topic-partitions).

토픽 설정 가이드 — 게시판 운영 규칙

토픽은 얼마나 보관할지·언제 정리할지라는 운영 규칙을 갖고 있어요. 회사 비유로 — 사내 게시판마다 "분기별 자료 게시판은 90일 보관, 일반 공지는 7일, 중요 자료실은 영구 보관" 같은 규칙을 정하는 식입니다.

핵심 토픽 설정 표

설정기본값설명
cleanup.policydelete정리 방식 (delete / compact)
retention.ms7일메시지 보관 기간
retention.bytes-1파티션당 최대 크기 (무제한)
min.insync.replicas1최소 ISR 수
max.message.bytes1 MB최대 메시지 크기
segment.bytes1 GB세그먼트 파일 최대 크기
segment.ms7일세그먼트 롤오버 주기
compression.typeproducer브로커 측 압축 정책

cleanup.policy — delete vs compact

여기서 한 번 짚고 갈게요. cleanup.policy는 두 가지 모드가 있고, 자기 자리가 완전히 다릅니다.

  • delete — 보존 기간(retention.ms)을 넘긴 세그먼트를 통째로 삭제. 일반 로그·이벤트 스트림용.
  • compact — 같은 키의 옛 메시지를 지우고, 키별로 마지막 값만 남김. 상태 스냅샷·CDC용.

회사 비유로 — delete는 "한 달 지난 공지는 자동 폐기", compact는 "사원 명부에서 같은 사번은 가장 최근 정보만 남기고 옛 기록은 정리"하는 식이에요. compact는 4편에서 풀었던 __consumer_offsets 토픽이 쓰는 모드이기도 합니다.

사용 사례별 설정 — 세 가지 패턴

자리핵심 설정
실시간 로그 수집파티션 6, 복제 3, retention.ms=86400000(1일), compression.type=snappy, cleanup.policy=delete
CDC (Change Data Capture)파티션 3, 복제 3, cleanup.policy=compact, min.insync.replicas=2, retention.ms=-1(무제한)
금융 거래파티션 10, 복제 3, min.insync.replicas=2, retention.ms=2592000000(30일), unclean.leader.election.enable=false

CDC 토픽이 compact + 무제한 보존인 이유는 키별 최신 상태만 남기면 충분하기 때문이에요. 금융 거래는 30일 감사 로그 보존 + ISR 강하게 + unclean 리더 금지로 데이터 손실을 차단합니다.

여기서 시험 함정이 하나 있어요. min.insync.replicas토픽 단위로 따로 잡을 수 있고, 브로커 기본값보다 토픽 설정이 우선합니다. 중요한 토픽만 따로 강하게 잡고, 일반 토픽은 기본값을 두는 식으로 운영해요.

클러스터 설정 — Rack Awareness와 Advertised Listeners

Kafka 운영에서 설치보다 한 단계 더 들어가면, 데이터 안전과 외부 접근을 다루는 설정 두 개가 등장합니다.

Rack Awareness — 한 랙이 통째로 죽어도 살아남기

데이터센터에는 서버가 랙(rack, 선반) 단위로 묶여 있어요. 한 랙이 통째로 정전·네트워크 장애를 겪을 수 있죠. 카프카는 복제본을 다른 랙에 분산해서 한 랙이 죽어도 데이터가 살아남게 만들 수 있어요.

# 브로커마다 자기 랙 위치를 적어 둠
broker.rack=us-east-1a

브로커마다 broker.rack을 다르게 적어 두면, 카프카가 새 파티션의 복제본을 배치할 때 서로 다른 랙으로 흩어 놓아요. 회사 비유로 — 같은 서류의 사본 세 장을 한 캐비넷에 두지 않고, 다른 층 다른 캐비넷에 한 장씩 보관하는 식이에요. 한 층에 불이 나도 사본이 남습니다.

여기서 시험 함정이 하나 있어요. Rack Awareness는 클러스터를 처음 구축할 때 잡아 두는 게 깔끔합니다. 나중에 추가하면 기존 파티션은 재배치되지 않아요. 새 파티션부터 적용됩니다. 이미 운영 중인 클러스터에 켤 땐 파티션 재할당(reassignment) 작업까지 묶어서 해야 해요.

Advertised Listeners — 외부에서 보이는 주소

이 설정이 Kafka 운영자가 가장 자주 발이 걸리는 자리예요. 한 번에 풀어 봅시다.

브로커 내부 IP: 10.0.0.1
브로커 외부 IP: 203.0.113.1 (클라이언트가 접속하는 주소)

listeners=PLAINTEXT://0.0.0.0:9092
   ← 브로커가 "이 주소로 들어오는 연결 받을게" 라고 여는 주소

advertised.listeners=PLAINTEXT://203.0.113.1:9092
   ← 클라이언트에게 "내게 접속하려면 이 주소로 와" 라고 광고하는 주소

회사 비유로 — listeners는 "건물 안에서 우편물 받는 책상 위치", advertised.listeners는 "외부에 인쇄된 사무실 주소(우편번호 포함)" 예요. 둘이 다를 수 있다는 게 핵심입니다.

여기서 시험 함정이 하나 있어요. 카프카 클라이언트의 첫 연결 흐름이 이렇습니다.

  1. 클라이언트가 bootstrap.servers에 적힌 한 브로커에 접속.
  2. 그 브로커가 클러스터 메타데이터를 돌려주는데, 거기엔 다른 브로커들의 advertised.listeners 주소가 들어 있어요.
  3. 클라이언트는 그 광고된 주소로 다시 연결합니다.

그래서 advertised.listeners가 잘못 잡혀 있으면, 첫 연결은 성공해도 그 뒤에 "Connection refused" 같은 오류가 납니다. 클라이언트가 잘못된 주소로 다시 가니까요. 도커·쿠버네티스·AWS 같은 환경에서 카프카가 안 뜨는 사고의 절반이 여기서 시작돼요.

다중 리스너 설정 — 내부와 외부 분리

# AWS 환경 — 내부는 PLAINTEXT, 외부는 SSL
listeners=INTERNAL://0.0.0.0:9092,EXTERNAL://0.0.0.0:9093
listener.security.protocol.map=INTERNAL:PLAINTEXT,EXTERNAL:SSL
advertised.listeners=INTERNAL://10.0.0.1:9092,EXTERNAL://ec2-203-0-113-1.compute.amazonaws.com:9093
inter.broker.listener.name=INTERNAL

내부 클라이언트는 빠른 PLAINTEXT로, 외부에서 들어오는 트래픽은 암호화된 SSL로 — 한 클러스터에 두 개 이상의 리스너를 두는 패턴이에요. inter.broker.listener.name으로 브로커끼리 통신할 때 어느 리스너를 쓸지도 따로 지정합니다.

모니터링 — Kafka 운영의 JMX·Prometheus·ELK·Grafana 스택

카프카는 정상일 때 조용한 시스템이에요. 그래서 무언가 잘못됐을 때 빨리 알아채려면 모니터링을 처음부터 깔아 둬야 합니다.

JMX — 자바 시스템의 표준 점검 창구

JMX(Java Management Extensions)는 자바 시스템 내부 상태를 표준화된 방식으로 노출하는 자바 표준이에요. 카프카는 자기 메트릭(초당 메시지 수·바이트·렉·복제 상태 등)을 다 JMX로 내보냅니다. 어떤 메트릭이 있는지 전체 목록은 Apache Kafka 모니터링 공식 가이드에 정리돼 있어요.

# JMX 포트 활성화
export JMX_PORT=9999
bin/kafka-server-start.sh config/server.properties

# 또는 더 정밀한 옵션
export KAFKA_JMX_OPTS="-Dcom.sun.management.jmxremote \
  -Dcom.sun.management.jmxremote.authenticate=false \
  -Dcom.sun.management.jmxremote.ssl=false \
  -Dcom.sun.management.jmxremote.port=9999"

회사 비유로 — JMX는 "관리실 벽에 붙은 점검 창구"예요. 누가 와서 들여다봐도 정해진 양식대로 카프카의 상태가 적혀 있습니다. JConsole·VisualVM 같은 도구로 직접 들여다볼 수도 있고, 자동화된 모니터링 시스템이 거기서 데이터를 긁어 가요.

꼭 봐야 할 핵심 메트릭

메트릭의미임계
MessagesInPerSec초당 수신 메시지 수처리량 추세
BytesInPerSec / BytesOutPerSec초당 입출력 바이트네트워크 부하
UnderReplicatedPartitions복제 못 따라잡은 파티션 수0이 정상
ActiveControllerCount활성 컨트롤러 수항상 1이어야 함
LeaderCount이 브로커의 리더 파티션 수분산 균형 지표
RequestsPerSec(Produce)초당 프로듀서 요청 수부하 추세
TotalTimeMs(Fetch) 99%ile페치 요청 99분위 지연응답 품질

여기서 시험 함정이 하나 있어요. ActiveControllerCount가 1이 아닌 모든 상황은 사고입니다. 0이면 컨트롤러 없음, 2 이상이면 분리(split-brain) 의심. KRaft 모드에선 거의 1이 보장되지만, 모니터링 알람으로는 항상 잡아 두세요.

Prometheus + Grafana — 가장 흔한 조합

JMX를 그대로 쓰는 자리는 드물고, 대부분 JMX → Prometheus Exporter → Grafana 순서로 흘립니다. 카프카 프로세스에 JMX Exporter 자바 에이전트(-javaagent:/opt/jmx_prometheus_javaagent.jar=7071:/opt/kafka-jmx-exporter.yaml)를 붙이면 :7071/metrics로 Prometheus 형식의 메트릭이 노출돼요. Prometheus가 그걸 긁어가고 Grafana가 차트로 그려 줍니다. 카프카용 Grafana 대시보드는 공개된 게 많아 처음부터 만들 필요는 없어요.

Alert 규칙 — 알람으로 잡을 사건 세 개

알람표현식임계심각도
Under-Replicated Partitionsunderreplicatedpartitions > 05분 지속critical
Consumer Lag 과다consumer_group_lag > 10000010분 지속warning
Broker Downmessagesinpersec == 02분 지속critical

복제 못 따라잡은 파티션, 컨슈머 렉, 브로커 다운 — 이 세 개만 잡아 둬도 운영 사고의 큰 그림은 잡힙니다.

ELK 스택 — 로그 쪽 모니터링

JMX·Prometheus가 숫자 메트릭 쪽이라면, ELK(Elasticsearch + Logstash + Kibana)는 로그 텍스트 쪽이에요. 카프카 자체 서버 로그(server.log·controller.log 등)를 ELK로 흘려 검색·분석합니다. 카프카가 메시지 운반 시스템이면서 동시에 ELK의 운반 라인 그 자체로 자주 쓰이는 게 재미있는 자리예요.

보안 — 암호화·인증·인가 세 축

카프카 보안은 세 단어로 정리됩니다.

풀어서구현
암호화(Encryption)전송 중 데이터를 가로채도 못 읽게SSL/TLS
인증(Authentication)누가 접속하는지 확인SSL 인증서 / SASL
인가(Authorization)누가 어디까지 할 수 있는지ACL

회사 비유로 — 암호화는 "우편봉투를 봉인해 내용물 못 보게", 인증은 "사원증으로 신원 확인", 인가는 "이 사원증으로 어느 게시판에 글을 쓰고 읽을 수 있는지 정해 둔 권한 명세서"예요. 셋 다 따로 굴러가고, 동시에 켤 수도 있고, 일부만 켤 수도 있습니다.

SSL/TLS — 전송 암호화

브로커-클라이언트, 브로커-브로커 통신을 TLS로 감싸는 자리예요. 큰 흐름은 — CA 인증서 발급 → 브로커 키스토어 생성 → CSR 만들어 CA로 서명 → 키스토어에 import → 트러스트스토어에 CA 인증서 import. 도구는 opensslkeytool을 같이 씁니다.

# server.properties — 핵심 키만
listeners=PLAINTEXT://localhost:9092,SSL://localhost:9093
advertised.listeners=PLAINTEXT://localhost:9092,SSL://localhost:9093

ssl.keystore.location=/ssl/kafka.server.keystore.jks
ssl.keystore.password=kafka-password
ssl.key.password=kafka-password
ssl.truststore.location=/ssl/kafka.server.truststore.jks
ssl.truststore.password=kafka-password
ssl.client.auth=required

ssl.client.auth=requiredmTLS(상호 인증) 를 의미해요. 클라이언트도 인증서를 내야 들어올 수 있습니다. 이걸 켜면 SSL 인증서가 곧 신원 증명서 역할을 해서 SASL 없이도 인증이 돼요.

SASL — 사용자명/비밀번호 인증

SSL이 인증서 기반이라면, SASL은 사용자명/비밀번호 기반 인증이에요. 사람·서비스 계정 단위로 권한 관리하기에 자연스러워서 운영에서 더 자주 씁니다. 메커니즘 중 가장 많이 쓰이는 건 SCRAM-SHA-512.

# 사용자 생성 — alice
kafka-configs.sh --bootstrap-server localhost:9092 --alter \
  --add-config 'SCRAM-SHA-512=[iterations=8192,password=alice-secret]' \
  --entity-type users --entity-name alice

브로커 쪽은 리스너를 SASL_SSL로 바꾸고 sasl.enabled.mechanisms=SCRAM-SHA-512, JAAS 설정에 admin 계정을 박아 둡니다. 클라이언트는 security.protocol=SASL_SSL, sasl.mechanism=SCRAM-SHA-512, sasl.jaas.config에 ScramLoginModule + 자기 계정을 적어 두면 끝.

여기서 시험 함정이 하나 있어요. SASL은 보통 SSL 위에서 굴려야 합니다. SASL_PLAINTEXT는 비밀번호가 평문으로 흘러갈 위험이 있어요. 운영에서는 거의 다 SASL_SSL을 씁니다.

ACL — 누가 어디까지 할 수 있는지

ACL(Access Control List)은 이 사용자가 이 토픽에 글을 쓸 수 있다·읽을 수 있다를 명세서로 적어 두는 자리예요.

# server.properties — ACL 활성화
authorizer.class.name=kafka.security.authorizer.AclAuthorizer
allow.everyone.if.no.acl.found=false
super.users=User:admin
# Alice에게 my-topic 읽기 권한 + 그룹 사용 권한
kafka-acls.sh --bootstrap-server localhost:9092 --add \
  --allow-principal User:alice \
  --operation Read --topic my-topic --group my-group

# Alice에게 my-topic 쓰기 권한
kafka-acls.sh --bootstrap-server localhost:9092 --add \
  --allow-principal User:alice --operation Write --topic my-topic

# ACL 목록 / 삭제
kafka-acls.sh --bootstrap-server localhost:9092 --list --topic my-topic
kafka-acls.sh --bootstrap-server localhost:9092 --remove \
  --allow-principal User:alice --operation Read --topic my-topic

# 특정 IP에서만 허용
kafka-acls.sh --bootstrap-server localhost:9092 --add \
  --allow-principal User:alice --allow-host 10.0.0.5 \
  --operation Read --topic my-topic

여기서 시험 함정이 하나 있어요. allow.everyone.if.no.acl.found=false로 두면 ACL이 정의되지 않은 토픽은 누구도 못 씁니다. 보안 모범 사례지만, ACL을 처음 켤 땐 기존 클라이언트가 갑자기 다 끊기는 사고가 자주 나요. 단계적으로 켜고, super user를 먼저 정의해 둔 뒤에 토픽별로 ACL을 채워 가는 게 안전해요.

멀티 클러스터 복제 — MirrorMaker 2

클러스터가 한 데이터센터에만 있으면, 그 데이터센터가 통째로 죽었을 때 답이 없습니다. 그래서 여러 클러스터를 두고 데이터를 복제해 두는 운영 패턴이 흔해요. 그 자리에 쓰이는 도구가 MirrorMaker 2(MM2) 입니다.

MirrorMaker 2가 푸는 자리

회사 비유로 — MM2는 "두 우체국 간 양방향 자료 복사기"예요. 본사 우체국과 지사 우체국이 있는데, 본사에 들어온 우편물이 자동으로 지사에도 똑같이 사본이 가게 만드는 거죠. 본사가 잠깐 닫혀도 지사 우체국이 살아 있으면 업무가 끊기지 않아요.

[Primary Cluster]  →  [MirrorMaker 2]  →  [DR Cluster]
   (us-east)                                  (us-west)

대표 사용 자리:

  • DR(재해 복구) — Active-Passive 또는 Active-Active 구성
  • 데이터센터 간 복제 — 지역별 지연 줄이기
  • 클라우드 마이그레이션 — 점진적 전환
  • 지역별 클러스터 운영 — 글로벌 서비스

MM2 설정 한 컷

# mm2.properties

# 클러스터 별칭
clusters=source, target

# 각 클러스터 연결 정보
source.bootstrap.servers=source-kafka:9092
target.bootstrap.servers=target-kafka:9092

# 복제 방향: source → target
source->target.enabled=true
source->target.topics=my-topic,important-.*

# 메타데이터 토픽 복제 팩터
replication.factor=3
offset.syncs.topic.replication.factor=3
heartbeats.topic.replication.factor=3
checkpoints.topic.replication.factor=3

# 컨슈머 오프셋도 동기화 — DR 자리에서 결정적
sync.group.offsets.enabled=true
connect-mirror-maker.sh mm2.properties

여기서 시험 함정이 하나 있어요. MM2는 기본적으로 소스 클러스터의 토픽을 source.my-topic 형태로 리네이밍해서 타겟에 만듭니다. 무한 복제 루프(같은 토픽이 양쪽에서 서로 다시 복제되는 상황)를 막기 위한 안전장치예요. Active-Active로 양방향 복제할 때도 이 리네이밍이 굴러갑니다.

또 하나 — sync.group.offsets.enabled=true를 켜야 컨슈머가 DR 쪽으로 페일오버할 때 어디까지 읽었는지가 같이 옮겨가요. 이걸 빼먹으면 페일오버 후 데이터를 처음부터 다시 읽거나 일부를 건너뛰게 됩니다.

성능 튜닝 큰 그림

성능 튜닝은 책 한 권 분량이지만, Kafka 운영자가 처음 잡아 둘 큰 다이얼만 추려 둘게요.

프로듀서 — 배치와 압축

props.put(ProducerConfig.LINGER_MS_CONFIG, 20);
props.put(ProducerConfig.BATCH_SIZE_CONFIG, 32 * 1024);
props.put(ProducerConfig.COMPRESSION_TYPE_CONFIG, "snappy");
props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 64 * 1024 * 1024);
props.put(ProducerConfig.MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION, 5);

linger.ms를 약간 올리면 더 많은 메시지가 한 배치로 묶여 압축 효율도 처리량도 올라갑니다. 3편에서 풀었던 "배치를 키울수록 처리량이 좋아진다" 흐름과 같은 자리예요.

컨슈머 — 페치 크기와 폴 한도

props.put(ConsumerConfig.FETCH_MIN_BYTES_CONFIG, 1024 * 1024);
props.put(ConsumerConfig.FETCH_MAX_WAIT_MS_CONFIG, 500);
props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 1000);
props.put(ConsumerConfig.FETCH_MAX_BYTES_CONFIG, 52428800);

한 번 가져올 때 더 많이 가져오면 처리량이 올라가지만, max.poll.records를 너무 크게 잡으면 한 폴 처리 시간이 길어져 max.poll.interval.ms를 넘기고 그룹에서 쫓겨나요. 4편에서 본 리밸런싱 사고의 흔한 원인입니다.

브로커 — 스레드와 OS

num.network.threads=8
num.io.threads=16
socket.send.buffer.bytes=102400
socket.receive.buffer.bytes=102400
socket.request.max.bytes=104857600

num.io.threads디스크 수의 2배 정도로 잡는 게 흔한 가이드예요.

OS 레벨 — 자주 잊히는 자리

# 파일 디스크립터 제한 — 카프카는 파일을 많이 엽니다
echo "kafka soft nofile 100000" >> /etc/security/limits.conf
echo "kafka hard nofile 100000" >> /etc/security/limits.conf

# 스왑 거의 금지 — 카프카는 페이지 캐시 의존
echo "vm.swappiness=1" >> /etc/sysctl.conf

# 더티 페이지 — 빠른 OS 페이지 플러시
echo "vm.dirty_background_ratio=5" >> /etc/sysctl.conf
echo "vm.dirty_ratio=60" >> /etc/sysctl.conf

여기서 시험 함정이 하나 있어요. log.flush.interval.messages·log.flush.interval.ms를 강제로 켜면 오히려 느려져요. 카프카는 OS의 페이지 캐시에 맡겨서 비동기로 플러시되는 걸 전제로 설계됐어요. 강제 플러시를 켜면 디스크 I/O가 막혀 처리량이 떨어집니다.

파티션 수와 복제 팩터 선택 가이드

설치 다음으로 가장 많이 받는 질문이 "파티션 몇 개로 잡을까요?""복제 팩터 몇으로 갈까요?" 예요. 정답은 없지만 가이드라인이 있어요.

파티션 수

  • 너무 적으면 — 컨슈머를 더 추가해도 병렬 처리량이 안 늘어요. 한 파티션은 한 컨슈머만 읽기 때문이에요(4편 컨슈머 그룹 흐름).
  • 너무 많으면 — 메타데이터 부담·컨트롤러 부하·페일오버 시간 증가.

가이드 — 목표 처리량을 한 파티션 처리량(보통 10~50 MB/s)으로 나눈 값 정도로 시작하세요. 늘리는 건 가능해도 줄이는 건 안 되니, 약간 여유 있게 잡는 게 맞아요. 토픽 당 100개 이하면 일반적, 1000개 이상이면 특수한 자리.

복제 팩터

  • 1 — 복제 없음. 개발용. 운영 금지.
  • 2 — 사고 한 번에 데이터 손실 가능. 운영 비추.
  • 3 — 운영 표준. 한 브로커 죽어도 ISR이 2 유지.
  • 4 이상 — 디스크·네트워크 부담 큼. 특수한 자리에만.

Kafka 운영에서는 거의 항상 3을 잡고, 거기에 min.insync.replicas=2·acks=all을 묶어서 강한 내구성을 만듭니다. 5편에서 풀었던 그 조합이에요.

문제 해결 흐름 — 자주 만나는 세 자리

연결 안 됨

kafka-broker-api-versions.sh --bootstrap-server localhost:9092

이게 안 되면 다음 순서로 점검해요.

  1. advertised.listeners 설정 — 첫 연결과 두 번째 연결 주소가 다를 가능성.
  2. 방화벽·보안그룹 9092 포트.
  3. hosts 파일·DNS — 광고된 호스트명이 클라이언트에서 실제로 풀리는지.
  4. SSL 인증서 유효성 — 만료·CN 미스매치.

컨슈머 렉이 쌓임

kafka-consumer-groups.sh --bootstrap-server localhost:9092 \
  --describe --all-groups

LAG가 큰 그룹이 보이면 다음을 먼저 확인.

  1. 파티션 수 < 컨슈머 수가 가능한가 — 늘릴 여지가 있나.
  2. 컨슈머의 처리 시간이 너무 긴가max.poll.records를 줄이거나 처리 로직 최적화.
  3. 브로커 쪽 부하 — Bytes Out, 페치 지연 메트릭 확인.

디스크 부족

du -sh /var/kafka-data/*

# 즉시 보존 단축 — 제일 큰 토픽 1시간으로
kafka-configs.sh --bootstrap-server localhost:9092 \
  --entity-type topics --entity-name large-topic --alter \
  --add-config retention.ms=3600000

응급 처치는 가장 큰 토픽의 retention.ms를 임시로 줄여서 세그먼트를 빨리 날리는 거예요. 다만 줄인 값은 다시 원복해 둬야 합니다.

URP(Under-Replicated Partitions)가 0이 아님

kafka-topics.sh --bootstrap-server localhost:9092 \
  --describe --under-replicated-partitions

# 리더 강제 재균형
kafka-leader-election.sh \
  --bootstrap-server localhost:9092 \
  --election-type PREFERRED \
  --all-topic-partitions

원인은 보통 네 가지 — 브로커 장애·과부하, 네트워크 문제, 디스크 I/O, 복제 설정. URP가 5분 이상 지속되면 무조건 알람으로 잡아 두세요.

Kafka 운영자 시점 압축 노트

여기까지가 카프카 6편 — Kafka 운영의 큰 그림이에요. 매주 손이 가는 자리들만 압축해서 한 번 더 짚어 둘게요.

  • 사전 조건은 JDK 11 이상, 카프카 3.x는 Java 11/17 권장.
  • macOS 개발용은 brew install kafka 한 줄, 이미 KRaft 모드로 자동 설정.
  • 직접 설치는 압축 해제 → kafka-storage.sh format(KRaft 첫 실행 필수)kafka-server-start.sh.
  • Docker는 docker-compose 한 파일로 카프카 + 관리 UI(Kowl·AKHQ·Conduktor 등) 같이 띄우기 가능.
  • 매일 쓰는 CLI 다섯 — kafka-topics.sh·kafka-console-producer.sh·kafka-console-consumer.sh·kafka-consumer-groups.sh·kafka-configs.sh.
  • 파티션은 늘리기만 가능, 줄이기 불가 — 처음에 여유 있게.
  • --from-beginning은 그룹 오프셋이 없을 때만 동작, 재처리는 --reset-offsets 로.
  • cleanup.policy 두 모드 — delete(시간 지난 세그먼트 삭제), compact(키별 최신 값만).
  • 운영 표준 토픽 옵션 — 복제 3, min.insync.replicas=2, unclean.leader.election.enable=false.
  • Rack Awareness(broker.rack) 로 한 랙 장애 대비, 클러스터 구축 시점에 잡는 게 깔끔.
  • listeners수신 주소, advertised.listeners는 외부에 광고할 주소 — 도커·AWS 사고의 절반은 여기.
  • 모니터링 흐름 — JMX → Prometheus Exporter → Grafana, 알람은 URP·렉·브로커 다운 세 개부터.
  • ActiveControllerCount 항상 1이어야 함, 0이거나 2 이상이면 사고.
  • 보안 세 축 — 암호화(SSL/TLS), 인증(SSL 인증서·SASL), 인가(ACL).
  • 운영 권장 — SASL_SSL + ACL, 평문 SASL은 비추.
  • ACL 처음 켤 땐 단계적으로, super user 먼저, allow.everyone.if.no.acl.found=false 신중히.
  • MirrorMaker 2 — 두 클러스터 양방향 복제, DR·마이그레이션·지역별 운영.
  • MM2는 토픽을 source.<원래이름> 으로 리네이밍해 무한 루프 방지.
  • sync.group.offsets.enabled=true 가 있어야 페일오버 시 컨슈머 오프셋도 같이 옮겨짐.
  • 복제 팩터는 운영 표준 3, 1·2는 사실상 비추, 4+는 특수한 자리만.
  • 카프카는 OS 페이지 캐시 활용 전제log.flush.interval.*을 강제로 켜면 오히려 느려짐.
  • 파일 디스크립터 10만, vm.swappiness=1 — 운영 OS 튜닝의 기본 두 가지.

다음 글(7편)에서는 카프카가 자주 자리잡는 실제 사용 사례 패턴 — 활동 추적·로그 수집·CDC·이벤트 소싱·마이크로서비스 디커플링이 어떤 토폴로지로 굴러가는지를 시리즈 마무리로 풀어 갑니다. 1~6편이 카프카의 부품과 Kafka 운영을 풀었다면, 7편은 그 부품이 실제 시스템 안에서 어떻게 모이는지를 한 번에 보는 단원이에요.

시리즈 다른 편

같은 시리즈의 다른 글들도 같은 친절 톤으로 묶어 정리되어 있어요.

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

답글 남기기

error: Content is protected !!