Conéctese a Kafka ejecutándose en Docker

Resuelto Sasha Shpota asked hace 6 años • 6 respuestas

Configuré un contenedor Kafka Docker de un solo nodo en mi máquina local como se describe en la documentación de Confluent (pasos 2 y 3).

Además, también expuse el puerto 2181 de Zookeeper y el puerto 9092 de Kafka para poder conectarme a ellos desde un cliente que se ejecuta en la máquina local:

$ docker run -d \
    -p 2181:2181 \
    --net=confluent \
    --name=zookeeper \
    -e ZOOKEEPER_CLIENT_PORT=2181 \
    confluentinc/cp-zookeeper:4.1.0

$ docker run -d \
    --net=confluent \
    --name=kafka \
    -p 9092:9092 \
    -e KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181 \
    -e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092 \
    -e KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1 \
    confluentinc/cp-kafka:4.1.0

Problema: cuando intento conectarme a Kafka desde la máquina host, la conexión falla porque can't resolve address: kafka:9092.

Aquí está mi código Java:

Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("client.id", "KafkaExampleProducer");
props.put("key.serializer", LongSerializer.class.getName());
props.put("value.serializer", StringSerializer.class.getName());
KafkaProducer<Long, String> producer = new KafkaProducer<>(props);
ProducerRecord<Long, String> record = new ProducerRecord<>("foo", 1L, "Test 1");
producer.send(record).get();
producer.flush();

La excepción:

java.io.IOException: Can't resolve address: kafka:9092
    at org.apache.kafka.common.network.Selector.doConnect(Selector.java:235) ~[kafka-clients-2.0.0.jar:na]
    at org.apache.kafka.common.network.Selector.connect(Selector.java:214) ~[kafka-clients-2.0.0.jar:na]
    at org.apache.kafka.clients.NetworkClient.initiateConnect(NetworkClient.java:864) [kafka-clients-2.0.0.jar:na]
    at org.apache.kafka.clients.NetworkClient.ready(NetworkClient.java:265) [kafka-clients-2.0.0.jar:na]
    at org.apache.kafka.clients.producer.internals.Sender.sendProducerData(Sender.java:266) [kafka-clients-2.0.0.jar:na]
    at org.apache.kafka.clients.producer.internals.Sender.run(Sender.java:238) [kafka-clients-2.0.0.jar:na]
    at org.apache.kafka.clients.producer.internals.Sender.run(Sender.java:176) [kafka-clients-2.0.0.jar:na]
    at java.lang.Thread.run(Thread.java:748) [na:1.8.0_144]
Caused by: java.nio.channels.UnresolvedAddressException: null
    at sun.nio.ch.Net.checkAddress(Net.java:101) ~[na:1.8.0_144]
    at sun.nio.ch.SocketChannelImpl.connect(SocketChannelImpl.java:622) ~[na:1.8.0_144]
    at org.apache.kafka.common.network.Selector.doConnect(Selector.java:233) ~[kafka-clients-2.0.0.jar:na]
    ... 7 common frames omitted

Pregunta: ¿Cómo conectarse a Kafka ejecutándose en Docker? Mi código se ejecuta desde la máquina host, no desde Docker.

Nota: Sé que, en teoría, podría jugar con la configuración de DNS, /etc/hostspero es una solución alternativa; no debería ser así.

También hay una pregunta similar aquí , sin embargo, se basa en ches/kafkala imagen. Utilizo confluentincuna imagen basada que no es la misma.

Sasha Shpota avatar Aug 01 '18 16:08 Sasha Shpota
Aceptado

tl;dr : un simple reenvío de puerto desde el contenedor al host no funcionará ... Los archivos de hosts (por ejemplo, /etc/hostsen sistemas *NIX) no deben modificarse para funcionar con la red Kafka, ya que esta solución no es portátil.

1) ¿A qué IP/nombre de host y puerto exacto desea conectarse? Asegúrese de que el valor esté establecido como advertised.listeners(no advertised.host.namey advertised.port, ya que están en desuso) en el intermediario. Si ve un error como Connection to node -1 (localhost/127.0.0.1:9092), significa que el contenedor de su aplicación intenta conectarse consigo mismo. ¿El contenedor de su aplicación también ejecuta un proceso de corredor de Kafka? Probablemente no.

2) Asegúrese de que los servidores enumerados como parte de bootstrap.serversrealmente se puedan resolver. Por ejemplo, pinguna IP/nombre de host, úselo netcatpara verificar los puertos... Si sus clientes están en un contenedor, debe hacerlo desde el contenedor , no (solo) desde su host. Úselo docker execsi el contenedor no se bloquea inmediatamente para llegar a su caparazón.

3) Si ejecuta un proceso desde el host, en lugar de otro contenedor, para verificar que los puertos estén asignados correctamente en el host, asegúrese de que docker psmuestre que el contenedor Kafka está asignado desde 0.0.0.0:<host_port> -> <advertised_listener_port>/tcp. Los puertos deben coincidir si se intenta ejecutar un cliente desde fuera de la red Docker. No necesita reenvío de puertos entre dos contenedores; utilizar enlaces/redes acoplables


