ZooKeeper to KRaft Migration

Sanghyun An
SPITHA Blog
Published in
27 min readJun 23, 2023
  1. ZK 클러스터 환경 체크
  2. KRaft Controller 생성
  3. ZK Broker 마이그레이션 등록
  4. 마이그레이션 모드 해제
  5. 롤백

목표

ZooKeeper + Kafka (ZK broker)클러스터를 KRaft Controller + Kafka (KRaft broker) 클러스터로 전환합니다. 완전히 새로운 KRaft controller를 구성하고 ZK broker와 함께 마이그레이션 모드로 등록하면 ZooKeeper 메타 데이터를 KRaft controller로 마이그레이션 할 수 있습니다.

KRaft 모드의 이점

  • ZooKeeper 종속성 제거 : Kraft 모드는 ZooKeeper를 사용하지 않고 Kafka 클러스터를 운영할 수 있도록 합니다.
  • 단일 클러스터 관리 : ZooKeeper를 사용하는 것은 관리 및 운영의 복잡성을 증가 시켰습니다. KRaft 모드로 마이그레이션하면 Kafka 클러스터에서 자체적인 메타데이터 저장소를 가지며 단일 시스템으로 운영될 수 있습니다.
  • 성능 및 확장성 향상 : 개별 파티션 수의 한계를 크게 확장하여 수백만 개의 파티션을 처리할 수 있게 됩니다.
  • 단일 보안 모델 : Kraft 모드로 마이그레이션하면 Kafka 클러스터에 대한 보안 설정을 통합하여 단일 보안 모델을 사용할 수 있게 됩니다.

테스트 환경

  • Kafka 3대 (3.4.0)
  • ZooKeeper 1대 (3.7.1)
  • Kafka KRaft controller 1대 (3.4.0)
  • PLAINTEXT

마이그레이션 주의사항

전 과정에서 문제가 생겼을 때 다시 ZooKeeper로 롤백 하는 방법이 존재합니다.

KRaft는 (broker, controller)가 결합된 모드에서 마이그레이션이 진행되지 않습니다.

1. ZK 클러스터 환경 체크

마이그레이션 시작 전, 기존 클러스터에 대한 정보를 확인합니다. zkCli.sh를 통해서 “/{CLUSTER}/broker/ids” 나 “/{CLUSTER}/controller” 정보를 확인합니다. ZooKeeper에 연결되어 있는 Broker의 수를 확인할 수 있으며, Active Controller가 어느 Broker에 할당되어있는지 확인할 수 있습니다.

[zk: localhost:2181(CONNECTED) 2] ls /zkbroker/brokers/ids
[0, 1, 2]
[zk: localhost:2181(CONNECTED) 4] get /zkbroker/controller
{"version":2,"brokerid":0,"timestamp":"1676248822925","kraftControllerEpoch":-1}

ZooKeeper 메타데이터 정보를 통해, 그림 1과 같이 ZK Broker 3개로 구성된 것을 알 수 있습니다. 또한 Active Controller는 Broker 0번에 위치하고 있습니다.

그림-1

2. KRaft Controller 생성

마이그레이션에 순서 종속성은 없지만, 이 글에서는 ZooKeeper 데이터를 마이그레이션 할 KRaft Controller를 먼저 생성 합니다.

KRaft Controller 설정

마이그레이션을 위한 KRaft Controller를 설정합니다. 아래의 설정은 마이그레이션을 시작하기 위한 필수 옵션들 입니다.

zookeeper.metadata.migration.enable=true

zookeeper.connect=localhost:2181/zkbroker

## Kraft controller 
## controller.properties

## 마이그레이션 활성화 설정
zookeeper.metadata.migration.enable=true
zookeeper.connect=localhost:2181/zkbroker

## kraft controller 설정
process.roles=controller
node.id=3
controller.quorum.voters=3@localhost:9093
listeners=CONTROLLER://:9093
controller.listener.names=CONTROLLER
...

로그 디렉토리 생성

KRaft Controller를 시작하기 전, kafka-storage.sh 스크립트를 사용해 로그 디렉토리를 생성합니다. “— cluster-id” 및 “-t”옵션으로 기존 클러스터 아이디를 지정하며, -c 옵션으로 Controller 설정 파일을 지정합니다.

