He empezado a jugar, investigar y escribir alguna cosilla sobre Apache Kafka. Lo utilicé hace ya unos años, pero siempre me lo habían dado configurado y yo únicamente programaba clientes productores/consumidores.
Al ponerme a investigar cómo instalar Apache Kafka con Docker me he encontrado con un montón de parámetros de configuración, algunos de ellos muy parecidos y poco clara su utilidad, en concreto KAFKA_LISTENERS, KAFKA_ADVERTISED_LISTENERS y KAFKA_LISTENER_SECURITY_PROTOCOL_MAP. Detrás de ellos, unos cuantos derivados como KAFKA_INTER_BROKER_LISTENER_NAME y KAFKA_CONTROLLER_LISTENER_NAMES
Vamos a ver qué son y por qué tantos
Problema con los Listener de Kafka
Si instalamos uno o más nodos de Apache Kafka en cluster, dentro de una única red y en el que todos los clientes van a acceder también desde dentro de esa red, no hay mucho problema. Cada nodo estará accesible desde kafkaN:9092 o como queramos configurarlo, siendo kafkaN el nombre del nodo concreto en la red y 9092 el puerto que queramos poner.
El problema surge si varios de esos nodos están en redes distintas y/o los clientes acceden desde redes distintas. Es posible que el acceso a un nodo no sea igual desde dentro de una red que desde otra.
Un ejemplo claro es si montamos en cluster de nodos Kakfa en una red de contenedores docker pero algún cliente va a correr en el host anfitrión. En este caso, es normal publicar el puerto 9092 en el host anfitrión con la opción -p 9092:9092 del contenedor. Desde dentro de la red de dockers podemos acceder al nodo con kafkaN:9092, pero desde el host anfitrión accederíamos con localhost:9092.
Hasta ahora esto no sería un problema si no fuera por el siquiente comportamiento de Kafka. Cuando el cliente se conecta desde el host anfitrión con localhost:9092, lo primero que hace es preguntarle a Kafka cual es el nodo principal y Kakfa le contesta con la IP/nodo y puerto del nodo principal. En nuestro ejemplo, le contestaría con kafkaN:9092. Nuestro host anfitrión iría a ese nodo:puerto a seguir la comunicación, pero no la tiene accesible.
Veamos como resolver todo esto con los parámetros de configuración de arriba.
KAFKA_LISTENERS
En esta propiedad ponemos los puertos e interfaces de red por los que tiene que escuchar el broker de Kafka. Podemos poner tantos como nos hagan falta y en la misma propiedad ponemos un nombre de nuestro gusto a esa esccuha. Por ejemplo
KAFKA_LISTENERS: ‘LISTENER_INTERNO://:9094,LISTENER_EXTERNO://:9092’
Hemos dicho a Kafka que abra dos puertos, el 9092 y el 9094. Para identificarlos en las siguientes propiedades, les hemos puesto los nombre LISTENER_INTERNO, para la red interna de dockers y LISTENER_EXTERNO, para el host anfitrión. Si tenemos otras configuraciones de red o necesidades, podemos definir aquí los que queramos con los nombres y puertos que queramos.
En el formato podríamos poner, por ejemplo, LISTENER_INTERNO://<IP>:9094 siendo la IP la de la interface de red por la que queramos que se ponga a la escucha. Si no ponemos nada (como en el ejemplo) o ponemos 0.0.0.0, se pone a la escucha de todas las interfaces de red disponibles.
KAFKA_ADVERTISED_LISTENERS
En esta propiedad debemos decir qué IP y puerto debe comunicar Kaka a sus clientes para que puedan conectarse correctamente. Esta IP puerto puede ser distina según sea alguien interno a la red de dockers o alguien del host anfitrión.
Imagina que usando las opciones de docker, hemos publicado el puerto 9092 en el puerto 29092 del host anfitrión y que al host docker le hemos puesto de nombre kafkaN. Esa propiedad podría quedar así
KAFKA_ADVERTISED_LISTENERS: ‘LISTENER_EXTERNO://localhost:29092,LISTENER_INTERNO://kafkaN:9094’
Para los que se conecten al puerto 9092 (LISTENER_EXTERNO, es decir, host anfitrión), KAFKA les dirá que deben hacerlo a través de localhost:29092. Para los que se conecten al puerto 9094 (LISTENER_INTERNO, es decir, dentro de la red interna de docker) deben hacerlo con kakfaN:9092
KAFKA_INTER_BROKER_LISTENER_NAME
Los distintos broker de Kafka se conectan entre ellos para intercambiar información. Como hemos abierto dos puertos, uno para listener internos y otro para externos, debemo indicar aquí a Kafka cuál debe usar para interconexión entre nodos. Habitualmente es el de la red interna. Un ejemplo podría ser como lo siguiente
KAFKA_INTER_BROKER_LISTENER_NAME=LISTENER_INTERNO
KAFKA_CONTROLLER_LISTENER_NAMES
Este es sólo necesario si usamos KRaft en vez de Zookeeper. Usando KRaft, los mismos servidores Kafka tienen un protocolo entre ellos para decidir cual de ellos hace de controlador principal del cluster.
Para los controladores debemos seguir una lógica similar a los Listener. Es decir
- En KAFKA_LISTENER definir un puerto adicional para atender a los demás broker del cluster. Además de los dos que ya tenemos, añadimos algo como CONTROLLER://9093.
- En KAFA_CONTROLLER_LISTENER_NAMES debemos indicar cual de los tres listener que hemos abierto (interno, externo y controller). Es decir, KAFA_CONTROLLER_LISTENER_NAMES=CONTROLLER
Has probado Apache Pulsar?
Pues no, pero lo tengo en el tintero, no hace mucho me ha salido en conversaciones con otros grupos en el trabajo.