Apache Kafka con Docker

De ChuWiki


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.