Controller를 생성할 때 기존 ZooKeeper 클러스터 ID와 동일하게 생성해야 합니다.

기존 클러스터 아이디는 ZK Broker 로그 디렉토리 “meta.properties” 내용에서 확인이 가능합니다.

./kafka-storage.sh format --cluster-id "ZOOKEEPER_CLUSTER_ID" -c ../config/server.properties

위 명령어를 사용하면, server.properties에 작성된 “log.dirs” 경로에 카프카 로그 파일이 생성된 것을 확인할 수 있습니다.

KRaft Controller 시작

Controller에 마이그레이션을 활성화 하여 실행하면 다음과 같은 로그를 확인할 수 있습니다.

  • [0, 1, 2] ZK 브로커를 등록하는데 기다리는 중…
## Kraft controller server.log
[2023-02-13 10:01:03,629] INFO Still waiting for ZK brokers [0, 1, 2] to register with KRaft. (org.apache.kafka.metadata.migration.KRaftMigrationDriver)
[2023-02-13 10:01:04,629] INFO Still waiting for ZK brokers [0, 1, 2] to register with KRaft. (org.apache.kafka.metadata.migration.KRaftMigrationDriver)
[2023-02-13 10:02:10,643] INFO Still waiting for ZK brokers [0, 1, 2] to register with KRaft. (org.apache.kafka.metadata.migration.KRaftMigrationDriver)
[2023-02-13 10:02:11,640] INFO Still waiting for ZK brokers [0, 1, 2] to register with KRaft. (org.apache.kafka.metadata.migration.KRaftMigrationDriver)

3. ZK Broker 마이그레이션 등록

다음으로 ZK Broker를 KRaft Controller에 등록하기 위한 절차 입니다. 아래의 세 항목은 ZK Broker에 필수적으로 등록되어야 합니다.

zookeeper.metadata.migration.enable=true
controller.quorum.voters=3@localhost:9093
controller.listener.names=CONTROLLER

## ZK 브로커 
## server.properties

## 마이그레이션 활성화 설정
zookeeper.metadata.migration.enable=true
controller.quorum.voters=3@localhost:9093
controller.listener.names=CONTROLLER

## map에 Controller 추가
listener.security.protocol.map=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT

## 기존 항목
broker.id=0
...

ZK Broker에 설정을 추가 하고, [0, 1, 2]의 ID를 가진 Broker를 롤링 재 기동 하면 마이그레이션 모드로 등록 됩니다. 모든 ZK Broker를 마이그레이션 모드로 등록해야 합니다.

모두 등록이 되었다면, 아래와 같은 로그가 출력 됩니다. 마이그레이션이 성공했다는 메시지와 함께 어떤 데이터가 이전 됐는지 확인할 수 있습니다.

