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/mitamaeとsorah/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 段階でアップグレードしました。
- パッケージのみ更新し
inter.broker.protocol.version=3.6のまま全台をローリングリスタート - 全台完了後、
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 台ずつ処理します。
- mitamae でパッケージと設定を適用(hocho の機能)
- マシンを再起動(mitamaeのレシピ+補助スクリプト)
- SSH 復帰を待機(Rubyで待つ)
- Datadog API で under-replicated partitions が 0 になるまで待機3(RubyでAPI叩いて待機)
- 次のブローカーへ進む
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 などの分散システムの運用に興味がある方は、ぜひお話しましょう!
- 記事執筆時点では KRaft 移行は完了していません。↩
-
AmazonLinux2023 のバージョンは
2023.9.20251208や2023.10.20260216のようになっているのですがdnf check-release-update --latest-only --version-onlyで最新のリリースを取得すると2023.10.20260216ではなく2023.9.20251208が返ってきます。そのため、素朴に OS アップグレードを自動化すると、複数回アップグレードしなければならないことがあります。↩ -
kafka-topicsコマンドでも under-replicated partitions をチェックできます。↩ - Confluent Platform のパッケージでは提供されていないので Kafka Controller 用 systemd unit file を作成しました。↩