La siguiente respuesta utiliza confluentincimágenes de la ventana acoplable para abordar la pregunta que se hizo, no wurstmeister/kafka . Si tiene KAFKA_ADVERTISED_HOST_NAMEuna variable configurada, elimínela (es una propiedad obsoleta)

Las siguientes secciones intentan agregar todos los detalles necesarios para utilizar otra imagen. Para otras imágenes de Kafka de uso común, es el mismo Apache Kafka ejecutándose en un contenedor.
Solo dependes de cómo esté configurado . Y qué variables lo hacen así.

wurstmeister/kafka

A partir de octubre de 2023, esto ya no existe en DockerHub. De todos modos, no se mantuvo más allá de 2022.

Consulte su sección README sobre la configuración del oyente . Lea también su wiki de conectividad .

bitnami/kafka

Si quieres un recipiente pequeño, prueba estos. Las imágenes son mucho más pequeñas que las de Confluent y están mucho mejor mantenidas que wurstmeister. Consulte su archivo README para conocer la configuración del oyente.

debezium/kafka

Los documentos al respecto se mencionan aquí .

Nota : las configuraciones de puerto y host anunciadas están en desuso. Los oyentes anunciados cubren ambos. Al igual que los contenedores de Confluent, Debezium puede utilizar KAFKA_configuraciones de intermediario con prefijos para actualizar sus propiedades.

Otros

  • ubuntu/kafkarequiere que agregue --override advertised.listeners=kafka:9092a través de argumentos de imagen de Docker... Creo que es menos portátil que las variables de entorno, por lo que no se recomienda
  • spotify/kafkaestá en desuso y desactualizado.
  • fast-data-devo lensesio/boxson excelentes para una solución todo en uno, con Schema Registry, Kafka Connect, etc., pero están inflados si solo quieres Kafka. Además, es un antipatrón de Docker para ejecutar muchos servicios en un contenedor.
  • El tuyo Dockerfile- ¿Por qué? ¿Hay algo incompleto con estos otros? Comience con una solicitud de extracción, no desde cero.

Para lecturas complementarias, diagramas de red y completamente funcionales , consulte este blog de @rmoffdocker-compose

Respuesta

El documento de inicio rápido de Confluent (Docker) supone que todas las solicitudes de producción y consumo estarán dentro de la red Docker.

Puede solucionar el problema de conectarse kafka:9092ejecutando el código de su cliente Kafka dentro de su propio contenedor, ya que utiliza el puente de red Docker, pero de lo contrario necesitará agregar algunas variables de entorno más para exponer el contenedor externamente, sin dejar de funcionar dentro. la red Docker.

Primero agregue un mapeo de protocolo PLAINTEXT_HOST:PLAINTEXTque mapeará el protocolo de escucha a un protocolo Kafka.

KAFKA_LISTENER_SECURITY_PROTOCOL_MAP
Valor clave :PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT

Luego configure dos oyentes anunciados en diferentes puertos. ( kafkaaquí se refiere al nombre del contenedor de la ventana acoplable; también podría tener un nombre broker, así que verifique su servicio + nombres de host).

KAFKA_ADVERTISED_LISTENERS
Valor clave :PLAINTEXT://kafka:9092,PLAINTEXT_HOST://localhost:29092

Observe que los protocolos aquí coinciden con los valores del lado izquierdo de la configuración de mapeo de protocolos anterior

Al ejecutar el contenedor, agregue -p 29092:29092para la asignación del puerto del host y PLAINTEXT_HOSTel oyente anunciado.


Entonces... ( con la configuración anterior )

Si algo aún no funciona, KAFKA_LISTENERSse puede configurar para incluir <PROTOCOL>://0.0.0.0:<PORT>donde ambas opciones coincidan con la configuración anunciada y el puerto reenviado por Docker.

Cliente en la misma máquina, no en un contenedor

La publicidad de localhost y el puerto asociado le permitirán conectarse fuera del contenedor, como era de esperar.

En otras palabras, cuando ejecute cualquier Cliente Kafka fuera de la red Docker (incluidas las herramientas CLI que haya instalado localmente), utilícelo localhost:29092para servidores de arranque y localhost:2181para Zookeeper (requiere reenvío de puertos Docker).

Cliente en otra máquina

Si intenta conectarse desde un servidor externo, deberá anunciar el nombre/ip del host externo (p. ej. 192.168.x.y) del host , así como/en lugar de localhost .
Simplemente anunciar localhost con un reenvío de puerto no funcionará porque el protocolo Kafka seguirá anunciando los oyentes que haya configurado.