## Kraft controller server.log
[2023-02-13 10:01:02,656] INFO [Controller 3] Registered new broker: RegisterBrokerRecord(brokerId=0, isMigratingZkBroker=true, incarnationId=cgS-OnxQQgSUG0vtyQIJyw, brokerEpoch=198, endPoints=[BrokerEndpoint(name='PLAINTEXT', host='hostname', port=19092, securityProtocol=0)], features=[BrokerFeature(name='metadata.version', minSupportedVersion=8, maxSupportedVersion=8)], rack=null, fenced=true, inControlledShutdown=false) (org.apache.kafka.controller.ClusterControlManager)
[2023-02-13 10:01:02,725] INFO [Controller 3] The request from broker 0 to unfence has been granted because it has caught up with the offset of its register broker record 198. (org.apache.kafka.controller.BrokerHeartbeatManager)
[2023-02-13 10:01:03,629] INFO Still waiting for ZK brokers [1, 2] to register with KRaft. (org.apache.kafka.metadata.migration.KRaftMigrationDriver)
[2023-02-13 10:01:04,629] INFO Still waiting for ZK brokers [1, 2] to register with KRaft. (org.apache.kafka.metadata.migration.KRaftMigrationDriver)
[2023-02-13 10:02:10,643] INFO Still waiting for ZK brokers [1, 2] to register with KRaft. (org.apache.kafka.metadata.migration.KRaftMigrationDriver)
[2023-02-13 10:02:11,640] INFO Still waiting for ZK brokers [1, 2] to register with KRaft. (org.apache.kafka.metadata.migration.KRaftMigrationDriver)
[2023-02-13 10:02:12,390] INFO [Controller 3] Registered new broker: RegisterBrokerRecord(brokerId=1, isMigratingZkBroker=true, incarnationId=_6oGLJ2CSzekK0MmNqvYMg, brokerEpoch=339, endPoints=[BrokerEndpoint(name='PLAINTEXT', host='hostname', port=29092, securityProtocol=0)], features=[BrokerFeature(name='metadata.version', minSupportedVersion=8, maxSupportedVersion=8)], rack=null, fenced=true, inControlledShutdown=false) (org.apache.kafka.controller.ClusterControlManager)
[2023-02-13 10:02:12,454] INFO [Controller 3] The request from broker 1 to unfence has been granted because it has caught up with the offset of its register broker record 339. (org.apache.kafka.controller.BrokerHeartbeatManager)
[2023-02-13 10:02:12,640] INFO Still waiting for ZK brokers [2] to register with KRaft. (org.apache.kafka.metadata.migration.KRaftMigrationDriver)
[2023-02-13 10:02:13,640] INFO Still waiting for ZK brokers [2] to register with KRaft. (org.apache.kafka.metadata.migration.KRaftMigrationDriver)
[2023-02-13 10:02:14,641] INFO Still waiting for ZK brokers [2] to register with KRaft. (org.apache.kafka.metadata.migration.KRaftMigrationDriver)
[2023-02-13 10:04:54,698] INFO Still waiting for ZK brokers [2] to register with KRaft. (org.apache.kafka.metadata.migration.KRaftMigrationDriver)
[2023-02-13 10:04:55,704] INFO Still waiting for ZK brokers [2] to register with KRaft. (org.apache.kafka.metadata.migration.KRaftMigrationDriver)
[2023-02-13 10:04:55,979] INFO [Controller 3] Fencing broker 0 because its session has timed out. (org.apache.kafka.controller.ReplicationControlManager)
[2023-02-13 10:04:56,699] INFO Still waiting for ZK brokers [0, 2] to register with KRaft. (org.apache.kafka.metadata.migration.KRaftMigrationDriver)
[2023-02-13 10:05:07,705] INFO Still waiting for ZK brokers [0, 2] to register with KRaft. (org.apache.kafka.metadata.migration.KRaftMigrationDriver)
[2023-02-13 10:05:08,105] INFO [Controller 3] Re-registered broker id 0: RegisterBrokerRecord(brokerId=0, isMigratingZkBroker=true, incarnationId=aSmgQkh9RG2mCVufqfbYnQ, brokerEpoch=694, endPoints=[BrokerEndpoint(name='PLAINTEXT', host='hostname', port=19092, securityProtocol=0)], features=[BrokerFeature(name='metadata.version', minSupportedVersion=8, maxSupportedVersion=8)], rack=null, fenced=true, inControlledShutdown=false) (org.apache.kafka.controller.ClusterControlManager)
[2023-02-13 10:05:08,196] INFO [Controller 3] The request from broker 0 to unfence has been granted because it has caught up with the offset of its register broker record 694. (org.apache.kafka.controller.BrokerHeartbeatManager)
[2023-02-13 10:05:08,701] INFO Still waiting for ZK brokers [2] to register with KRaft. (org.apache.kafka.metadata.migration.KRaftMigrationDrive
[2023-02-13 10:12:47,800] INFO Still waiting for ZK brokers [2] to register with KRaft. (org.apache.kafka.metadata.migration.KRaftMigrationDriver)
[2023-02-13 10:12:48,816] INFO Still waiting for ZK brokers [2] to register with KRaft. (org.apache.kafka.metadata.migration.KRaftMigrationDriver)
[2023-02-13 10:12:49,038] INFO [Controller 3] Registered new broker: RegisterBrokerRecord(brokerId=2, isMigratingZkBroker=true, incarnationId=zD9baDWpRbSSffGeF1ua8g, brokerEpoch=1617, endPoints=[BrokerEndpoint(name='PLAINTEXT', host='hostname', port=39092, securityProtocol=0)], features=[BrokerFeature(name='metadata.version', minSupportedVersion=8, maxSupportedVersion=8)], rack=null, fenced=true, inControlledShutdown=false) (org.apache.kafka.controller.ClusterControlManager)
[2023-02-13 10:12:49,151] INFO [Controller 3] The request from broker 2 to unfence has been granted because it has caught up with the offset of its register broker record 1617. (org.apache.kafka.controller.BrokerHeartbeatManager)
[2023-02-13 10:12:50,789] INFO KRaft controller 3 overwriting /controller to become the active controller with ZK epoch 5. The previous controller was 1. (kafka.zk.KafkaZkClient)
[2023-02-13 10:12:50,813] INFO Successfully registered KRaft controller 3 with ZK epoch 5 (kafka.zk.KafkaZkClient)
[2023-02-13 10:12:51,784] INFO Starting ZK migration (org.apache.kafka.metadata.migration.KRaftMigrationDriver)
[2023-02-13 10:12:51,800] INFO Migrating 2 records from ZK (org.apache.kafka.metadata.migration.KRaftMigrationDriver)
[2023-02-13 10:12:51,801] INFO [Controller 3] Created topic test with topic ID LsZ0KDlCT7eZEN8Bnjyvfw. (org.apache.kafka.controller.ReplicationControlManager)
[2023-02-13 10:12:51,802] INFO [Controller 3] Created partition test-0 with topic ID LsZ0KDlCT7eZEN8Bnjyvfw and PartitionRegistration(replicas=[0], isr=[0], removingReplicas=[], addingReplicas=[], leader=0, leaderRecoveryState=RECOVERED, leaderEpoch=4, partitionEpoch=4). (org.apache.kafka.controller.ReplicationControlManager)
[2023-02-13 10:12:51,822] INFO Migrating 54 records from ZK (org.apache.kafka.metadata.migration.KRaftMigrationDriver)
[2023-02-13 10:12:51,822] INFO [Controller 3] Created topic __consumer_offsets with topic ID P5zRX8MSSgWwgOdTiOffsA. (org.apache.kafka.controller.ReplicationControlManager)
[2023-02-13 10:12:51,822] INFO [Controller 3] Created partition __consumer_offsets-22 with topic ID P5zRX8MSSgWwgOdTiOffsA and PartitionRegistration(replicas=[0], isr=[0], removingReplicas=[], addingReplicas=[], leader=0, leaderRecoveryState=RECOVERED, leaderEpoch=4, partitionEpoch=4). (org.apache.kafka.controller.ReplicationControlManager)
...
[2023-02-13 10:12:51,825] INFO [Controller 3] Created partition __consumer_offsets-40 with topic ID P5zRX8MSSgWwgOdTiOffsA and PartitionRegistration(replicas=[0], isr=[0], removingReplicas=[], addingReplicas=[], leader=0, leaderRecoveryState=RECOVERED, leaderEpoch=4, partitionEpoch=4). (org.apache.kafka.controller.ReplicationControlManager)
[2023-02-13 10:12:51,825] INFO [Controller 3] ConfigResource(type=TOPIC, name='__consumer_offsets'): set configuration compression.type to producer (org.apache.kafka.controller.ConfigurationControlManager)
[2023-02-13 10:12:51,825] INFO [Controller 3] ConfigResource(type=TOPIC, name='__consumer_offsets'): set configuration cleanup.policy to compact (org.apache.kafka.controller.ConfigurationControlManager)
[2023-02-13 10:12:51,825] INFO [Controller 3] ConfigResource(type=TOPIC, name='__consumer_offsets'): set configuration segment.bytes to 104857600 (org.apache.kafka.controller.ConfigurationControlManager)
[2023-02-13 10:12:51,880] INFO Migrating 1 records from ZK (org.apache.kafka.metadata.migration.KRaftMigrationDriver)
[2023-02-13 10:12:51,910] INFO Completed migration of metadata from Zookeeper to KRaft. A total of 57 metadata records were generated. The current metadata offset is now 1682 with an epoch of 1. Saw 3 brokers in the migrated metadata [0, 1, 2]. (org.apache.kafka.metadata.migration.KRaftMigrationDriver)

활성 Controller 변경 확인

다음으로, ZooKeeper 메타데이터를 통해 Controller가 변경 됐는지 살펴보겠습니다. 활성 Controller는 0번 이었지만 KRaft 가 “/controller”, “/controller_epoch” ZNode를 덮어 쓰기 함으로써 업데이트 되었습니다. 추가된 Controller 번호는 3번(node_id)입니다.

## zkCli.sh
[zk: localhost:2181(CONNECTED) 1] get /zkbroker/controller
{"version":2,"brokerid":3,"timestamp":"1676250770784","kraftControllerEpoch":1}
[zk: localhost:2181(CONNECTED) 2] get /zkbroker/controller_epoch
5
[zk: localhost:2181(CONNECTED) 3] get /zkbroker/migration
{"version":0,"kraft_metadata_offset":1682,"kraft_controller_id":3,"kraft_metadata_epoch":1,"kraft_controller_epoch":1}
[zk: localhost:2181(CONNECTED) 4]

이중 쓰기 모드

마이그레이션을 성공하면 다음과 같은 그림[그림-2]이 됩니다. 브로커는 아직 ZK Broker와 같이 동작하고, 추가된 KRaft Controller는 기존 카프카 Controller와 같이 동작합니다.

메타데이터는 이중 쓰기 모드 때문에 KRaft Controller “__cluster_metadata”와 “zookeeper” 모두에 저장됩니다 . 따라서 아직 완전히 KRaft 모드로 전향 된 것은 아닙니다. KRaft 모드로 완전히 바꾸기 위해서는 마이그레이션 모드를 해제해야 합니다.

그림-2

4. 마이그레이션 모드 해제

ZK Broker 설정

우선 ZK Broker를 KRaft Broker로 변경해야 합니다. 설정은 아래와 같으며 변경된 점은 “process.roles” 옵션이 추가 되었습니다. 여기서 “zookeeper.connect” 옵션은 더 이상 필요하지 않습니다. KRaft Broker로 전향 되기 때문입니다.

## ZK broker
## server.properties

## zookeeper.metadata.migration.enable=true

## 유지 및 변경 항목
controller.quorum.voters=3@localhost:9093
controller.listener.names=CONTROLLER
node.id=1
process.roles=broker

## 제거 항목
## broker.id=1
## zookeeper.connect=localhost:2181/zkbroker
## zookeeper.connection.timeout.ms=18000

이번에도 모든 Broker(0~2)의 설정을 편집하고 롤링으로 재 기동 합니다.

KRaft Controller 설정

마지막으로 KRaft Controller 마이그레이션 모드를 해제 합니다. ZooKeeper 관련 옵션은 필요 없으므로 주석 처리 합니다. KRaft Controller를 재 기동 하면 마이그레이션을 완료할 수 있습니다.

## Kraft controller
## controller.properties

## 수정 항목
zookeeper.metadata.migration.enable=false
## zookeeper.connect=localhost:2181/zkbroker

##
process.roles=controller
node.id=3
controller.quorum.voters=3@localhost:9093
listeners=CONTROLLER://:9093
controller.listener.names=CONTROLLER

마이그레이션이 완료되면 그림-3과 같은 구성이 됩니다. ZooKeeper는 분리되며 카프카 Controller 쿼럼이 구성되어 Controller가 직접 메타데이터를 관리하게 됩니다.

그림-3

5. 롤백

마이그레이션을 진행 시 문제가 생겼을 경우, ZooKeeper로 롤백 해야하는 과정은 매우 중요합니다. 아래의 항목을 설정하면 ZooKeeper로 롤백이 가능합니다.

  • Broker는 ZK 모드에서 하나씩 다시 시작
  • KRaft Controller Quorum이 완전히 종료
  • 운영자는 ZK Controller 선출을 허용하는 영구 “/controller” 및 “/controller_epoch” 노드를 제거

참고

--

--