Un análisis profundo de los problemas de conexión de Kafka con ZooKeeper
Soluciona fallos de conexión de Kafka con ZooKeeper con comprobaciones prácticas de configuración, red, tiempos de espera, registros y carga del broker.
Un análisis profundo de los problemas de conexión de Kafka con ZooKeeper
Los problemas de conexión de Kafka con ZooKeeper afectan principalmente a clústeres antiguos de Kafka y a aquellos que no han migrado al modo KRaft. Las implementaciones más nuevas de Kafka pueden funcionar sin ZooKeeper, pero muchos sistemas de producción aún dependen de él. Si tus brokers usan zookeeper.connect en server.properties, ZooKeeper sigue siendo parte de tu plano de control y merece el mismo cuidado que el propio Kafka.
Cuando un broker de Kafka no puede mantener su sesión con ZooKeeper, los síntomas pueden parecer más graves que un simple problema de conexión. Los brokers pueden reiniciarse. Las elecciones del controlador pueden repetirse. Las particiones pueden quedar no disponibles. Los registros pueden mostrar expiración de sesión, renuncia del controlador o intentos repetidos de reconexión. Los productores y consumidores solo ven el efecto secundario: errores de metadatos, tiempos de espera o líderes inestables.
Comienza por el papel que desempeña ZooKeeper. En los clústeres de Kafka basados en ZooKeeper, los brokers se registran allí, la elección del controlador depende de él y la coordinación de metadatos del clúster pasa por él. Si un broker pierde su sesión con ZooKeeper el tiempo suficiente para que expire, Kafka trata a ese broker como desaparecido del clúster. Incluso si el proceso del broker sigue ejecutándose, el clúster puede mover el liderazgo lejos de él.
La primera comprobación es aburrida y a menudo detecta el problema: verifica zookeeper.connect en cada broker.
zookeeper.connect=zk01.example.com:2181,zk02.example.com:2181,zk03.example.com:2181/kafka
zookeeper.connection.timeout.ms=18000
zookeeper.session.timeout.ms=18000
La cadena de conexión debe enumerar los miembros del conjunto que Kafka puede alcanzar. Si usas una ruta chroot como /kafka, inclúyela de manera consistente en cada broker. No configures la mitad de los brokers con /kafka y la otra mitad sin ella; se comportarán como si estuvieran hablando con diferentes clústeres de Kafka. Si usas un chroot, créalo primero o confirma que existe con las herramientas de ZooKeeper.
Verifica el DNS además del texto de la configuración. Un nombre de host que se resuelve correctamente desde tu portátil puede fallar desde una subred de brokers. Ejecuta las comprobaciones desde el host del broker de Kafka, no desde un bastión a menos que el bastión tenga la misma ruta de red.
getent hosts zk01.example.com
nc -vz zk01.example.com 2181
nc -vz zk02.example.com 2181
nc -vz zk03.example.com 2181
Una conexión TCP exitosa no prueba que ZooKeeper esté saludable, pero una conexión fallida es suficiente para seguir investigando firewalls, grupos de seguridad, enrutamiento, DNS o configuración del listener. Prueba cada broker de Kafka con cada nodo de ZooKeeper. La conectividad parcial es peor que una interrupción limpia porque la falla puede aparecer solo cuando un broker intenta conectarse a un miembro específico del conjunto.
Los comandos de cuatro letras de ZooKeeper pueden ayudar cuando están habilitados. Muchas instalaciones los restringen, así que no asumas que funcionan. Si están permitidos, ruok debería devolver imok, y mntr puede mostrar estadísticas útiles del servidor.
echo ruok | nc zk01.example.com 2181
echo mntr | nc zk01.example.com 2181
Si estos comandos están deshabilitados, usa las herramientas de administración compatibles o tu pila de monitoreo. El punto es responder una pregunta simple: ¿está ZooKeeper escuchando, participando en el conjunto y respondiendo rápidamente?
A continuación, inspecciona la salud del conjunto de ZooKeeper. Un conjunto de tres nodos puede tolerar que un nodo de ZooKeeper esté caído. No puede tolerar dos. Un conjunto de cinco nodos puede tolerar dos. Evita conjuntos de tamaño par porque añaden costo sin mejorar el quórum de la manera que la gente espera. Tres y cinco son opciones comunes.
Del lado de ZooKeeper, revisa zoo.cfg. Confirma clientPort, tickTime, initLimit, syncLimit y las líneas de servidor. Asegúrate de que los nombres de host de servidor anunciados sean accesibles entre los nodos de ZooKeeper, no solo desde los brokers de Kafka. Los pares de ZooKeeper necesitan sus propios puertos de quórum y elección de líder. Un broker de Kafka puede alcanzar el puerto 2181 mientras el conjunto de ZooKeeper en sí mismo no está saludable porque el tráfico entre pares está bloqueado.
El ajuste del tiempo de espera de sesión es otra fuente común de confusión. Kafka solicita un tiempo de espera de sesión a ZooKeeper, pero ZooKeeper impone límites basados en su propia configuración. En ZooKeeper, el tiempo de espera mínimo de sesión suele ser 2 * tickTime y el máximo suele ser 20 * tickTime, a menos que se anule con configuraciones específicas del servidor. Eso significa que un valor de tiempo de espera de Kafka fuera del rango permitido puede ser ajustado por ZooKeeper.
Si tickTime=2000, el rango de sesión permitido habitual es aproximadamente de 4 a 40 segundos. Una configuración de Kafka como zookeeper.session.timeout.ms=18000 encaja dentro de ese rango. Un tiempo de espera muy bajo puede producir fallos falsos durante pausas cortas de red o pausas de recolección de basura. Un tiempo de espera muy alto puede hacer que las fallas reales de los brokers tarden más en detectarse. Estás eligiendo entre sensibilidad y estabilidad.
No cambies tickTime a la ligera. Afecta al conjunto de ZooKeeper, no solo a Kafka. Si necesitas más tolerancia para las pausas de los brokers, a menudo es mejor comenzar revisando zookeeper.session.timeout.ms de Kafka, el comportamiento JVM del broker y la salud de la red antes de cambiar la temporización de ZooKeeper.
Los registros suelen contar la historia si los alineas por marca de tiempo. En los brokers de Kafka, busca mensajes relacionados con desconexiones de ZooKeeper y expiración de sesión:
rg -i "zookeeper|session|expired|controller|reconnect" /var/log/kafka/server.log
Los patrones importan más que una sola línea. Una reconexión única durante un reinicio planificado de ZooKeeper puede ser inofensiva. La expiración repetida cada pocos minutos apunta a inestabilidad. La expiración durante la recolección de basura apunta a pausas de JVM o sobrecarga del broker. La expiración al mismo tiempo en muchos brokers apunta a ZooKeeper, la red o un evento de infraestructura compartido.
En los nodos de ZooKeeper, verifica cambios de líder, advertencias de fsync, limitación de conexiones y latencia de solicitudes larga. ZooKeeper es sensible a la latencia del disco porque escribe registros de transacciones. Un disco lento puede hacer que el servicio parezca accesible mientras aún falla al responder lo suficientemente rápido para sesiones estables.
La latencia de red y la pérdida de paquetes son más importantes que el ancho de banda bruto para ZooKeeper. Los brokers de Kafka no necesitan un gran rendimiento hacia ZooKeeper, pero necesitan una comunicación confiable y de baja latencia. Si los brokers y ZooKeeper están divididos en redes distantes, espera problemas. Mantenlos cerca. En entornos de nube, evita enrutar el tráfico de broker a ZooKeeper a través de NAT innecesario, firewalls sobrecargados o rutas entre regiones.
La contención de recursos en el broker de Kafka puede parecer exactamente un problema de ZooKeeper. Si la JVM detiene el mundo durante una pausa larga de recolección de basura, el broker puede perder latidos. Si la CPU está saturada, el manejo de latidos puede retrasarse. Si el host está atascado en alta espera de E/S, Kafka puede no mantener el ritmo del trabajo de coordinación. Verifica las métricas del broker en las mismas marcas de tiempo que las desconexiones de ZooKeeper.
Preguntas útiles del lado del broker incluyen: ¿aumentó el uso del heap antes de la desconexión?, ¿aumentó el tiempo de pausa de GC?, ¿fue alta la espera de E/S del disco?, ¿aumentaron las retransmisiones de red?, ¿hubo grandes reasignaciones de particiones o movimientos de líder al mismo tiempo? Un broker ahogado bajo carga puede necesitar menos líderes de partición, mejor disco, ajuste de JVM o un cambio de tráfico. Aumentar los tiempos de espera de ZooKeeper puede ocultar el síntoma sin solucionar la causa.
La consistencia de la configuración es fácil de pasar por alto. Todos los brokers de Kafka en el mismo clúster deben usar la misma cadena de conexión de ZooKeeper y chroot. También deben tener valores únicos de broker.id. Un ID de broker duplicado puede causar un comportamiento de registro confuso porque dos procesos intentan representar el mismo broker.
Si recientemente cambiaste nombres de host de ZooKeeper, certificados, reglas de firewall o configuraciones de brokers de Kafka, compara el broker que funciona con el que falla. Las pequeñas diferencias son comunes: un sufijo DNS antiguo, una ruta chroot faltante, un grupo de seguridad adjunto a dos brokers pero no al tercero, o un error tipográfico en un archivo de entorno de systemd.
La recuperación depende de lo que se rompió. Si faltaba una regla de firewall, corrígela y reinicia el broker afectado si no se reconecta limpiamente. Si ZooKeeper perdió el quórum, restaura el quórum primero antes de reiniciar los brokers de Kafka. Si un broker expiró porque estaba sobrecargado, reiniciarlo puede traerlo de vuelta temporalmente, pero el problema volverá a menos que elimines la presión.
Usa reinicios progresivos. Reiniciar todos los brokers de Kafka a la vez porque ZooKeeper era inestable puede convertir una interrupción parcial en una completa. Restablece la salud de ZooKeeper, luego reinicia o recupera los brokers uno a la vez mientras observas la estabilidad del controlador y el liderazgo de las particiones.
Para la estabilidad a largo plazo, monitorea ambos lados. En ZooKeeper, observa la latencia de solicitudes, solicitudes pendientes, cambios de líder, estado de sincronización de seguidores, espacio en disco y reinicios de procesos. En Kafka, observa cambios de controlador, particiones fuera de línea, particiones sub-replicadas, reinicios de brokers y registros que mencionen expiración de sesión de ZooKeeper. Alerta sobre patrones repetidos, no solo sobre la muerte total del proceso.
La solución más limpia, para equipos que planean una actualización mayor, puede ser la migración de ZooKeeper al modo KRaft de Kafka. Eso es un proyecto, no un paso de respuesta a incidentes. Requiere planificación de versiones, comprobaciones de compatibilidad y un trabajo de migración cuidadoso. Hasta entonces, trata a ZooKeeper como infraestructura de producción. Mantenlo pequeño, cerca de Kafka, configurado de manera consistente, monitoreado y aburrido.
Un patrón práctico de runbook es construir una pequeña matriz durante el incidente. Pon los brokers de Kafka en un eje y los nodos de ZooKeeper en el otro. Llena cada celda con el resultado de nc -vz host 2181 y, si está disponible, una verificación simple de salud de ZooKeeper. Esto convierte informes vagos de "Kafka no puede alcanzar ZooKeeper" en un patrón visible. Si cada broker falla al alcanzar zk02, investiga zk02 o su ruta de red. Si solo broker-4 falla al alcanzar cada nodo de ZooKeeper, investiga el host de ese broker, la tabla de rutas, DNS o firewall.
La sincronización de tiempo también puede importar. La mecánica de sesión de ZooKeeper no requiere relojes de pared perfectamente idénticos para cada operación, pero relojes muy desviados hacen que los registros sean más difíciles de interpretar y pueden romper la automatización circundante, certificados y monitoreo. Mantén NTP o chrony saludables en los nodos de Kafka y ZooKeeper. Cuando las marcas de tiempo no coinciden durante una interrupción, la gente pierde tiempo persiguiendo la secuencia incorrecta de eventos.
Ten cuidado con las implementaciones de ZooKeeper en contenedores u orquestadas. ZooKeeper almacena identidad y datos en disco. Si los pods se mueven y pierden identidad persistente, o si el descubrimiento de servicios apunta a los clientes a nodos que no están listos, Kafka puede ver un comportamiento de conexión inestable. La identidad de tipo StatefulSet, volúmenes persistentes, DNS estable y comprobaciones de preparación importan. Un conjunto de ZooKeeper no debe comportarse como un conjunto de pods web desechables sin estado.
La configuración de seguridad añade otra capa. Si se introdujo recientemente SASL, TLS o política de red, los fallos de conexión pueden parecer problemas simples de accesibilidad al principio. Verifica si los registros de Kafka muestran fallos de autenticación, fallos de handshake o errores de autorización en lugar de tiempos de espera TCP. Un puerto puede estar abierto mientras la sesión aún falla porque el broker no puede autenticarse en ZooKeeper.
Después del incidente, guarda un breve registro del síntoma exacto, la causa raíz y la solución. Los problemas de ZooKeeper a menudo se repiten porque la reparación original fue local: una regla de firewall, un reinicio de broker, un aumento de tiempo de espera. Una buena nota posterior al incidente debe indicar si el clúster tenía quórum, qué brokers perdieron sesiones, si el ISR se redujo, si el controlador cambió y qué monitoreo lo detectará antes la próxima vez.
Si estás solucionando problemas desde Kubernetes u otro programador, verifica también dónde aterrizaron las cargas de trabajo de Kafka y ZooKeeper. Un problema de red a nivel de nodo, problema de disco o evento de inanición de CPU puede afectar solo a los pods programados allí. Mover un pod puede parecer que soluciona el problema, pero el problema real puede ser el host. Compara eventos y métricas de nodos antes de declarar la aplicación reparada.
Las copias de seguridad y las instantáneas merecen precaución. Los directorios de datos de ZooKeeper no deben ser instantáneos casualmente mientras el proceso está activo a menos que tu método de copia de seguridad esté diseñado para ello. Para los metadatos de Kafka, un estado de ZooKeeper dañado o desactualizado puede ser extremadamente disruptivo. Sigue las prácticas de copia de seguridad compatibles con ZooKeeper y prueba los procedimientos de restauración fuera de producción. Una copia de seguridad que nadie ha restaurado es solo un archivo esperanzador.
La mejor medida preventiva es mantener ZooKeeper aburrido. No lo coloques junto a brokers pesados de Kafka si puedes evitarlo. Mantén sus discos confiables. Mantén el dimensionamiento del heap conservador y monitoreado. Limita quién puede cambiar la membresía del conjunto. La mayoría de los incidentes de ZooKeeper que he visto no fueron causados por errores exóticos; vinieron de la deriva ordinaria de la infraestructura alrededor de un pequeño servicio que todos olvidaron que era crítico.