Probado con apache/kafka 3.9.2 y 4.4.0 · Docker en arm64 · verificado

Actualizado: 2026-10-27

Para quitar ZooKeeper de un clúster de Kafka hay que pasar por la 3.9: es la última versión que habla con ZooKeeper y la única desde la que la guía oficial migra los metadatos a KRaft sin parar el servicio. Después ya puedes subir a la 4.x. He repetido la migración completa en Docker el 27 de octubre de 2026: un clúster de tres brokers con ZooKeeper en Kafka 3.9.2, un controlador KRaft, cuatro reinicios escalonados y la subida final a Kafka 4.4.0. Los 100 000 mensajes del topic de prueba y los offsets confirmados del grupo de consumidores llegaron intactos al final.

Kafka 4.0 llegó el 18 de marzo de 2025, según el anuncio oficial de la Apache Software Foundation[1], y con ella desapareció el modo ZooKeeper. La propuesta venía de lejos: KIP-500 se registró en octubre de 2019[2] y el modo KRaft se marcó apto para producción en Kafka 3.3, a finales de 2022. La migración de ZooKeeper a KRaft (KIP-866) entró como acceso anticipado en la 3.4. Desde la 4.0 ya no se puede arrancar un broker con ZooKeeper.

Para el contexto de infraestructura de datos donde Kafka opera, el análisis de Delta Lake e Iceberg comparativa describe la capa de almacenamiento que complementa a Kafka en arquitecturas modernas. El patrón de migración escalonada con dependencias externas también aplica al contexto de Kubernetes 1.33 mejoras, donde la actualización de plataforma sigue una lógica similar.

Puntos clave

  • KRaft almacena los metadatos del clúster en un topic interno __cluster_metadata usando Raft como consenso; no hay sistema externo.

  • Kafka 4.x no arranca en modo ZooKeeper. La migración se hace antes, en la 3.9.x, y la guía de 3.9 admite subir a esa versión desde cualquier 0.8.x a 3.8.x.

  • El orden es fijo: controlador KRaft en modo migración, brokers con la migración activada, brokers en modo KRaft y, al final, controlador sin ZooKeeper. Hasta ese último paso puedes volver atrás; después, no.

  • En la prueba, el controlador copió los metadatos en 145 ms (62 registros) en cuanto el último broker volvió con la migración activada.

  • Las métricas de ZooKeeper desaparecen; aparecen métricas nuevas del quórum de controladores que los paneles heredados necesitan actualizar.

Por qué ZooKeeper era un problema

Durante más de una década Kafka dependió de ZooKeeper para mantener metadatos del clúster: quién es el controlador, qué brokers están vivos, qué topics existen, qué particiones van a qué réplica. ZooKeeper es sólido pero no estaba diseñado para el volumen de metadatos que un clúster Kafka grande genera. Había un techo práctico: clústeres con cientos de miles de particiones encontraban ZooKeeper como cuello de botella.

El segundo problema era operativo: mantener dos sistemas distribuidos distintos con dos modelos de consenso distintos. Un equipo tenía que saber Kafka y también ZooKeeper, con su configuración, sus comandos, sus modos de fallo. Cada ampliación del clúster implicaba pensar en los dos lados. Cada fallo obligaba a decidir si era un fallo de Kafka o de ZooKeeper.

KRaft es la respuesta: el propio Kafka mantiene sus metadatos usando Raft como algoritmo de consenso, dentro de un quórum de nodos con el rol de controlador. No hay sistema externo. Los metadatos se almacenan en un topic interno llamado __cluster_metadata y se replican siguiendo las mismas reglas que cualquier otro topic.

Qué mejora con KRaft

La primera mejora es el techo de particiones. Según la documentación de Confluent sobre KRaft[3], el diseño permite clústeres con potencial de escalar a millones de particiones. ZooKeeper empezaba a sufrir en la práctica en el orden de los cientos de miles. Saber que el techo es alto cambia cómo diseñas tus topics: puedes ser más generoso con las particiones sin miedo a que el metadato sea el cuello de botella.

La segunda es la recuperación del controlador. Con ZooKeeper, un controlador nuevo tenía que leer todo el estado desde ZooKeeper antes de actuar. En KRaft, los controladores en espera ya tienen el registro de metadatos replicado y toman el relevo sin esa carga.

