March 22, 2024·4 min read·engineeringkafkastrimzikraftmigration

Strimzi makes KRaft migration an explicit state transition

Strimzi 0.40 coordinates ZooKeeper-to-KRaft migration through annotations and reported metadata states. Finalization remains a separate decision from completing the reversible migration stages.

Moving Kafka metadata out of ZooKeeper requires more than starting a controller quorum. Brokers and controllers must change configuration in a coordinated sequence, and rollback remains possible only while the migration preserves the necessary connection to ZooKeeper. An operator can automate those transitions while still leaving the final commitment visible to the person running the cluster.

Strimzi 0.40.0 adds ZooKeeper-to-KRaft migration support. Paolo Patierno's March 22 explanation describes the semi-automated sequence. The main interface is the strimzi.io/kraft annotation on the Kafka custom resource, together with metadata state reported by the operator.

Kafka's retained history is the capability I value most. Replacing the service that manages cluster metadata is a different change from replacing that history. The migration needs an explicit account of which metadata system is authoritative at each stage and when returning to the previous one stops being an option.

Preparation establishes the operator's control

The documented prerequisites include Kafka 3.7.0 or newer, the Unidirectional Topic Operator, and KafkaNodePool resources for the brokers. The Kafka resource needs node pools enabled and an initial KRaft annotation value of disabled to identify the ZooKeeper-based cluster.

A controller KafkaNodePool supplies the new controller processes. Changing the Kafka resource's KRaft annotation to migration starts the transition. The annotation represents an instruction to the operator, which then reconciles several configurations and rolling updates.

For an already prepared resource, the relevant metadata fields look like this:

metadata:
  annotations:
    strimzi.io/node-pools: enabled
    strimzi.io/kraft: migration

This is a fragment of the resource, not a complete cluster definition. Its usefulness is showing the requested transition without pretending that an annotation supplies the controller storage, broker configuration or other prerequisites by itself.

The release also moves the UseKRaft feature gate to beta and enables it by default. That gate controls availability of the operator capability. It does not substitute for the migration annotation and prerequisites described in the procedure. An existing ZooKeeper-based resource still has an explicit mode and a sequence to follow before it becomes a finalized KRaft cluster.

Migration includes several reconciliations

The operator deploys controllers with ZooKeeper connection details and migration enabled. It configures the brokers with the controller quorum information and migration settings, then rolls them. During reconciliation, it inspects the controller's ZkMigrationState metric and reflects progress through status.kafkaMetadataState.

The documented states include KRaftMigration, KRaftDualWriting and KRaftPostMigration. Reaching the last of those does not finalize the process. The controllers remain connected to ZooKeeper, preserving the dual-write condition needed for a possible rollback.

The operator also reconfigures the brokers to disconnect from ZooKeeper and rolls them again. That can sound like the entire migration is complete, but the controller side still retains the migration relationship. The reported state distinguishes broker progress from the final removal of that relationship.

This is why one annotation change can legitimately trigger multiple rolling updates. The declaration describes the desired migration stage, while the operator owns the sequence necessary to reach it. Watching only pod readiness would provide less information than the metadata state about which transition has actually finished.

Finalization is a separate action

At KRaftPostMigration, the user can choose to finalize by changing the KRaft annotation to enabled. The operator then removes ZooKeeper connection details from the controllers, disables their migration setting and rolls them. Once brokers and controllers operate in KRaft mode, it deletes the ZooKeeper-related resources.

The documentation permits removing the Kafka resource's ZooKeeper section after that process. This ordering follows the system's actual transition. Deleting a configuration section early is not the same operation as allowing the operator to finish migration and remove the now-unneeded resources.

The article also calls out inter.broker.protocol.version and log.message.format.version if they remain explicitly set. They are unsupported in KRaft, and the operator reports warnings. Removing them at the end can cause another rolling update, so a finished metadata migration does not necessarily mean every obsolete configuration entry has already disappeared.

Rollback has its own completion sequence

Before finalization, changing the annotation to rollback tells the operator to restore the brokers' ZooKeeper connection and roll them. The documented sequence then requires removal of the controller node pool and changing the annotation to disabled to complete the rollback.

The operator removes ZooKeeper's controller znode so brokers can elect a controller again, removes the KRaft quorum settings from the brokers, and rolls them. The metadata state returns to ZooKeeper. This is a stateful recovery procedure, not merely reversing the first annotation string and assuming every process has returned to its old configuration.

I find the separation between migration and finalization useful. It gives an administrator a named intermediate state in which the new metadata path is operating while rollback remains available. Strimzi automates configuration and rolling updates, while the resource records the decision to cross the finalization point.