KafkaをDockerの外から叩けないときに確認すべき設定
Dockerで立ち上げたKafkaを、ホスト環境でビルドしたJavaアプリケーションから叩こうとしたのだが、なぜかエラーになって叩けず。
java.lang.IllegalStateException: No entry found for connection 21729123512
よく調べてみると、advertised_listenerの設定を忘れていたことに気がついた。
advertised_listenerとは
KafkaのコアであるKafka brokerはListenerを持っている。クライアントからListenerに接続があると、そのListenerは自身がアクセスできるIPアドレスまたはホスト名を返し、それを使ってその後の処理が行われる。
しかし、Kafkaとアプリケーションが同じ環境にあれば全く問題ないが、Dockerを使っているとこれが問題になりうる。ListenerはDockerのインターナルネットワークの中にあるので、返ってくるホスト名もインターナルネットワークのものになってしまうのである。
現に、エラーが起きているKafkaに、ホスト環境からkafkacatで接続してみると以下のようになった。(kafkacatは、Kafka版netcatとして開発されたCLIであり、こういった接続確認などを行うときとても便利に使える)
$ kafkacat -b localhost:9092 -L Metadata for all topics (from broker -1: localhost:9092/bootstrap): 1 brokers: broker 0 at cd572956787b:9092 29 topics: ...
broker 0 at cd572956787b:9092
に注目。このcd572956787b
とはKafkaのDockerコンテナのホスト名である。Dockerコンテナのインターナルネットワークのホスト名が返ってきてしまうために、ホスト環境ではこの名前解決ができず、その後のKafka brokerとの接続に失敗するわけだ。
具体例のついたより詳しい説明が、この記事に書かれている。
advertised_listenerを設定する
対応としては、Kafkaに外部ネットワークからアクセスできるような設定をすることが必要となる。server.properties
内のadvertised.listeners
がそれにあたる。これは外部ネットワークからアクセスする場合用に、Zookeeperに公開されるListenerの設定をするための項目になっている。
ここにadvertised.listeners=PLAINTEXT://<IP>:9092
と設定すれば良い。なおこのIPはlocalhostでも192.168始まりのプライベートIPアドレスでも動作したが、kafka-dockerのREADMEによると、localhostだと複数のbrokerを動かすときに不具合が発生するとのことなので、プライベートIPアドレスを使う方が良いかもしれない。
landoop/fast-data-devを使っている場合はもっと簡単で、READMEにあるとおり、環境変数としてADV_HOST=<IP>
を指定して起動させれば良い。
プライベートIPアドレスを指定して起動させ、再びkafkacatを叩いてみる。
$ kafkacat -b localhost:9092 -L Metadata for all topics (from broker -1: localhost:9092/bootstrap): 1 brokers: broker 0 at 192.168.58.109:9092 29 topics: ...
broker 0 at 192.168.58.109:9092
に変わっていることが確認できた。