En un clúster de tres brokers no lo notas: en la prueba, cada broker tardó entre 3 y 6 s en volver tras un reinicio, con ZooKeeper y sin él. La diferencia aparece con decenas de miles de particiones.

La tercera mejora es operativa. Una sola tecnología, una sola configuración, un solo conjunto de comandos. Los comandos de administración como kafka-topics y kafka-configs siguen funcionando igual, pero detrás ya no hay un sistema adicional que puede fallar por su cuenta.

Lo que cambia en la operativa

El modelo de despliegue cambia en dos puntos. Primero, los brokers pueden asumir el rol de controlador además del rol de broker. En un clúster pequeño es habitual tener tres nodos que hacen ambas cosas. En un clúster grande se separan: tres o cinco nodos dedicados como controladores, y el resto como brokers puros.

Segundo, el concepto de cluster ID aparece desde el principio. En KRaft hay que formatear el almacenamiento con kafka-storage antes del primer arranque. En una migración no se genera un ID nuevo: se reutiliza el que ZooKeeper ya tiene, como se ve en el paso 1 de la migración.

El monitoreo también cambia. Las métricas de ZooKeeper desaparecen. Aparecen métricas nuevas sobre el quórum de controladores: cuántas hay en lag, cuánto tardan las actualizaciones de metadatos, cuántos eventos por segundo procesa el controlador activo. Los paneles de Grafana heredados necesitan ajuste.

Cómo migrar de ZooKeeper a KRaft paso a paso

La migración tiene cinco fases y todas se hacen con reinicios escalonados, sin parar el clúster. Las he seguido tal como las describe la guía oficial de migración de Kafka 3.9[4], con las imágenes oficiales apache/kafka:3.9.2 y apache/kafka:4.4.0 en Docker sobre arm64.

El laboratorio tenía un ZooKeeper, tres brokers (broker.id 1, 2 y 3) y un controlador KRaft nuevo. Antes de empezar creé el topic pedidos con 6 particiones y replicación 3 y envié 100 000 mensajes de 200 bytes con acks=all. Después consumí 40 000 con el grupo lectores, para comprobar al final que sobreviven los datos y los offsets confirmados. En producción usa tres controladores, no uno.

Paso 0: dejar los brokers en 3.9 con ZooKeeper

Todos los brokers tienen que ejecutar Kafka 3.9.x con el protocolo entre brokers fijado a 3.9. La guía no permite cambiar la versión de metadatos durante la migración, así que esta línea se queda en el server.properties de cada broker hasta el paso 3:

broker.id=1
listeners=PLAINTEXT://:9092
zookeeper.connect=zk:2181
inter.broker.protocol.version=3.9

Paso 1: crear el controlador KRaft con el ID del clúster

El controlador se formatea con el mismo ID de clúster que guarda ZooKeeper. Lo lees con zookeeper-shell.sh, y el node.id del controlador no puede coincidir con ningún broker.id existente, porque brokers y controladores comparten el espacio de IDs:

bin/zookeeper-shell.sh zk:2181 get /cluster/id
bin/kafka-storage.sh format --standalone \
  --cluster-id=g3x3R7lsSYqcRtSON2SpPg -c controller.properties
bin/kafka-server-start.sh controller.properties

La primera orden devuelve un JSON con el campo id, que es el valor que pasas a --cluster-id. El controller.properties activa la migración y apunta a ZooKeeper:

process.roles=controller
node.id=3000
controller.quorum.bootstrap.servers=ctl:9093
controller.listener.names=CONTROLLER
listeners=CONTROLLER://:9093
zookeeper.metadata.migration.enable=true
zookeeper.connect=zk:2181
inter.broker.listener.name=PLAINTEXT

Paso 2: reiniciar los brokers con la migración activada

Cada broker sigue en modo ZooKeeper, pero ahora conoce el quórum KRaft. Añade estas cuatro líneas a su configuración y reinícialos de uno en uno:

zookeeper.metadata.migration.enable=true
controller.quorum.bootstrap.servers=ctl:9093
controller.listener.names=CONTROLLER
listener.security.protocol.map=PLAINTEXT:PLAINTEXT,CONTROLLER:PLAINTEXT

