以下の内容はhttps://tech.repro.io/entry/2026/03/02/092645より取得しました。


Repro における AWS アカウント分離の取り組み Part3 - Kafka バージョンアップと KRaft 移行

AWS アカウント分離シリーズの Part3 です。Development Division/Platform Team/Sys-Infra Unit では、マネージドサービスを使わず EC2 + Auto Scaling Group で自前運用している Kafka クラスターの運用も担当しています。AWS アカウント分離に伴い Kafka クラスターも分離する必要がありますが、分離前の作業として Kafka のバージョンアップ(3.6 → 3.9)と ZooKeeper から KRaft への移行を進めました。本記事ではその過程を紹介します。

これまでのあらすじ

以前の記事で触れた通り Kafka クラスターの分離を実施する前に、バージョンアップと KRaft 移行を進めることにしました。1

構成管理

以前は、しっかりと構成管理されておらず、設定のドリフト等も発生していましたが、この機会にitamae-kitchen/mitamaesorah/hochoを使うことにしました。mitamae はシングルバイナリでホスト側に余計なものを入れずに使え、レシピを mruby で書けるのが利点です。hocho は複数ホストを効率良く構成管理するために導入しました。バージョンアップ開始前の状態で mitamae を導入しています。

hocho 本体にはシグナルハンドラーが設定されていないため SIGINT で即終了してしまいます。今回のユースケースでは、負荷が高い時間帯にローリングアップデートをホスト単位で中断したかったため、Rake task でラップして使っています。

Kafka 3.6 + ZK -> Kafka 3.9 + ZK へのバージョンアップ

Repro では Amazon Linux 2023 上で Confluent Platform を使って Kafka を運用しています。Kafka 4.0 で ZooKeeper が廃止されるため、ZK と KRaft の両方をサポートする最後のバージョンである 3.9 をターゲットにしました。しばらくぶりのバージョンアップだったのと短期間で完了させたかったため、3.6 から 3.9 へ直接バージョンアップしています。

Confluent Platform と Apache Kafka のバージョン対応は以下の通りです。

Confluent Platform Apache Kafka
7.6.x 3.6.x
7.9.x 3.9.x
8.0.x 4.0.x

参考: Confluent Platform and Apache Kafka Compatibility

アップグレード戦略

安全のため 2 段階でアップグレードしました。

  1. パッケージのみ更新し inter.broker.protocol.version=3.6 のまま全台をローリングリスタート
  2. 全台完了後、inter.broker.protocol.version=3.9 へ変更して再度ローリングリスタート

inter.broker.protocol.version を古いバージョンに据え置くことで、万が一問題が発生した場合にパッケージのダウングレードだけでロールバックできます。

Kafka パッケージの更新と合わせて Java 11 から Java 17 へのアップグレードと Amazon Linux 2023 の OS アップグレード2も同時に実施しました。カーネルパラメータの変更や Java のアップグレードを確実に反映するため、サービスの再起動ではなくマシン再起動を採用しています。

ローリングアップデートの自動化

3.6 までのバージョンアップ手順やスクリプトを参考にして mitamae + hocho を使ったローリングアップデートスクリプトを Ruby で作成しました。以下の流れで 1 台ずつ処理します。

  1. mitamae でパッケージと設定を適用(hocho の機能)
  2. マシンを再起動(mitamaeのレシピ+補助スクリプト)
  3. SSH 復帰を待機(Rubyで待つ)
  4. Datadog API で under-replicated partitions が 0 になるまで待機3(RubyでAPI叩いて待機)
  5. 次のブローカーへ進む

ZooKeeper ノードは最後に処理しています。

実施順序

dev_staging → staging → production の順に展開しました。各環境で問題がないことを確認してから次の環境に進めています。

変更内容

  • /etc/yum.repos.d/confluent.repo のバージョンを 7.6 から 7.9 に更新
  • Confluent Platform の GPG key を更新
  • java-11-amazon-corretto-headless から java-17-amazon-corretto-headless に変更