Esta configuración requiere el reenvío de puertos Docker y el reenvío de puertos del enrutador (y cambios en el firewall/grupo de seguridad) si no están en la misma red local; por ejemplo, su contenedor se ejecuta en la nube y desea interactuar con él desde su máquina local.

Cliente (u otro corredor) en un contenedor, en el mismo host

Esta es la configuración menos propensa a errores; puede utilizar los nombres de los servicios DNS directamente.

Cuando ejecute una aplicación en la red Docker , utilice kafka:9092(consulte PLAINTEXTla configuración de escucha anunciada arriba) para servidores de arranque y zookeeper:2181para Zookeeper, como cualquier otra comunicación de servicio Docker (no requiere ningún reenvío de puerto).


Si usa docker runcomandos separados o compone archivos, debe definir un archivo compartido networkmanualmente usando networksla sección de redacción odocker network --create


Consulte el archivo de redacción de ejemplo para ver la pila completa de Confluent o uno más mínimo para un solo corredor.

Si utiliza varios corredores, entonces deben usar nombres de host únicos + oyentes anunciados. ver ejemplo

Pregunta relacionada

Conéctese a Kafka en el host desde Docker (ksqlDB)

Apéndice

Para cualquiera interesado en implementaciones de Kubernetes :

  • Accediendo a Kafka
  • Operadores (recomendado): https://operatorhub.io/?keyword=Kafka
  • Centro de artefactos de Helm: https://artifacthub.io/packages/search?ts_query_web=kafka&sort=stars&page=1
OneCricketeer avatar Aug 01 '2018 13:08 OneCricketeer

Cuando se conecta por primera vez a un nodo Kafka, le devolverá todos los nodos Kafka y la URL donde conectarse. Luego, su aplicación intentará conectarse directamente a cada Kafka.

El problema siempre es ¿qué es lo que Kafka te dará como URL? Es por eso que existe el KAFKA_ADVERTISED_LISTENERScuál Kafka utilizará para decirle al mundo cómo se puede acceder a él.

Ahora, para su caso de uso, hay varias cosas pequeñas en las que pensar:

Digamos que configurasteplaintext://kafka:9092

  • Esto está bien si tiene una aplicación en su ventana acoplable que usa kafka. Esta aplicación obtendrá de Kafka la URL kafkaque se puede resolver a través de la red Docker.
  • Si intenta conectarse desde su sistema principal o desde otro contenedor que no esté en la misma red acoplable, esto fallará, ya que el kafkanombre no se puede resolver.

==> Para solucionar este problema, necesita tener un servidor DNS específico, como uno de descubrimiento de servicios, pero es un gran problema para cosas pequeñas. O configura manualmente el kafkanombre de la IP del contenedor en cada/etc/hosts

si establecesplaintext://localhost:9092

  • Esto estará bien en su sistema si tiene una asignación de puertos (-p 9092:9092 al iniciar kafka)
  • Esto fallará si prueba desde una aplicación en un contenedor (la misma red acoplable o no) (localhost es el contenedor en sí, no el de Kafka)

==> Si tiene esto y desea utilizar un cliente Kafka en otro contenedor, una forma de solucionarlo es compartir la red para ambos contenedores (misma IP)

Última opción: establezca una IP en el nombre: plaintext://x.y.z.a:9092(la URL anunciada de Kafka no puede ser 0.0.0.0 como se indica en el documento https://kafka.apache.org/documentation/#brokerconfigs_advertised.listeners )

Esto estará bien para todos... PERO ¿cómo puedes obtener el nombre xyza?

La única forma es codificar esta IP cuando inicia el contenedor: docker run .... --net confluent --ip 10.x.y.z .... Tenga en cuenta que debe adaptar la IP a una IP válida en la confluentsubred.

wargre avatar Aug 01 '2018 12:08 wargre

antes del cuidador del zoológico

  1. ejecución del contenedor acoplable --name zookeeper -p 2181:2181 zookeeper

después de kafka

  1. ejecución del contenedor acoplable --name kafka -p 9092:9092 -e KAFKA_ZOOKEEPER_CONNECT=192.168.8.128:2181 -e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://ip_address_of_your_computer_but_not_localhost!!!:9092 -e KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1 confluentin c/cp-kafka

en la configuración de consumidor y productor de Kafka

@Bean
public ProducerFactory<String, String> producerFactory() {
    Map<String, Object> configProps = new HashMap<>();
    configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.8.128:9092");
    configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
    configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
    return new DefaultKafkaProducerFactory<>(configProps);
}

@Bean
public ConsumerFactory<String, String> consumerFactory() {
    Map<String, Object> props = new HashMap<>();
    props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.8.128:9092");
    props.put(ConsumerConfig.GROUP_ID_CONFIG, "group_id");
    props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
    props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
    return new DefaultKafkaConsumerFactory<>(props);
}

Dirijo mi proyecto con estas regulaciones. Buena suerte amigo.

İbrahim Ersin Yavaş avatar May 14 '2020 11:05 İbrahim Ersin Yavaş