La copia de metadatos empieza sola cuando vuelve el último broker. En la prueba, el log del controlador lo confirmó en cuanto volvió el tercer broker: «Completed migration of metadata from ZooKeeper to KRaft. 62 records were generated in 145 ms». Eran 2 topics, 56 particiones, 3 configuraciones y un registro de IDs de productor. Desde ese momento el controlador KRaft es el activo y escribe también en ZooKeeper, lo que permite volver atrás.

Paso 3: pasar los brokers a modo KRaft

Ahora cada broker deja ZooKeeper. Cambia broker.id por node.id con el mismo número, añade process.roles=broker y borra zookeeper.connect, zookeeper.metadata.migration.enable e inter.broker.protocol.version:

node.id=1
process.roles=broker
listeners=PLAINTEXT://:9092
controller.quorum.bootstrap.servers=ctl:9093
controller.listener.names=CONTROLLER
listener.security.protocol.map=PLAINTEXT:PLAINTEXT,CONTROLLER:PLAINTEXT

Otro reinicio escalonado. Si usas kafka.security.authorizer.AclAuthorizer, cámbialo en este paso por org.apache.kafka.metadata.authorizer.StandardAuthorizer. Cada broker volvió en 6 s.

Paso 4: finalizar y apagar ZooKeeper

Quita zookeeper.metadata.migration.enable y zookeeper.connect del controlador y reinícialo. Este es el punto sin retorno: hasta aquí la guía documenta cómo revertir a ZooKeeper según el último paso completado; después, no se puede. Con el controlador ya sin ZooKeeper, apagué ZooKeeper y comprobé el estado:

bin/kafka-topics.sh --bootstrap-server b1:9092 --describe --topic pedidos
bin/kafka-get-offsets.sh --bootstrap-server b1:9092 --topic pedidos
bin/kafka-consumer-groups.sh --bootstrap-server b1:9092 \
  --describe --group lectores

Las 6 particiones seguían con sus tres réplicas sincronizadas, la suma de los offsets finales daba 100 000 y el grupo lectores conservaba sus 40 000 mensajes confirmados.

Paso 5: subir a Kafka 4.x

Con el clúster ya en KRaft, la subida a 4.x es un reinicio escalonado más: primero el controlador y después cada broker, con la imagen nueva y la misma configuración. La guía de 4.4 admite subir desde cualquier 3.3.x en adelante. Al terminar, fija las funciones del clúster a la versión nueva:

bin/kafka-features.sh --bootstrap-server b1:9092 \
  upgrade --release-version 4.4
bin/kafka-features.sh --bootstrap-server b1:9092 describe

La orden subió metadata.version de 3.9-IV0 a 4.4-IV2 y activó, entre otras, kraft.version 1 y group.version 1. Después, el grupo lectores leyó exactamente los 60 000 mensajes que le quedaban. Desde crear el controlador hasta esa lectura final pasaron 2 min y 14 s en el laboratorio. En un clúster real, el tiempo lo marca cuánto tarda cada broker en recuperar sus réplicas tras cada reinicio.

Dónde puede doler

Hay puntos donde la migración duele:

  • Herramientas externas que hablan directamente con ZooKeeper: herramientas antiguas de gestión, scripts internos que leen nodos de ZooKeeper para inventario, integraciones de terceros que asumen el modelo anterior. Todo eso deja de funcionar y hay que catalogarlo antes de migrar para reescribirlo contra el AdminClient.

  • Funciones que KRaft aún no cubre: la guía de 3.9 avisa de que usar una función no implementada del todo en KRaft bloquea la migración, y de que un broker con JBOD y un directorio roto no migra hasta repararlo.

  • Configuración de ACL y cuotas: con ZooKeeper era habitual ponerlas directamente en los nodos. Con KRaft eso se traslada a kafka-configs y al StandardAuthorizer. La migración copia lo que hay, pero cualquier automatización de despliegue que toque nodos ZooKeeper habrá que reescribirla.

Cuándo esperar antes de migrar

