KRaft モードの Kafka をコンテナ環境にデプロイする
はじめに
#今年の初めに「KRaft による ZooKeeper レス Kafka クラスター構成」という記事でローカル環境で KRaft モードで Kafka を実行する方法について書きました。
この時は Docker や Kubernetes などコンテナ環境でのサポートが追いついておらず、設定にはかなりの手作業が必要でした。現在では手軽に構築できるようになっています。
macOS Sonoma / OrbStack 1.6.2 の環境で試しました。
OrbStack については以下の記事で取り上げています。
Docker の場合
#Bitnami の Docker サイトにドキュメントがあります。
https://hub.docker.com/r/bitnami/kafka
KRaft コントローラーと Kafka ブローカーを兼用するシングルノードのミニマム構成を docker compose で起動させる例です。kafka-kraft というディレクトリ配下に compose.yml を作成しました。
services:
kafka:
image: 'bitnami/kafka:latest'
environment:
- KAFKA_CFG_NODE_ID=0
- KAFKA_CFG_CONTROLLER_BROKER_ID=0 #1
- KAFKA_CFG_PROCESS_ROLES=controller, broker #2
- KAFKA_CFG_LISTENERS=PLAINTEXT://:9092, CONTROLLER://:9093 #3
- KAFKA_CFG_LISTENERS_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT #4
- KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093 #5
- KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER #6
environment に KRaft の設定が必要になっています。
- コントローラーになるブローカーのID を指定します。(シングルノードなので
KAFKA_CFG_NODE_ID
と同じ0
を指定) - ノードにコントローラーとブローカー両方のロールを付与します。
- ブローカーとコントローラーのリスナーを定義しています。それぞれ 9092、9093 ポートを割り当てています。
- リスナー用のセキュリティプロトコルマップの指定。PLAINTEXT にしています。
- Quorum voters として
0@kafka:9093
を指定しています。{id}@{host}:{port}
の形式で指定します。 - コントローラーのリスナー名を指定します。
この例ではプロトコルに PLAINTEXT を指定していますが、プロダクション環境では SASL などの認証メカニズムとプロトコルを利用しましょう。
compose.yml があるディレクトリ(ここでは kafka-kraft) で docker compose で実行します。
docker compose up -d
Kafka のコンテナが起動されていることを確認。
$ docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
kafka-kraft-kafka-1 bitnami/kafka:latest "/opt/bitnami/script…" kafka 10 minutes ago Up 10 minutes 9092/tcp
起動中のコンテナ kafka-kraft-kafka-1
に入って kafka-topics.sh でトピック一覧を表示します。
docker exec -it kafka-kraft-kafka-1 kafka-topics.sh --list --bootstrap-server kafka:9092
まだトピックはないので、エラーが出ずコマンドが終了すれば接続成功です。
開発時に便利なように、ホストマシンから接続できるようにし、kafka-ui も使えるようにしてみましょう。
services:
kafka:
image: 'bitnami/kafka:latest'
ports:
- '9094:9094' #1
environment:
- KAFKA_CFG_NODE_ID=0
- KAFKA_CFG_CONTROLLER_BROKER_ID=0
- KAFKA_CFG_PROCESS_ROLES=controller,broker
- KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094 #2
- KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://localhost:9094 #3
- KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT #4
- KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093
- KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER
kafka-ui:
image: 'provectuslabs/kafka-ui:latest'
ports:
- '8080:8080'
depends_on:
- kafka
environment:
- KAFKA_CLUSTERS_0_NAME=local #5
- KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS=kafka:9092 #6
- コンテナ外部からポート9094で Kafka Broker に接続できるようにします。
- 外部接続(EXTERNAL) のポート(9094)を追加します。
- EXTERNAL のホスト名を追加します。
- EXTERNAL のプロトコルを指定します。
- kafka-ui の管理対象のクラスターの名前をつけます。
- kafka-ui の管理対象のクラスターの bootstrap-server のホスト名とポートを指定します。
ホストマシンには、Homebrew などで kafka をインストールして、kafka の管理用コマンドが使えるようにしておきます。ホストマシンで kafka-topics.sh を実行してトピック一覧を表示します。
kafka-topics --bootstrap-server localhost:9094 --list
エラーなしでコマンドが終了すれば、接続成功です。
ホストマシンのブラウザから localhost:8080 にアクセスすると kafka-ui のダッシュボードが表示されます。
コントローラーとブローカーを複数のサービスに分離する、より本格的な構成については bitnami の GitHub リポジトリの README にサンプルがあります。
containers/bitnami/kafka/README.md at main · bitnami/containers
Bitnami ではなく Confluent のイメージを使いたい場合は、以下のリポジトリにサンプルがあります。
Kubernetes の場合
#Artifact Hub の Bitnami の Helm chart のページにドキュメントがあります。
kafka 29.3.4 · bitnami/bitnami
Bitnami の Helm chart では KRaft モードがデフォルトで有効化され、ZooKeeper は無効化されています。
kraft:
## @param kraft.enabled Switch to enable or disable the KRaft mode for Kafka
##
enabled: true
zookeeper:
## @param zookeeper.enabled Switch to enable or disable the ZooKeeper helm chart. Must be false if you use KRaft mode.
##
enabled: false
このため、オプション指定なしで helm install を実行すれば KRaft モードで Kafka クラスターが作成されます。Kafka client の Pod から簡易に接続確認するため、listeners.client.protocol
に PLAINTEXT
を指定した設定ファイルを用意しました。
listeners:
client:
protocol: PLAINTEXT
kafka
namespace を作成し、この設定ファイルを指定して helm install します。
kubectl create ns kafka
helm install my-kafka oci://registry-1.docker.io/bitnamicharts/kafka -n kafka -f values.yaml
作成されたオブジェクトを見てみましょう。
$ kubectl get po,svc,sts -n kafka
NAME READY STATUS RESTARTS AGE
pod/my-kafka-controller-1 1/1 Running 0 14m
pod/my-kafka-controller-2 1/1 Running 0 14m
pod/my-kafka-controller-0 1/1 Running 0 14m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/my-kafka-controller-headless ClusterIP None <none> 9094/TCP,9092/TCP,9093/TCP 14m
service/my-kafka ClusterIP 192.168.194.159 <none> 9092/TCP 14m
NAME READY AGE
statefulset.apps/my-kafka-controller 3/3 14m
コントローラー兼ブローカーの Pod (my-kafka-controller-*) が3つ起動しています[1]。もちろん ZooKeeper の Pod は起動していません。bootstrap-server に指定する (ClusterIP を持つ) Service と、broker / controller 個別の IP を返すための Headless Service が作られています。
接続確認をしてみます。Kafka クライアント用の Pod を起動します。
kubectl -n kafka run kafka-client --restart='Never' --image docker.io/bitnami/kafka --command -- sleep infinity
my-kafka:9092 を bootstrap-server に指定して、kafka-topics でリスト表示を実行します。何も表示されずコマンドが終了すれば成功です。
kubectl -n kafka exec -i kafka-client -- bash << 'EOS'
kafka-topics.sh --bootstrap-server my-kafka:9092 --list
EOS
おまけ(ホストマシンからの接続設定)
#おまけとして、Pod に入らなくても Kafka に接続できるよう、ホストマシンからの接続設定をしてみましょう。上記で作成した Kafka クラスターを削除します。
helm uninstall my-kafka -n kafka
設定ファイルに externalAccess
の設定を追加します。LoadBalancer や Ingress による設定も可能ですが、ここでは NodePort を使用しています(コントローラーとブローカー両方)。
externalAccess:
enabled: true
controller:
service:
type: NodePort
broker:
service:
type: NodePort
autoDiscovery:
enabled: true
rbac:
create: true
controller:
automountServiceAccountToken: true
broker:
automountServiceAccountToken: true
listeners:
client:
protocol: PLAINTEXT
external:
protocol: PLAINTEXT
ランダムなポートを利用できるよう、autoDiscovery.enabled
を true にしています。このオプションでは Kubernetes API を使用してポートの割り当てを行うため、rbac.create
を true
にしてcontroller.automountServiceAccountToken
、broker.automountServiceAccountToken
も true
にする必要があります。
listeners.client
と listeners.external
のプロトコルは簡便のため PLAINTEXT にしました。
この設定ファイルを指定して helm install を実行します。
helm install my-kafka oci://registry-1.docker.io/bitnamicharts/kafka -n kafka -f values.yml
Service を表示すると、3つの Pod に対応する NodePort が作成されています。
$ kubectl get svc -n kafka
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-kafka-controller-headless ClusterIP None <none> 9094/TCP,9092/TCP,9093/TCP 2m6s
my-kafka ClusterIP 192.168.194.201 <none> 9092/TCP,9095/TCP 2m6s
my-kafka-controller-1-external NodePort 192.168.194.185 <none> 9094:31512/TCP 2m6s
my-kafka-controller-2-external NodePort 192.168.194.217 <none> 9094:32077/TCP 2m6s
my-kafka-controller-0-external NodePort 192.168.194.230 <none> 9094:31943/TCP 2m6s
ホストマシンからは bootstrap-server に3つの NodePort [31512, 32077, 31943] のいずれかを指定して接続可能です。
kafka-topics --bootstrap-server localhost:31512 --list
さいごに
#以上、KRaft モードの Kafka をコンテナ環境で利用する方法の紹介でした。ローカルでも Docker や Kubernetes を使って簡単に利用できるようになっているので、開発環境構築がより楽になりますね。
Kafka 4.0 では ZooKeeper は削除される予定ですので、本番環境の移行が必要になります。Kafka のドキュメントにはすでにマイグレーション手順の記載が追加されています。
https://kafka.apache.org/documentation/#kraft_zk_migration
controller.replicaCount
のデフォルト値は3です。 ↩︎