Kafka 3.9 + KRaft への移行

事前準備

  • EC2 インスタンスにタグ付け
  • Kafka Controller(KRaft) 用 EBS の準備
  • Kafka Controller 用 DNS レコードの準備

Apache Kafka のサイトや Confluent Platform のサイトを参考に作業手順をまとめた後 Phase ごとに mitamae のレシピにしました。

./hocho-apply.rb --stage staging --phase 1           # デフォルトでは Dry-run
./hocho-apply.rb --stage staging --phase 1 --execute # --execute を付けると実行
Phase 作業内容 ブローカー再起動 コントローラー再起動 ロールバック
Phase 1 ログ設定、安全対策設定 ✅ 容易
Phase 2 KRaftコントローラー起動 ✅ 容易
Phase 3 ハイブリッドモード移行 ✅ Rolling ✅ 可能
Phase 4 純粋KRaftモードへ変換 ✅ Rolling ✅ 可能(高リスク)
Phase 5 移行確定、ZooKeeper停止 ✅ Rolling 不可

Phase 1: Kafka Broker の設定変更

(1) /etc/kafka/log4j.properties で ZK to KRaft マイグレーション用のログレベルを変更します(Kafka Broker の再起動なし)。

log4j.logger.org.apache.kafka.metadata.migration=TRACE

(2) /etc/kafka/server.properties でアプリタイムアウト防止のため設定変更します。

auto.leader.rebalance.enable=false

Phase 3 で Kafka Broker を再起動するので Phase 1 では再起動しません。

Phase 2: Kafka Controller (KRaft) の準備

(1) コントローラー用設定ファイルを作成します。

/etc/kafka/kraft/controller.properties(抜粋):

node.id=2001
process.roles=controller
controller.quorum.voters=2001@kafka-controller-1.dev-staging.repro.local,...
controller.listener.names=CONTROLLER
listeners=CONTROLLER://:9093
listener.security.protocol.map=PLAINTEXT:PLAINTEXT,SSL:SSL,SASL_PLAINTEXT:SASL_PLAINTEXT,SASL_SSL:SASL_SSL,CONTROLLER:PLAINTEXT
zookeeper.metadata.migration.enable=true
# ZooKeeper 接続設定 (移行中のみ必要)
zookeeper.connect=zk1:2181,zk2:2181,zk3:2181 
log.dirs=/var/lib/kafka-controller

node.id は既存の broker.id と重複しない値を割り当てます。ZooKeeper を使っているときに一部のブローカーで broker.id を自動割り当てしており、1000 番台がいくつか使われていたため、ここでは 2000 番台をコントローラーの node.id としました。

(2) コントローラのフォーマットと起動4

cluster_id=$(zookeeper-shell localhost:2181 get /cluster/id | tail -n1 | jq -r .id)
echo "cluster_id=${cluster_id}"
kafka-storage format \
--cluster-id ${cluster_id} \
--config /etc/kafka/kraft/controller.properties
systemctl start kafka-controller.service

Phase 3: Kafka Broker 移行モード有効化

/etc/kafka/server.properties を更新して移行モードを有効化し、Kafka Broker を rolling restart します。

# 移行モード有効化
zookeeper.metadata.migration.enable=true

# KRaft コントローラー設定
controller.quorum.voters=2001@kafka-controller-1.dev-staging.repro.local,...
controller.listener.names=CONTROLLER

# リスナー設定に CONTROLLER を追加
listener.security.protocol.map=PLAINTEXT:PLAINTEXT,SSL:SSL,SASL_PLAINTEXT:SASL_PLAINTEXT,SASL_SSL:SASL_SSL,CONTROLLER:PLAINTEXT

Phase 4: Kafka Broker を KRaft モードに変更

/etc/kafka/server.properties を変更して Kafka Broker を rolling restart します。

# broker.id を node.id に変更 (同じ ID 値を使用)
node.id=1
# broker.id=1  ← 削除

process.roles=broker # 追加

