Вопрос-ответ

Connect to Kafka running in Docker

Подключение к Kafka, работающей в Docker

Я настраиваю контейнер Kafka Docker с одним узлом на моем локальном компьютере, как описано в документации Confluent (шаги 2-3).

Кроме того, я также предоставил доступ к портам 2181 Zookeeper и 9092 Kafka, чтобы я мог подключаться к ним с клиента, работающего на локальном компьютере:

$ 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

Проблема: Когда я пытаюсь подключиться к Kafka с хост-компьютера, соединение завершается с ошибкой, потому что это can't resolve address: kafka:9092.

Вот мой 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();

Исключение:

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

Вопрос: Как подключиться к Kafka, работающей в Docker? Мой код выполняется с хост-компьютера, а не с Docker.

Примечание: Я знаю, что теоретически мог бы поиграть с настройкой DNS и /etc/hosts но это обходной путь - так не должно быть.

Здесь также возникает аналогичный вопрос, однако он основан на ches/kafka изображении. Я использую confluentinc изображение на основе, которое отличается.

Переведено автоматически
Ответ 1

tl; dr - Простая пересылка порта из контейнера на хост не будет работать... Файлы Hosts (например, /etc/hosts в системах * NIX) не следует изменять для работы в сети Kafka, поскольку это решение не является переносимым.

1) К какому именно IP / имени хоста + порту вы хотите подключиться? Убедитесь, что в брокере установлено значение как advertised.listeners (не advertised.host.name и advertised.port, поскольку они устарели). Если вы видите ошибку, такую как Connection to node -1 (localhost/127.0.0.1:9092), то это означает, что ваш контейнер приложения пытается подключиться к самому себе. В вашем контейнере приложения также запущен процесс Kafka broker? Вероятно, нет.

2) Убедитесь, что серверы, перечисленные как часть bootstrap.servers, действительно разрешимы. Например, ping IP / имя хоста, используйте netcat для проверки портов... Если ваши клиенты находятся в контейнере, вам нужно сделать это из контейнера, а не (только) с вашего хоста. Используйте docker exec, если контейнер не выходит из строя немедленно, чтобы перейти к его оболочке.

3) При запуске процесса с хоста, а не из другого контейнера, для проверки правильности сопоставления портов на хосте убедитесь, что docker ps показывает, что контейнер kafka сопоставлен с 0.0.0.0:<host_port> -> <advertised_listener_port>/tcp. Порты должны совпадать, если вы пытаетесь запустить клиент извне сети Docker. Вам не нужна переадресация портов между двумя контейнерами; используйте ссылки / сети docker



В приведенном ниже ответе используются confluentinc образы docker для ответа на заданный вопрос, а не wurstmeister/kafka. Если у вас установлена KAFKA_ADVERTISED_HOST_NAME переменная, удалите ее (это устаревшее свойство)


В следующих разделах предпринята попытка объединить все детали, необходимые для использования другого образа. Для других, часто используемых образов Kafka, это все тот же Apache Kafka, работающий в контейнере.
Вы просто зависите от того, как это настроено. И какие переменные делают это таким.


wurstmeister/kafka


По состоянию на октябрь 2023 года этого больше не существует в DockerHub. В любом случае, после 2022 года это не поддерживалось.


Обратитесь к их разделу README о конфигурации прослушивателя, также прочитайте их вики о подключении.



bitnami/kafka


Если вам нужен контейнер небольшого размера, попробуйте эти. Изображения намного меньше, чем Confluent, и находятся в гораздо более хорошем состоянии, чем wurstmeister. Обратитесь к их README для настройки прослушивателя.



debezium/kafka


Документы по нему упомянуты здесь.


Примечание: настройки объявленного хоста и порта устарели. Объявленные прослушиватели охватывают оба. Подобно контейнерам Confluent, Debezium может использовать KAFKA_ префиксные настройки брокера для обновления своих свойств.



Прочее



  • ubuntu/kafka требуется добавить --override advertised.listeners=kafka:9092 аргументы с помощью образа Docker... Я нахожу это менее переносимым, чем переменные окружения, поэтому не рекомендуется

  • spotify/kafka is deprecated and outdated.

  • fast-data-dev or lensesio/box are great for an all in one solution, with Schema Registry, Kafka Connect, etc, but are bloated if you only want Kafka. Plus, it's a Docker anti pattern to run many services in one container

  • Your own Dockerfile - Why? Is something incomplete with these others? Start with a pull request, not starting from scratch.


For supplemental reading, a fully-functional docker-compose, and network diagrams, see this blog by @rmoff

Answer

The Confluent quickstart (Docker) document assumes all produce and consume requests will be within the Docker network.

You could fix the problem of connecting to kafka:9092 by running your Kafka client code within its own container as that uses the Docker network bridge, but otherwise you'll need to add some more environment variables for exposing the container externally, while still having it work within the Docker network.

First add a protocol mapping of PLAINTEXT_HOST:PLAINTEXT that will map the listener protocol to a Kafka protocol

Клавиша: KAFKA_LISTENER_SECURITY_PROTOCOL_MAP
Значение: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT

Затем настройте два объявленных прослушивателя на разных портах. (kafka здесь указано имя контейнера docker; оно также может иметь имя broker, поэтому дважды проверьте свои службы + имена хостов).

Клавиша: KAFKA_ADVERTISED_LISTENERS
Значение: PLAINTEXT://kafka:9092,PLAINTEXT_HOST://localhost:29092

Обратите внимание, что протоколы здесь соответствуют левым значениям приведенного выше параметра сопоставления протоколов

