IT/기타

Kafka 3.9를 다리 삼아 KRaft로 넘어가는 법

gfrog 2026. 5. 14. 06:12
반응형

Kafka 4.0이 나오면서 ZooKeeper 지원이 완전히 잘려나갔다. 더 정확히는, 3.9가 ZK를 지원하는 마지막 버전이고 4.0부터는 KRaft 전용이다. 우리 팀도 3.7에서 한동안 버티고 있었는데, 보안 패치 백포팅이 점점 줄어드는 게 보여서 결국 마이그레이션 일정을 잡았다. 클러스터 8대(브로커 24개, ZK 5노드 앙상블)를 다운타임 없이 옮겨야 하는 상황이라, 정리 차원에서 절차를 적어둔다.

왜 3.9를 거쳐야 하는가

4.0으로 직행이 안 된다. 공식 가이드에서도 명시적으로 "3.x에서 KRaft로 먼저 옮긴 다음 4.0으로 올려라"라고 못 박는다. 그 이유는 마이그레이션 도구 자체가 3.x 브로커 안에 들어있기 때문이다. 3.4에서 처음 등장했고, 3.7부터 production-ready, 3.9가 마지막 ZK 호환 버전이다. 그래서 실무에서는 3.9를 "bridge release"라고 부르고 거의 모든 운영자가 이걸 거쳐간다.

처음에 우리도 "그냥 4.0으로 한번에 못 가나" 싶어서 PoC를 돌려봤는데, 컨트롤러 quorum 부트스트랩 단계에서 메타데이터 변환이 안 돼서 막혔다. ZK에 쌓인 토픽/ACL 메타데이터를 __cluster_metadata 토픽으로 옮기는 로직이 3.x 코드 경로 안에만 있다. 4.0은 아예 그 코드를 들어냈다.

전체 흐름

큰 그림은 이렇다.

  1. 현재 클러스터를 3.9로 롤링 업그레이드
  2. 새로운 KRaft 컨트롤러 quorum을 띄움 (브로커와는 분리된 노드)
  3. 브로커에 zookeeper.metadata.migration.enable=true를 켜고 dual-write 모드로 진입
  4. 메타데이터 마이그레이션이 완료되기를 기다림
  5. 브로커를 KRaft 모드로 재시작 (process.roles=broker만 남기고 zk 설정 제거)
  6. 안정화 후 4.0으로 롤링 업그레이드

말로 적으면 6줄인데 실제로는 각 단계마다 검증 포인트가 있다.

컨트롤러 quorum부터

KRaft에서는 별도 컨트롤러 노드가 메타데이터 관리를 맡는다. 브로커 노드에 같이 띄우는 combined mode도 가능하지만, production은 분리(isolated) 모드를 권장한다. 우리는 3대짜리 컨트롤러 quorum을 새로 띄웠다.

# controller.properties
process.roles=controller
node.id=3001
controller.quorum.voters=3001@kafka-ctrl-1:9093,3002@kafka-ctrl-2:9093,3003@kafka-ctrl-3:9093
listeners=CONTROLLER://:9093
inter.broker.listener.name=PLAINTEXT
controller.listener.names=CONTROLLER

# 핵심: 기존 클러스터의 ZK와 연결
zookeeper.connect=zk-1:2181,zk-2:2181,zk-3:2181
zookeeper.metadata.migration.enable=true

마지막 두 줄이 포인트다. 마이그레이션용 컨트롤러는 ZK도 알아야 한다. 메타데이터를 ZK에서 읽어와 KRaft 로그로 옮기는 게 이 노드들의 첫 번째 일이다.

storage 포맷도 처음에 한 번 해줘야 한다.

KAFKA_CLUSTER_ID=$(bin/zookeeper-shell.sh zk-1:2181 get /cluster/id | grep -oP '(?<="id":")[^"]+')
bin/kafka-storage.sh format -t $KAFKA_CLUSTER_ID -c controller.properties

cluster.id가 기존 ZK 클러스터와 같아야 한다. 다르게 들어가면 컨트롤러가 "이건 우리 클러스터 메타데이터가 아닌데" 하면서 마이그레이션 자체를 거부한다. 이 부분은 매뉴얼에 작게 적혀있는데 모르면 한참 헤맨다.

브로커에 dual-write 켜기

컨트롤러가 살아있는 걸 확인했으면 브로커 쪽 설정을 손볼 차례다.

# broker.properties (3.9 기준)
broker.id=101
zookeeper.connect=zk-1:2181,zk-2:2181,zk-3:2181

# 마이그레이션 활성화
zookeeper.metadata.migration.enable=true
controller.quorum.voters=3001@kafka-ctrl-1:9093,3002@kafka-ctrl-2:9093,3003@kafka-ctrl-3:9093
controller.listener.names=CONTROLLER
listener.security.protocol.map=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT

브로커를 하나씩 롤링 재시작하면 dual-write 모드로 들어간다. 이 상태에서는 모든 메타데이터 변경이 ZK와 KRaft 로그 양쪽에 기록된다. 사실상 더블 쓰기라서 약간의 추가 부하는 있는데, 우리 환경(평소 메타데이터 변경 빈도가 낮음)에서는 체감되지 않았다.

