Apache Kafka con Docker
Opciones para Apache Kafka en Docker[editar]
Apache Kafka con Zookeeper o con KRaft[editar]
Tradicionalmente Apache Kafka necesitaba una instalación de Zookeeper. En las nuevas versiones, se reemplaza esa instalación de Zookeeper por KRaft, que hace las mismas funciones, pero embebido dentro del servidor de Apache Kafka. Evitamos así la instalación separada de Zookeeper.
En este ejemplo instalaremos Apache Kafka con KRaft, evitando así instalar un segundo docker con Zookeeper. No obstante, si quieres hacerlo con Zookeeper, tienes aquí un fichero docker-compose-kafka-zookeeper.yml docker-compose-kafka-zookeeper.yml que te instalar y arranca ambos docker: Zookeeper y Apache Kafka. Podrías instalarlo siguiendo exactamente los mismos pasos de este tutorial, pero usando este fichero en vez de el que se menciona en el tutorial.
Imagen Docker oficial de Apache Kafka[editar]
No hay una imagen oficial de Apache Kafka en Docker, así que usaremos los de confluent inc. Esta empresa ofrece servicios completos en la nube basándose en Apache Kafka.
Docker Compose para Apache Kafka[editar]
Puesto que el Docker de Apache Kafka requiere unos cuantos parámetros de configuración, en vez de escribir la línea de comandos docker run .... usaremos un fichero de docker compose. El fichero lo tienes en docker-compose-kafka-kraft.yml. Su contenido es el siguiente
version: '3'
services:
kafka1:
image: confluentinc/cp-kafka
container_name: kafka-kraft
hostname: kafka-kraft
ports:
- "9092:9092"
environment:
KAFKA_NODE_ID: 1
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: 'CONTROLLER:PLAINTEXT,LISTENER:PLAINTEXT'
KAFKA_LISTENERS: 'LISTENER://:9092,CONTROLLER://:9093'
KAFKA_CONTROLLER_LISTENER_NAMES: 'CONTROLLER'
KAFKA_INTER_BROKER_LISTENER_NAME: 'LISTENER'
KAFKA_ADVERTISED_LISTENERS: 'LISTENER://localhost:9092'
KAFKA_CONTROLLER_QUORUM_VOTERS: '1@localhost:9093'
KAFKA_PROCESS_ROLES: 'broker,controller'
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
CLUSTER_ID: 'ciWo7IWazngRchmPES6q5A=='
Vamos al grano y vemos como arrancarlo. Si tienes interés en los detalles, los vemos más adelante.
Arrancar la imagen de Apache Kafka[editar]
En el directorio donde tengamos este fichero y acceso a los comandos docker y docker-compose, ejecutamos el siguiente comando desde una ventana de comandos de linux/windows
docker-compose -f docker-compose-kafka-kraft.yml up --remove-orphans
Básicamente es ejecutar el comando docker-compose, con la opción -f indicar el fichero y con el comando up indicar que queramos levantarlo. Si es la primera vez que arrancamos esta imagen, la opción --remove-orphans no es necesaria. Si estamos haciendo pruebas y arrancamos varias veces, es encesario eliminar los contendores creados en las pruebas anteriores. La opción --remove-orphans lo hace por nosotros.
Si todo va bien, deberíamos ver el arranque de Apache Kafka y el log del mismo, similar a lo siguiente
Creating network "docker-kafka_default" with the default driver Pulling kafka1 (confluentinc/cp-kafka:)... latest: Pulling from confluentinc/cp-kafka 4c006e7aa3ac: Pull complete 933449361692: Pull complete 770dec15b572: Pull complete f4eecfadff6a: Pull complete 043cc968ae8d: Pull complete ff417226328f: Pull complete c52f109b8777: Pull complete cba62982e7d4: Pull complete 0674d6202fb9: Pull complete c9769d7f6fe8: Pull complete 10fbb7def661: Pull complete Digest: sha256:51145a40d23336a11085ca695d02bdeee66fe01b582837c6d223384952226be9 Status: Downloaded newer image for confluentinc/cp-kafka:latest Creating kafka-kraft ... done Attaching to kafka-kraft kafka-kraft | ===> User kafka-kraft | uid=1000(appuser) gid=1000(appuser) groups=1000(appuser) kafka-kraft | ===> Configuring ... kafka-kraft | Running in KRaft mode... kafka-kraft | ===> Running preflight checks ... kafka-kraft | ===> Check if /var/lib/kafka/data is writable ... kafka-kraft | ===> Running in KRaft mode, skipping Zookeeper health check... kafka-kraft | ===> Using provided cluster id ciWo7IWazngRchmPES6q5A== ... kafka-kraft | ===> Launching ... kafka-kraft | ===> Launching kafka ... kafka-kraft | [2024-01-27 07:52:51,462] INFO Registered kafka:type=kafka.Log4jController MBean (kafka.utils.Log4jControllerRegistration$) kafka-kraft | [2024-01-27 07:52:51,733] INFO Setting -D jdk.tls.rejectClientInitiatedRenegotiation=true to disable client-initiated TLS renegotiation (org.apache.zookeeper.common.X509Util) ...
Podemos pararlo con <Ctrl-C>. Para los siguientes arranques/paradas, nos bastarán los comandos Docker normales
docker start kafka-kraft docker stop kafka-kraft
Configuración de Apache Kafka[editar]
Veamos ahora una breve explicación de lo que hemos puesto en el fichero docker-compose-kafka-kraft.yml. Hay muchos más parámetros posibles y configuraciones más complejas. Aquí nos hemos limitado a una lo más sencilla posible para arrancar un único servidor de Apache Kafka para poder usar en nuestras pruebas.
En este trozo del fichero
image: confluentinc/cp-kafka container_name: kafka-kraft hostname: kafka-kraft ports: - "9092:9092"
Básicamente indicamos
- De qué imagen partir confluentinc/cp-kafka.
- El nombre que queremos dar al contenedor cuando se cree container_name: kafka-kraft.
- El nombre que queremos que tenga el servidor de Kafka hostname: kafka-kraft dentro de la red para no tener que buscarlo por IP.
- Queremos que se exponga el puerto 9092 para hacerlo accesible desde el host anfitrión.
En cuanto a los parámetros de entorno del servidor de Apache Kafka
KAFKA_NODE_ID: 1
Es un número que identifica al nodo de Kafka dentro del Cluster. Como solo tenemos un nodo, pues 1. Si quisieramos levantar un Cluster con varios nodos, cada uno debería llevar un número distinto.
KAFKA_LISTENERS: 'LISTENER://:9092,CONTROLLER://:9093'
Aquí indicamos qué puertos tiene que abrir Apache Kafa y le damos un nombre a cada uno de esos puertos. LISTENER será el 9092 y CONTROLLER el 9093. En // lo hemos dejado vacío para que atienda a ese puerto sea como sea que se acceda a él. Podriamos poner cosas como LISTENER:/localhost/:9092, LISTENER:/kafka-kraft/:9092 si queremos que solo atienda a determinadas interfaces de red.
KAFKA_ADVERTISED_LISTENERS: 'LISTENER://localhost:9092'
Cuando un cliente externo se conecta a Apache Kafka, lo que pongamos aquí se le pasará para que sepa a dónde dirigirse. Como en el punto anterior pusimos que LISTENER es el puerto 9092, si un cliente se conecta al puerto 9092, se le indicará que las peticiones debe hacerlas a localhost:9092. En este ejemplo parece una tontería, pero si tenemos un Cluster y una distribución en varios dockers/nube, desde aquí podemos decirle al cliente que debe conectarse a tal máquina o usar otro puerto.
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: 'CONTROLLER:PLAINTEXT,LISTENER:PLAINTEXT'
Aquí definimos qué tipo de seguridad queremos para los dos puertos CONTROLLER y LISTENER. Detrás de cada uno de ellos, el tipo de seguridad que queremos. PLAINTEXT indica que no usaremos ningún tipo de seguridad.
KAFKA_INTER_BROKER_LISTENER_NAME: 'LISTENER'
Los distintos servidores Apache Kafka hablan entre ellos para coordinarse. Aquí les indicamos qué puerto deben usar para ello.
KAFKA_CONTROLLER_LISTENER_NAMES: 'CONTROLLER'
Cuando hay varios nodos Apache Kafka en un Cluster, sólo uno de ellos hace de controlador de todos los demás. Si este se cae, el resto eligen a otro para que sea controlador. Aquí indicamos qué puerto deben usar para este protocolo propio de ellos para elegir controlador.
KAFKA_CONTROLLER_QUORUM_VOTERS: '1@localhost:9093
En la elección de controlador, aquí indicamos quienes tiene derecho a voto. Como en este ejemplo solo tenemos uno, lo ponemos. El formato en número de nodo @ nombre de servidor : puerto.
KAFKA_PROCESS_ROLES: 'broker,controller'
En este se indica si el nodo es para manejar mensajes de clientes (broker) y/o para hacer de controlador del cluster (controller). En este caso y para pruebas, como sólo tenemos un servidor Apache Kakfa levantado, hará los dos papeles. En un Cluster en producción con varios nodos podemos tener nodos que hagan solo uno de los papeles.
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
Cuantas réplicas queremos, como solo tenemos un serivdor, debemos poner 1. Si tuvieramos un Cluster con más, podemos no poner esta opcion y dejar el valor por defecto.
CLUSTER_ID: 'ciWo7IWazngRchmPES6q5A=='
En un UUID aleatorio que puede ir codificado base 64 (el == del final) o no. La forma oficial de generarlo, teniendo nuetro docker kafka arrancado, es
$ docker exec -it kafka-kraft /usr/bin/kafka-storage random-uuid
que puede producir una salida como
MW8gxNWhSfu9lmaTwXtZgw
En cualquier caso, basta poner cualquier combinación de 22 caracteres, añadiendo detrás o no == (serían 24 si los añadimos). Lo importante es que esta cadena identifica al cluster y debemos asegurarnos que todos los nodos del cluster tienen la misma cadena.