Kafka en Docker

Para que desde el exterior de una red puedan conectarse a dicha red hay que definir advertised.listeners. Si usamos docker, KAFKA_ADVERTISED_LISTENERS.

Cuándo un cliente se conecta a un clúster lo primero que hace es pedir los metadatos para saber qué bróker es el líder del clúster. Estos metadatos contienen la dirección de los endpoints disponibles para conectarse al líder.

  • LISTENERS interfaces a las que se conecta Kafka
  • ADVERTISED_LISTENERS cómo los clientes se nos conectan

Resumen

En la web de Confluent está la guía que he usado para extraer la información de este documento. Allí hay una explicación mucho más extendida. Se trata incluso cómo conectar desde máquinas externas, no solo dentro de la misma máquina.

Vamos a usar compose, en concreto las imágenes de debezium de quay. Es importante saber la distribución porque la forma de nombrar las variables cambia entre ellas.

Usamos docker-compose.yml. Ahí definimos dos parámetros fundamentales:

  • KAFKA_LISTENERS : las interfaces a las que se conecta el cliente en sí (por ejemplo los otros brokers del clúster a los que se puede conectar).
  • KAFKA_ADVERTISED_LISTENERS : las direcciones a las que se pueden conectar los clientes. Hay que configurarlo con la dirección exterior a la red (host/IP) interna de docker para que los clientes puedan conectarse correctamente.

El primero corresponde con listeners en la configuración de Kafka, el segundo con advertised.listeners.

Figure 1: Kafka en Docker con clientes dentro y fuera de la red privada

Figure 1: Kafka en Docker con clientes dentro y fuera de la red privada

Cómo conectarse

Como hemos dicho, hay que decirle a Kafka:

  • como se pueden encontrar y conectar los brokers que hay dentro de nuestra red; y
  • como los clientes externos a la red privada pueden conectarse con los brokers.

Cuando se le pasa la ruta inicial de uno de los broker del clúster a un cliente, este se conecta con dicho broker el cual en respuesta le pasará unos metadatos donde van incluidas las direcciones especificadas en advertised_listeners, que serán las que usen finalmente los clientes para poder conectarse.

Dicho todo lo anterior, es fácil entender que en advertised tendremos que referirnos a puertos accesibles desde el exterior, por lo que la ruta tendrá que ser localhost, y no el nombre/identificador del servicio dentro de la red.

En resumen, tenemos dos situaciones:

Conectamos dentro de la misma red donde se encuentra el clúster

En este caso basta con usar el nombre del servicio donde corre el broker como host al que conectarse desde dentro de la red. Típicamente kafka0, kafka1, etc.

Nos vamos a conectar desde fuera de la red privada

Lo habitual será conectarnos a localhost, a un puerto expuesto fuera de la red privada. Los metadatos devolverán como comentamos los datos de advertised_listener. Como nos queremos conectar desde el exterior necestiamos que todos los broker del clUster tengan un puerto expuesto, que no puede ser el mismo para evitar conflictos, para poder conectarnos a dicho puerto.

Ejemplo

Si tenemos por ejemplo 3 brokers, definiremos tal queremos

kafka1:
    hostname: kafka1
    ports:
      - 9095:9095
    environment:
      - KAFKA_LISTENERS=INTERNAL://kafka1:9092,EXTERNAL://0.0.0.0:9095,CONTROLLER://kafka1:29092
      - KAFKA_ADVERTISED_LISTENERS=INTERNAL://kafka1:9092,EXTERNAL://localhost:9095
      - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:INTERNAL,INTERNAL:INTERNAL,EXTERNAL:INTERNAL

  kafka2:
    hostname: kafka2
    ports:
      - 9096:9096
    environment:
      - KAFKA_LISTENERS=INTERNAL://kafka2:9092,EXTERNAL://0.0.0.0:9096,CONTROLLER://kafka2:29092
      - KAFKA_ADVERTISED_LISTENERS=INTERNAL://kafka2:9092,EXTERNAL://localhost:9096
      - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:INTERNAL,INTERNAL:INTERNAL,EXTERNAL:INTERNAL

  kafka3:
    hostname: kafka3
    ports:
      - 9097:9097
    environment:
      - KAFKA_LISTENERS=INTERNAL://kafka3:9092,EXTERNAL://0.0.0.0:9097,CONTROLLER://kafka3:29092
      - KAFKA_ADVERTISED_LISTENERS=INTERNAL://kafka3:9092,EXTERNAL://localhost:9097
      - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:INTERNAL,INTERNAL:INTERNAL,EXTERNAL:INTERNAL

Si por ejemplo queremos conectarnos con kafka-ui, y éste se encuentra en la misma red, tendremos que especificar, a la hora de configurar el clúster, host: kafka1, port: 9092. Si nos queremos conectar a este mismo clúster con conduktor, en cambio, tendremos que especificar, para conectarnos al primer broker, host: localhost, port: 9095.

Referencias

Para más información genérica sobre redes en docker, ver Redes en Docker.