마이그레이션 진행 상황은 컨트롤러 로그에서 봐야 한다.

grep -E "Migration|MigrationState" /var/log/kafka/controller.log | tail -20

PRE_MIGRATION → MIGRATION → POSTMIGRATION 순서로 상태가 바뀐다. 토픽이 많을수록 MIGRATION 단계가 길어지는데, 우리는 토픽 4,200개에 약 7분 걸렸다. ACL과 dynamic config 양이 많으면 더 걸린다고 한다.

POSTMIGRATION 이후가 진짜 분기점

여기서부터가 중요하다. POSTMIGRATION 상태가 되면 메타데이터는 KRaft에 다 들어와 있지만, ZK도 여전히 살아있다. 이 상태에서 운영을 며칠 더 끌면서 안정성을 본 다음 다음 단계로 넘어가는 게 안전하다. 우리는 3일 정도 관찰했다.

문제 생기면 이 시점까지는 롤백 가능하다. 정확히는, 브로커 설정에서 zookeeper.metadata.migration.enable을 끄고 다시 시작하면 ZK 단독 운영으로 돌아간다. POSTMIGRATION을 넘어가는 순간(=KRaft 단독 모드로 브로커를 재시작하는 순간) 롤백이 불가능해진다. 이 경계를 팀 전체가 인지하는 게 중요하다.

KRaft 전용으로 컷오버

준비가 됐다 싶으면 브로커 설정에서 ZK 관련 줄을 모두 제거한다.

# broker.properties (KRaft 전용)
node.id=101
process.roles=broker
controller.quorum.voters=3001@kafka-ctrl-1:9093,3002@kafka-ctrl-2:9093,3003@kafka-ctrl-3:9093
controller.listener.names=CONTROLLER
listeners=PLAINTEXT://:9092
# zookeeper.* 라인 전부 삭제

브로커를 하나씩 재시작하면 ZK 의존성이 끊긴다. 모든 브로커가 KRaft 전용이 되면 컨트롤러에서도 ZK 설정을 빼고 한 번 더 재시작한다. 이 시점에 ZK 앙상블은 더 이상 안 쓰이는 상태가 된다. 일주일 정도 더 살려두다가 셧다운했다.

4.0 업그레이드

여기까지 왔으면 3.9 KRaft 클러스터가 살아있는 상태다. 4.0으로 가는 건 사실 일반적인 롤링 업그레이드와 거의 같다. inter.broker.protocol.version 같은 IBP는 KRaft에서는 더 이상 안 쓰니까, metadata.version만 신경 쓰면 된다.

# 4.0 바이너리로 교체 후 브로커 롤링 재시작
# 안정화 확인 후:
bin/kafka-features.sh --bootstrap-server kafka-1:9092   upgrade --metadata 4.0

운영자가 놓치기 쉬운 것들

직접 부딪혀본 부분만 적어둔다.

첫째, 클라이언트 호환성. 컨트롤러를 분리하면서 클라이언트 입장에서는 bootstrap.servers 자체는 그대로 브로커를 가리키니까 영향 없다고 생각하기 쉽다. 그런데 admin client 중에 일부 구버전(2.x 이하)이 ZK 직접 접근하는 코드가 남아있는 경우가 있다. ksqlDB 구버전이 대표적이다. 마이그레이션 전에 클라이언트 인벤토리부터 훑어보는 게 좋다.

둘째, JBOD. 4.0부터 KRaft에서도 JBOD가 정식 지원되지만, 3.9 KRaft에서는 아직 KIP-858 일부만 들어와 있다. 디스크가 여러 개인 브로커는 이 부분 한 번 더 검토해야 한다.

셋째, Strimzi 같은 오퍼레이터 환경. Strimzi 0.46+가 마이그레이션 워크플로우를 자체적으로 제공한다. 직접 명령어 두드리는 것보다 안전하다. 매니지드 환경(MSK 등)은 이미 백그라운드에서 마이그레이션이 진행 중이거나 끝났다.

마치며

ZK에서 KRaft로 넘어오는 게 막상 해보니 6-7시간짜리 작업은 아니었다. 준비 2주, 실제 컷오버 작업 3일, 안정화 1주. 그 사이 메타데이터 변경 작업(토픽 생성 등)은 freeze 걸어뒀는데, 이게 가장 큰 운영 부담이었다. 다음에 비슷한 클러스터 또 옮길 일이 있으면 freeze 윈도우를 더 짧게 잡을 수 있을 것 같다.

ZK 없이 사는 것 자체는 좋다. 5노드 줄어들고, 모니터링 대시보드도 한 줄 짧아졌다. 그리고 무엇보다, 컨트롤러 failover가 눈에 띄게 빨라졌다. 측정해보니 평균 200ms 정도였다.

혹시 다른 환경에서 KRaft 마이그레이션하신 분 있으면 어떤 함정을 만났는지 공유 부탁드린다.

반응형