# KRaft 設定は維持
controller.quorum.voters=2001@kafka-controller-1.dev-staging.repro.local:9093,...
controller.listener.names=CONTROLLER
listener.security.protocol.map=PLAINTEXT:PLAINTEXT,SSL:SSL,SASL_PLAINTEXT:SASL_PLAINTEXT,SASL_SSL:SASL_SSL,CONTROLLER:PLAINTEXT

# ZooKeeper 関係の設定を削除
# zookeeper.connect=...  ← 削除
# zookeeper.metadata.migration.enable=true  ← 削除

Phase 5: ZooKeeper 廃止(ロールバック不可)

(1) /etc/kafka/kraft/controller.properties を変更し Kafka Controller を rolling restart します。

# 以下の行を削除またはコメントアウト
# zookeeper.metadata.migration.enable=true  ← 削除
# zookeeper.connect=...  ← 削除

(2) ZooKeeper を停止します。

systemctl stop confluent-zookeeper.service
systemctl disable confluent-zookeeper.service

(3) Phase 1 で変更した設定を戻し Kafka Broker を rolling restart します。

参考: - Confluent Platform - Migrate ZooKeeper to KRaft - Apache Kafka 3.9 - ZooKeeper to KRaft Migration - Confluent Platform 7.9 - Migrate ZooKeeper to KRaft

ハマりポイント

controller.quorum.voters の設定漏れ

Phase 3 で controller.quorum.voters を設定せず controller.quorum.bootstrap.servers だけを設定してしまうと、いつまで経ってもマイグレーションが始まりません。しばらく待っても Completed migration of metadata from ZooKeeper to KRaft のログが出力されないことで気づきました。controller.quorum.voters を正しく設定すれば、メタデータの移行は比較的短時間で完了します。

Broker と Controller のログディレクトリの分離

Kafka Broker と Kafka Controller でアプリケーションログのディレクトリ(log.dirs ではなく環境変数 LOG_DIR)を分けておく必要があります。共有してしまうとログが壊れるほか、Kafka Controller を再起動した際に Kafka Broker が落ちます。想定していたログが出力されないことと、Controller の再起動時に Broker が落ちる現象から原因を特定しました。

Kafka Controller と Kafka Broker でホストを分けていれば、この問題は発生しません。既存の構成に合わせようとして Kafka Controller と Kafka Broker を同居させようとしたのが原因でした。

まとめ

本記事では、AWS アカウント分離の一環として実施した Kafka 3.6 から 3.9 へのバージョンアップと ZooKeeper から KRaft への移行について紹介しました。

バージョンアップでは inter.broker.protocol.version を据え置く 2 段階アップグレードでロールバック可能な状態を維持しつつ、Java 17 への更新や OS アップグレードも同時に実施しました。KRaft 移行では 5 つの Phase に分け、各 Phase のロールバック可否を整理した上で段階的に進めています。いずれも mitamae + hocho による構成管理とローリングアップデートの自動化により、安全かつ効率的に作業を進めることができました。

KRaft 移行の完了後、Kafka クラスターの AWS アカウント分離に取り組む予定です。

WE ARE HIRING!

Reproでは開発者を募集しています。AWS アカウント分離のような大規模なインフラ移行プロジェクトや、Kafka などの分散システムの運用に興味がある方は、ぜひお話しましょう!


  1. 記事執筆時点では KRaft 移行は完了していません。
  2. AmazonLinux2023 のバージョンは 2023.9.202512082023.10.20260216 のようになっているのですが dnf check-release-update --latest-only --version-only で最新のリリースを取得すると 2023.10.20260216 ではなく 2023.9.20251208 が返ってきます。そのため、素朴に OS アップグレードを自動化すると、複数回アップグレードしなければならないことがあります。
  3. kafka-topics コマンドでも under-replicated partitions をチェックできます。
  4. Confluent Platform のパッケージでは提供されていないので Kafka Controller 用 systemd unit file を作成しました。



以上の内容はhttps://tech.repro.io/entry/2026/03/02/092645より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14