Hay equipos que deberían esperar:

  • Clúster en Kafka 2.x: el primer paso es subir a 3.9.x con ZooKeeper, estabilizar y solo después planificar KRaft. Son dos cambios distintos y conviene no mezclarlos en la misma ventana.

  • Integraciones con software propietario que habla ZooKeeper: antes de migrar conviene verificar explícitamente con cada proveedor crítico.

  • En medio de otra migración grande: si el equipo está migrando a Kubernetes o cambiando de proveedor de nube al mismo tiempo, añadir KRaft al mismo proyecto multiplica el riesgo. Mejor secuenciar.

Mi lectura

KRaft es una mejora clara, y el salto a Kafka 4.x es una migración sensata para cualquier clúster que la planifique con tiempo. El procedimiento oficial funciona tal como está escrito: en el laboratorio no hizo falta ningún paso fuera de la guía. Pero no es una migración para hacer en viernes por la tarde: requiere inventario de dependencias externas, un ensayo en un entorno de prueba y una ventana de cambio pensada.

La recomendación práctica es ensayar las cinco fases con una copia del clúster y medir cuánto tarda cada broker en recuperar sus réplicas. Después cataloga cada integración externa que toque ZooKeeper y prepara los guiones de despliegue con antelación. El ensayo te dice cuánto durará la ventana real y si alguna función que usas bloquea la migración.

Lo que Kafka 4.0 consolida es una idea simple: una sola tecnología, un solo modelo de consenso, una sola cosa que aprender. Para quien opera Kafka a diario este es el cambio más significativo en años. Para quien solo publica y consume mensajes, es invisible. Ambas cosas son buenas señales.

Este artículo también está disponible en inglés: Kafka without ZooKeeper: KRaft in production.

Preguntas frecuentes

¿Puedo actualizar directamente a Kafka 4.x si mi clúster todavía usa ZooKeeper?

No: Kafka 4.x no arranca en modo ZooKeeper, así que la migración a KRaft tiene que estar terminada antes. El camino es subir a 3.9.x y crear el controlador KRaft con el ID del clúster que guarda ZooKeeper. Después reinicias los brokers con la migración activada, los pasas a modo KRaft y quitas ZooKeeper del controlador. Con el clúster ya en KRaft, la guía de 4.4 admite subir desde cualquier 3.3.x en adelante.

¿La migración a KRaft implica parar el servicio?

No: todas las fases se hacen con reinicios escalonados de un nodo cada vez. En la prueba en Docker, con tres brokers y replicación 3, sobrevivieron los 100 000 mensajes y los offsets confirmados del grupo de consumidores. Siguieron intactos tras la migración y tras la subida a 4.4.0.

La copia de metadatos tardó 145 ms. Lo que alarga la ventana en un clúster real es el tiempo que tarda cada broker en recuperar sus réplicas tras cada reinicio.

¿Qué puede romperse al quitar ZooKeeper?

Todo lo que hable directamente con ZooKeeper: herramientas antiguas de gestión, scripts internos que leen sus nodos para inventario e integraciones de terceros que asumen el modelo anterior. Catalógalo antes y reescríbelo contra el AdminClient. Las ACL pasan al StandardAuthorizer, y los paneles de Grafana heredados necesitan las nuevas métricas del quórum de controladores. Recuerda que, tras finalizar, la vuelta a ZooKeeper ya no es posible.

Fuentes:

  1. Apache Kafka: anuncio de la versión 4.0.0[1]
  2. Apache Kafka 3.9: migración de ZooKeeper a KRaft[4]
  3. Apache Kafka: guía de actualización a 3.9[5]
  4. Apache Kafka: guía de actualización a 4.4[6]
  5. Apache Software Foundation Jira: KIP-500[2]
  6. KIP-866: migración de ZooKeeper a KRaft[7]
  7. Confluent Developer: KRaft[3]

Fuentes

  1. anuncio oficial de la Apache Software Foundation
  2. KIP-500 se registró en octubre de 2019
  3. documentación de Confluent sobre KRaft
  4. guía oficial de migración de Kafka 3.9
  5. Apache Kafka: guía de actualización a 3.9
  6. Apache Kafka: guía de actualización a 4.4
  7. KIP-866: migración de ZooKeeper a KRaft