--- title: "Cómo crear un clúster Kafka de 3 nodos con KRaft" description: "Monta un clúster Apache Kafka de tres nodos en KRaft sobre Ubuntu 24.04 con replicación, comprueba la tolerancia a fallos y gestiona grupos y particiones." category: "Colas de Mensajes y Streaming" language: es source: https://cubepath.com/es/docs/colas-de-mensajes-y-streaming/configuracion-y-gestion-del-cluster-kafka updated: 2026-09-25 --- # Cómo crear un clúster Kafka de 3 nodos con KRaft Un único broker de Kafka es un punto único de fallo: si el servidor cae, los productores y consumidores se detienen y los datos no replicados pueden perderse. En este tutorial convertirás tres servidores Ubuntu 24.04 en un clúster Kafka 4 en modo KRaft, donde cada nodo actúa como broker y como controlador. Configurarás la replicación para tolerar la caída de un nodo sin perder mensajes confirmados, lo comprobarás apagando un broker y aprenderás las operaciones habituales: grupos de consumidores, reequilibrio de líderes y ampliación del clúster. ## Requisitos previos - Tres servidores con Ubuntu 24.04 LTS, por ejemplo tres VPS de CubePath, con al menos 4 GB de RAM cada uno. - Los tres servidores conectados por una red privada. En los ejemplos se usan las IPs `10.0.0.11`, `10.0.0.12` y `10.0.0.13`; sustitúyelas por las tuyas. - Kafka 4 instalado en cada nodo en `/opt/kafka`, con Java 21, el usuario `kafka`, el directorio `/var/lib/kafka/data` y la unidad systemd `kafka.service`, tal como se explica en la guía "Cómo instalar Apache Kafka en Ubuntu 24.04 con KRaft" (pasos 1, 2 y 4). No formatees todavía el almacenamiento ni arranques el servicio. - Un usuario no root con privilegios `sudo` en los tres nodos. Si ya formateaste o arrancaste Kafka en modo de un nodo, detén el servicio y vacía el directorio de datos antes de empezar: ```bash sudo systemctl stop kafka sudo find /var/lib/kafka/data -mindepth 1 -delete ``` ## Cómo se organiza el clúster | Concepto | Valor en este tutorial | Por qué | |---|---|---| | Nodos | 3, cada uno con `process.roles=broker,controller` | El quórum KRaft necesita mayoría: con 3 controladores tolera la caída de 1 | | Factor de replicación | 3 | Cada partición tiene una copia en cada nodo | | `min.insync.replicas` | 2 | Con `acks=all`, un mensaje solo se confirma cuando lo tienen al menos 2 réplicas | | Elección no limpia | Desactivada | Una réplica desactualizada nunca pasa a líder, así no se pierden mensajes confirmados | Con esta combinación el clúster sigue aceptando escrituras con un nodo caído y ningún mensaje confirmado se pierde. ## Paso 1: Resolver los nombres de los nodos Asigna un nombre a cada nodo en `/etc/hosts` en los tres servidores. Así los mensajes de log y los comandos son más legibles: ```bash sudo nano /etc/hosts ``` ```text 10.0.0.11 kafka-1 10.0.0.12 kafka-2 10.0.0.13 kafka-3 ``` Comprueba la conectividad desde cada nodo: ```bash ping -c 2 kafka-2 ``` ## Paso 2: Abrir los puertos en la red privada Kafka usa dos puertos: 9092 para clientes y replicación entre brokers, y 9093 para el quórum de controladores. Permítelos solo desde la red privada en los tres nodos: ```bash sudo ufw allow from 10.0.0.0/24 to any port 9092 proto tcp sudo ufw allow from 10.0.0.0/24 to any port 9093 proto tcp sudo ufw status ``` Si tus aplicaciones están en otra red, añade una regla para el puerto 9092 desde sus IPs. El puerto 9093 no debe ser accesible para los clientes. ## Paso 3: Configurar cada broker Edita `server.properties` en cada nodo: ```bash sudo nano /opt/kafka/config/server.properties ``` Sustituye el contenido por esta configuración. Es la del nodo `kafka-1`: ```properties # Identidad y roles process.roles=broker,controller node.id=1 # Quórum estático de controladores: id@host:puerto de los tres nodos controller.quorum.voters=1@10.0.0.11:9093,2@10.0.0.12:9093,3@10.0.0.13:9093 # Listeners listeners=PLAINTEXT://10.0.0.11:9092,CONTROLLER://10.0.0.11:9093 advertised.listeners=PLAINTEXT://10.0.0.11:9092 controller.listener.names=CONTROLLER inter.broker.listener.name=PLAINTEXT listener.security.protocol.map=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT # Almacenamiento log.dirs=/var/lib/kafka/data num.partitions=6 log.retention.hours=168 log.segment.bytes=1073741824 # Replicación y durabilidad default.replication.factor=3 min.insync.replicas=2 offsets.topic.replication.factor=3 transaction.state.log.replication.factor=3 transaction.state.log.min.isr=2 unclean.leader.election.enable=false # Los topics se crean de forma explícita auto.create.topics.enable=false # Hilos num.network.threads=3 num.io.threads=8 ``` En `kafka-2` y `kafka-3` cambia solo tres valores: `node.id` (2 y 3) y la IP en `listeners` y `advertised.listeners` (10.0.0.12 y 10.0.0.13). `controller.quorum.voters` es idéntico en los tres. Los ajustes clave: - `controller.quorum.voters` define un quórum estático de tres controladores. Cada `node.id` debe coincidir con el número antes de la `@`. - `offsets.topic.replication.factor` y `transaction.state.log.*` protegen los topics internos donde Kafka guarda los offsets de los consumidores y el estado de las transacciones. Por defecto usan 3 réplicas, pero dejarlo explícito evita sorpresas. - `auto.create.topics.enable=false` evita que un error tipográfico en un productor cree un topic con configuración por defecto. ## Paso 4: Formatear el almacenamiento con un ID de clúster común Todos los nodos deben compartir el mismo ID de clúster. Genéralo en `kafka-1`: ```bash /opt/kafka/bin/kafka-storage.sh random-uuid ``` ```output MkU3OEVBNTcwNTJENDM2Qk ``` Copia el valor y formatea el almacenamiento en los tres nodos con ese mismo ID: ```bash sudo -u kafka /opt/kafka/bin/kafka-storage.sh format -t MkU3OEVBNTcwNTJENDM2Qk -c /opt/kafka/config/server.properties ``` ```output Formatting metadata directory /var/lib/kafka/data with metadata.version 4.1-IV1. ``` Como `controller.quorum.voters` ya lista los controladores, aquí no se usa la opción `--standalone` de la instalación de un nodo. ## Paso 5: Arrancar el clúster Arranca Kafka en los tres nodos, con pocos segundos de diferencia: ```bash sudo systemctl enable --now kafka ``` El quórum necesita al menos dos controladores para elegir líder, así que el primer nodo esperará hasta que arranque el segundo. Cuando estén los tres, comprueba el estado del quórum desde cualquier nodo: ```bash /opt/kafka/bin/kafka-metadata-quorum.sh --bootstrap-server 10.0.0.11:9092 describe --status ``` ```output ClusterId: MkU3OEVBNTcwNTJENDM2Qk LeaderId: 2 LeaderEpoch: 1 HighWatermark: 318 MaxFollowerLag: 0 MaxFollowerLagTimeMs: 0 CurrentVoters: [{"id": 1, ...}, {"id": 2, ...}, {"id": 3, ...}] CurrentObservers: [] ``` Comprueba que los tres controladores están al día con la opción `--replication`: ```bash /opt/kafka/bin/kafka-metadata-quorum.sh --bootstrap-server 10.0.0.11:9092 describe --replication ``` ```output NodeId DirectoryId LogEndOffset Lag LastFetchTimestamp LastCaughtUpTimestamp Status 2 ... 318 0 1758794400000 1758794400000 Leader 1 ... 318 0 1758794399800 1758794399800 Follower 3 ... 318 0 1758794399800 1758794399800 Follower ``` ## Paso 6: Crear un topic replicado Crea un topic con 6 particiones y factor de replicación 3: ```bash /opt/kafka/bin/kafka-topics.sh --bootstrap-server kafka-1:9092 \ --create --topic payments --partitions 6 --replication-factor 3 ``` Consulta dónde está cada partición: ```bash /opt/kafka/bin/kafka-topics.sh --bootstrap-server kafka-1:9092 --describe --topic payments ``` ```output Topic: payments TopicId: ... PartitionCount: 6 ReplicationFactor: 3 Configs: ... Topic: payments Partition: 0 Leader: 1 Replicas: 1,2,3 Isr: 1,2,3 ... Topic: payments Partition: 1 Leader: 2 Replicas: 2,3,1 Isr: 2,3,1 ... Topic: payments Partition: 2 Leader: 3 Replicas: 3,1,2 Isr: 3,1,2 ... ... ``` - `Leader` es el broker que atiende lecturas y escrituras de la partición. - `Replicas` son los brokers que tienen copia. El primero es el líder preferido. - `Isr` (in-sync replicas) son las réplicas al día. Si una réplica sale de esta lista, está retrasada o caída. Envía mensajes con `acks=all`, la configuración que tus productores deben usar para datos que no pueden perderse: ```bash /opt/kafka/bin/kafka-console-producer.sh --bootstrap-server kafka-1:9092,kafka-2:9092,kafka-3:9092 \ --topic payments --producer-property acks=all ``` Escribe unos cuantos mensajes y sal con `Ctrl+C`. Indica siempre varios brokers en `--bootstrap-server`: el cliente solo los usa para descubrir el clúster, pero si el único que indicas está caído no podrá conectar. ## Paso 7: Probar la tolerancia a fallos En `kafka-3`, detén el broker: ```bash sudo systemctl stop kafka ``` Desde `kafka-1`, comprueba el topic otra vez: ```bash /opt/kafka/bin/kafka-topics.sh --bootstrap-server kafka-1:9092 --describe --topic payments ``` ```output Topic: payments Partition: 2 Leader: 1 Replicas: 3,1,2 Isr: 1,2 ... ``` Las particiones que lideraba el broker 3 tienen ahora otro líder y el 3 ha desaparecido del ISR. Lista las particiones sin réplicas suficientes: ```bash /opt/kafka/bin/kafka-topics.sh --bootstrap-server kafka-1:9092 --describe --under-replicated-partitions ``` Todas aparecerán, porque les falta una réplica, pero el ISR de cada una sigue teniendo 2 miembros, que es el mínimo configurado. Envía un mensaje con `acks=all` para confirmar que el clúster sigue aceptando escrituras: ```bash echo "pago de prueba con un nodo caído" | /opt/kafka/bin/kafka-console-producer.sh \ --bootstrap-server kafka-1:9092,kafka-2:9092 --topic payments --producer-property acks=all ``` El comando termina sin errores. Si apagaras un segundo nodo, cada partición quedaría con una sola réplica en el ISR y las escrituras con `acks=all` fallarían con `NotEnoughReplicasException` (además, el quórum de controladores perdería la mayoría). Es el comportamiento esperado: Kafka prefiere rechazar escrituras antes que confirmar datos que solo existen en un disco. Vuelve a arrancar el broker en `kafka-3`: ```bash sudo systemctl start kafka ``` Desde `kafka-1`, comprueba que no quedan particiones subreplicadas. Tras unos segundos, el comando no debe devolver nada: ```bash /opt/kafka/bin/kafka-topics.sh --bootstrap-server kafka-1:9092 --describe --under-replicated-partitions ``` Tras la recuperación, el broker 3 ya no lidera ninguna partición. Kafka devuelve el liderazgo a los líderes preferidos automáticamente cada pocos minutos (`auto.leader.rebalance.enable` está activo por defecto), pero puedes forzarlo al momento: ```bash /opt/kafka/bin/kafka-leader-election.sh --bootstrap-server kafka-1:9092 \ --election-type preferred --all-topic-partitions ``` ## Paso 8: Gestionar grupos de consumidores Cada grupo de consumidores guarda su posición (offset) en cada partición. Lista los grupos y consulta el retraso de uno: ```bash /opt/kafka/bin/kafka-consumer-groups.sh --bootstrap-server kafka-1:9092 --list /opt/kafka/bin/kafka-consumer-groups.sh --bootstrap-server kafka-1:9092 --describe --group payments-service ``` La columna `LAG` indica los mensajes pendientes por partición. Para reprocesar mensajes, por ejemplo tras corregir un fallo en el consumidor, puedes mover los offsets del grupo. El grupo debe estar inactivo (todos sus consumidores detenidos). Primero previsualiza el cambio: ```bash /opt/kafka/bin/kafka-consumer-groups.sh --bootstrap-server kafka-1:9092 \ --group payments-service --topic payments --reset-offsets --to-datetime 2026-09-25T00:00:00.000 --dry-run ``` Si el resultado es el esperado, aplícalo sustituyendo `--dry-run` por `--execute`. Otras opciones útiles son `--to-earliest`, `--to-latest` y `--shift-by -100`. ## Paso 9: Añadir un broker y repartir particiones Cuando el clúster se queda corto de disco o de red, añade brokers. Para un cuarto nodo (`10.0.0.14`) basta con que sea solo broker; el quórum de controladores sigue siendo de tres. Instala Kafka como en los requisitos, abre los puertos y usa esta configuración, cambiando solo lo que difiere de los demás nodos: ```properties process.roles=broker node.id=4 controller.quorum.voters=1@10.0.0.11:9093,2@10.0.0.12:9093,3@10.0.0.13:9093 listeners=PLAINTEXT://10.0.0.14:9092 advertised.listeners=PLAINTEXT://10.0.0.14:9092 controller.listener.names=CONTROLLER inter.broker.listener.name=PLAINTEXT listener.security.protocol.map=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT ``` Mantén también las secciones de almacenamiento y replicación del paso 3. Formatea con el mismo ID de clúster y arranca el servicio. Un broker nuevo no recibe particiones existentes por sí solo; hay que reasignarlas. Crea un archivo con los topics a mover: ```bash nano ~/topics.json ``` ```json {"version": 1, "topics": [{"topic": "payments"}]} ``` Genera una propuesta de reparto entre los cuatro brokers: ```bash /opt/kafka/bin/kafka-reassign-partitions.sh --bootstrap-server kafka-1:9092 \ --topics-to-move-json-file ~/topics.json --broker-list "1,2,3,4" --generate ``` La salida muestra la asignación actual (guárdala para poder volver atrás) y la propuesta. Copia la propuesta en `~/reassign.json` y ejecútala limitando el ancho de banda de la copia a 50 MB/s para no saturar la red: ```bash /opt/kafka/bin/kafka-reassign-partitions.sh --bootstrap-server kafka-1:9092 \ --reassignment-json-file ~/reassign.json --execute --throttle 50000000 ``` Comprueba el progreso hasta que todas las particiones estén completas. Ejecutar `--verify` al final también elimina el límite de ancho de banda: ```bash /opt/kafka/bin/kafka-reassign-partitions.sh --bootstrap-server kafka-1:9092 \ --reassignment-json-file ~/reassign.json --verify ``` ```output Status of partition reassignment: Reassignment of partition payments-0 is completed. ... Clearing broker-level throttles on brokers 1,2,3,4 Clearing topic-level throttles on topic payments ``` ## Reinicios y mantenimiento Para actualizar o reiniciar nodos sin cortar el servicio, hazlo de uno en uno: 1. Comprueba que no hay particiones subreplicadas con `--under-replicated-partitions`. 2. Reinicia el nodo con `sudo systemctl restart kafka`. 3. Espera a que el comando anterior vuelva a no devolver nada y a que `kafka-metadata-quorum.sh describe --replication` muestre lag 0 antes de pasar al siguiente nodo. Vigila también estas señales: particiones subreplicadas u offline, lag creciente en los grupos de consumidores y uso de disco en `/var/lib/kafka/data`. Para llevarlas a un sistema de monitorización, Kafka expone todas sus métricas por JMX. ## Solución de problemas - **Un nodo no arranca con `Invalid cluster.id`**: se formateó con un ID de clúster distinto. Detén el servicio, vacía `/var/lib/kafka/data` en ese nodo y formatéalo con el ID correcto. - **El quórum no elige líder**: menos de dos controladores se ven entre sí. Comprueba el puerto 9093 con `nc -zv kafka-2 9093` y que `controller.quorum.voters` es idéntico en todos los nodos. - **`NotEnoughReplicasException` en los productores**: el ISR de alguna partición tiene menos réplicas que `min.insync.replicas`. Revisa qué brokers están caídos o retrasados con `--describe --under-replicated-partitions`. - **Comportamiento errático del quórum o brokers que se expulsan entre sí**: dos nodos comparten `node.id`. Cada nodo necesita uno único y debe coincidir con su entrada en `controller.quorum.voters`. ## Conclusión Has montado un clúster Kafka de tres nodos en KRaft con replicación triple, `min.insync.replicas=2` y elección no limpia desactivada, has comprobado que sobrevive a la caída de un nodo sin perder escrituras confirmadas y has visto cómo gestionar offsets, reequilibrar líderes y ampliar el clúster. Como siguientes pasos, protege los listeners con TLS y SASL/SCRAM, exporta las métricas JMX a tu sistema de monitorización y configura los productores de tus aplicaciones con `acks=all` e idempotencia.