При запуске контейнера добавьте -p 29092:29092 для сопоставления портов хоста и объявленного PLAINTEXT_HOST прослушивателя.


Итак ... (с указанными выше настройками)


Если что-то по-прежнему не работает, KAFKA_LISTENERS можно установить значение include <PROTOCOL>://0.0.0.0:<PORT>, где оба параметра соответствуют объявленным настройкам и порту, перенаправленному Docker


Клиент на том же компьютере, а не в контейнере

Реклама localhost и связанного с ним порта позволит вам подключаться за пределами контейнера, как и следовало ожидать.

Другими словами, при запуске любого клиента Kafka вне сети Docker (включая инструменты командной строки, которые вы могли установить локально) используйте localhost:29092 для серверов начальной загрузки и localhost:2181 для Zookeeper (требуется перенаправление портов Docker)

Клиент на другой машине

При попытке подключения с внешнего сервера вам нужно будет указать внешнее имя хоста / ip (например, 192.168.x.y) хоста , а также/ вместо localhost.
Простое объявление localhost с перенаправлением на порт не сработает, потому что протокол Kafka все равно продолжит рекламировать настроенных вами прослушивателей.

Для этой настройки требуется переадресация портов Docker и маршрутизатора (и изменения брандмауэра / группы безопасности), если они находятся не в той же локальной сети, например, ваш контейнер запущен в облаке, и вы хотите взаимодействовать с ним со своего локального компьютера.

Клиент (или другой брокер) в контейнере, на том же хостинге

Это наименее подверженная ошибкам конфигурация; вы можете использовать имена служб DNS напрямую.

При запуске приложения в сети Docker используйте kafka:9092 (см. Рекламируемую PLAINTEXT конфигурацию прослушивателя выше) для серверов начальной загрузки и zookeeper:2181 для Zookeeper, как и для любого другого взаимодействия со службой Docker (не требует переадресации портов)


Если вы используете отдельные docker run команды или создаете файлы, вам необходимо определить общий доступ network вручную с помощью раздела compose networks или docker network --create


Смотрите пример создания файла для полного стека Confluent или более минимального для одного брокера.

Если используется несколько брокеров, то им необходимо использовать уникальные имена хостов + объявленные прослушиватели. Посмотреть пример

Связанный вопрос

Подключение к Kafka на хосте из Docker (ksqlDB)

Приложение

Для всех, кто интересуется развертываниями Kubernetes:

Ответ 2

При первом подключении к узлу kafka он вернет вам все узлы kafka и URL, по которым нужно подключиться. Затем ваше приложение попытается подключиться к каждому kafka напрямую.

Проблема всегда в том, что kafka предоставит вам в качестве URL? Вот почему существует KAFKA_ADVERTISED_LISTENERS который будет использоваться kafka, чтобы сообщить миру, как к нему можно получить доступ.

Теперь для вашего варианта использования есть о чем подумать:

Допустим, вы установили plaintext://kafka:9092


  • Это нормально, если в вашем docker compose есть приложение, использующее kafka. Это приложение получит от kafka URL с kafka который можно разрешить через сеть docker.

  • Если вы попытаетесь подключиться из вашей основной системы или из другого контейнера, который не находится в той же сети docker, это завершится неудачей, поскольку kafka имя не может быть разрешено.

==> Чтобы исправить это, вам нужно иметь определенный DNS-сервер, например, для обнаружения служб, но это большая проблема для мелочей. Или вы устанавливаете вручную kafka имя IP-адреса контейнера в каждом /etc/hosts

Если вы установите plaintext://localhost:9092


  • Это будет нормально в вашей системе, если у вас есть сопоставление портов ( -p 9092: 9092 при запуске kafka)

  • Это приведет к сбою, если вы выполните тестирование из приложения в контейнере (в той же сети docker или нет) (localhost - это сам контейнер, а не kafka)

==> Если у вас есть это и вы хотите использовать клиент kafka в другом контейнере, один из способов исправить это - предоставить общий доступ к сети для обоих контейнеров (один и тот же ip)

Последний вариант: задайте IP-адрес в имени: plaintext://x.y.z.a:9092 ( рекламируемый URL-адрес kafka не может быть 0.0.0.0, как указано в документе https://kafka.apache.org/documentation/#brokerconfigs_advertised.listeners )

Это подойдет всем... НО как вы можете получить имя x.y.z.a ?

Единственный способ - жестко закодировать этот ip-адрес при запуске контейнера: docker run .... --net confluent --ip 10.x.y.z .... Обратите внимание, что вам необходимо адаптировать ip-адрес к одному допустимому ip-адресу в confluent подсети.

Ответ 3

перед zookeeper


  1. запуск контейнера docker --имя zookeeper -страница 2181:2181 zookeeper

после kafka


  1. запуск контейнера docker --name kafka -p 9092:9092 -e KAFKA_ZOOKEEPER_CONNECT=192.168.8.128:2181 -e KAFKA_ADVERTISED_LISTENERS=ОТКРЫТЫЙ ТЕКСТ://ip_address_of_your_computer_but_not_localhost!!!:9092 -e KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1 confluentinc/cp- кафка

в конфигурации потребителя и производителя 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);
}

Я запускаю свой проект в соответствии с этими правилами. Удачи, чувак.

Ответ 4

самый простой способ решить эту проблему - добавить пользовательское имя хоста вашему брокеру, используя опцию -h

docker run -d \
--net=confluent \
--name=kafka \
-h broker-1 \
-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

и отредактируйте свой файл /etc/hosts

127.0.0.1   broker-1

и используйте:

props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "broker-1:9092");
java