Kafkaの一般的なパフォーマンスボトルネックのトラブルシューティング:実践ハンドブック
この実践ハンドブックでは、Apache Kafkaにおける一般的なパフォーマンスボトルネックの特定と解決方法を解説します。スループットの制限、高レイテンシ、コンシューマラグに、実用的なアドバイスと設定例で対処します。主要なメトリクスを理解し、実証済みのトラブルシューティング手法を適用することで、Kafkaクラスタを最適化し、より効率的なイベントストリーミングプラットフォームを実現します。
Kafkaの一般的なパフォーマンスボトルネックのトラブルシューティング:実践ハンドブック
Kafkaのパフォーマンス作業は、すべての遅延がKafkaの問題として扱われると混乱します。ブローカーが飽和状態にあることもあれば、プロデューサーが小さな非圧縮レコードを送信していることもあります。コンシューマーがデータベースを待機しており、Kafkaは単なるメッセンジャーであることもあります。有用なトラブルシューティングのパスは、時間がどこで費やされているかを特定することから始まります:プロデューサー送信、ブローカー追記とレプリケーション、コンシューマーフェッチ、またはフェッチ後のアプリケーション処理。
このハンドブックは、そのような調査のために書かれています。観察可能な症状、考えられる原因、そして一度に一つずつテストする価値のある変更に焦点を当てています。
Kafkaパフォーマンスメトリクスの理解
トラブルシューティングに入る前に、パフォーマンスの健全性を示す主要なメトリクスを理解することが不可欠です。これらのメトリクスを定期的に監視することで、異常を早期に発見できます:
- ブローカーメトリクス:
BytesInPerSecとBytesOutPerSec:入出力データレートを測定します。高い値は高負荷を示し、低い値は他の場所にボトルネックがある可能性を示します。RequestQueueTimeMs:リクエストがリクエストキューで待機する平均時間。高い値はブローカーの過負荷を示します。NetworkProcessorAvgIdlePercent:ネットワークスレッドがアイドル状態である時間の割合。低いパーセンテージは高いネットワークI/O負荷を示します。LogFlushRateAndTimeMs:ディスクフラッシュ操作を測定します。ここでの高レイテンシは、プロデューサーとフォロワーレプリケーションに直接影響します。UnderReplicatedPartitions:必要なレプリカ数より少ないパーティションの数。これはレプリケーションラグと潜在的なデータ損失を示す可能性があります。
- プロデューサーメトリクス:
RecordBatchSize:レコードバッチの平均サイズ。大きなバッチはスループットを向上させる可能性がありますが、レイテンシを増加させます。RecordSendRate:1秒あたりに送信されるレコード数。CompressionRate:圧縮の効果。高いレートは転送されるデータ量が少ないことを意味します。
- コンシューマーメトリクス:
FetchRate:1秒あたりのフェッチリクエスト数。BytesConsumedPerSec:1秒あたりに消費されるデータ量。OffsetLagMax:コンシューマーグループの最大オフセットラグ。これはコンシューマーパフォーマンスの重要な指標です。
- コントローラーメタデータメトリクス: ZooKeeperベースのクラスタでは、ZooKeeperリクエストレイテンシと接続の健全性を監視します。KRaftベースのクラスタでは、コントローラークォーラムの健全性とメタデータリクエストレイテンシを監視します。正確なメトリクス名は、Kafkaのバージョンと監視スタックによって異なります。
一般的なボトルネックシナリオと解決策
1. スループットの制限
スループットの制限は、データの取り込みや消費の遅延として現れ、イベントストリームの全体的な速度に影響を与える可能性があります。
1.1. 不十分なネットワーク帯域幅
- 症状:
BytesInPerSecまたはBytesOutPerSecがネットワークインターフェースの限界に近づいている、プロデューサー/コンシューマーのスループットが遅い。 - 診断: ブローカー、プロデューサー、コンシューマーのネットワーク使用率を監視します。利用可能な帯域幅と比較します。
- 解決策:
- ネットワークのスケールアップ: ブローカーマシンのネットワークインターフェースまたはNICをアップグレードします。
- 負荷の分散: ブローカーを追加してネットワークトラフィックを分散します。トピックがブローカー間で適切にパーティション分割されていることを確認します。
- シリアライゼーションの最適化: 効率の悪いシリアライゼーションフォーマット(例:JSON)よりも効率的なもの(例:Avro、Protobuf)を使用します。
- 圧縮: プロデューサー側の圧縮(Gzip、Snappy、LZ4、Zstd)を有効にして、ネットワーク経由で送信されるデータ量を削減します。例えば、プロデューサーを以下のように設定します:
# producer.properties compression.type=snappy
1.2. ディスクI/Oボトルネック
- 症状:
LogFlushRateAndTimeMsメトリクスが高い、ディスクの読み取り/書き込み操作が遅い、プロデューサーとフォロワーが遅れている。 - 診断: ブローカーマシンのディスクI/O使用率(IOPS、スループット)を監視します。Kafkaはシーケンシャルディスク書き込みに大きく依存しています。
- 解決策:
- より高速なディスク: Kafkaログには、より高速なSSDまたはNVMeドライブを使用します。ワークロードに十分なIOPSとスループットを確保します。
- RAID構成: 書き込みパフォーマンスを優先するRAID構成(例:RAID 0、RAID 10)を使用しますが、冗長性とのトレードオフに注意してください。
- ディスクの分離: Kafkaログを複数の物理ディスクに分散して、I/O操作を並列化します。
log.flush.interval.messagesとlog.flush.interval.msの調整: これらの設定は、ログがディスクにフラッシュされる頻度を制御します。大きな値はフラッシュ頻度を減らしてスループットを向上させる可能性がありますが、フラッシュ前にブローカーが障害を起こした場合のデータ損失のリスクを高めます。- 耐久性のトレードオフに注意: ブローカーのフラッシュ設定とプロデューサーの
acksは、許容する障害リスクの程度に影響します。耐久性の期待値を下げると、一部のワークロードでレイテンシを削減できる可能性がありますが、それはビジネス上の決定であり、文書化された障害モデルに基づくべきであり、軽率なチューニングのトリックではありません。
1.3. 不十分なブローカーリソース(CPU/メモリ)
- 症状: ブローカーのCPU使用率が高い、
RequestQueueTimeMsが高い、NetworkProcessorAvgIdlePercentが低い。 - 診断: ブローカーマシンのCPUとメモリ使用率を監視します。
- 解決策:
- スケールアップ: 既存のブローカーインスタンスのCPUコアまたはRAMを増やします。
- スケールアウト: クラスタにブローカーを追加します。負荷を分散するためにトピックが適切にパーティション分割されていることを確認します。
- JVMヒープの調整: KafkaブローカーのJVMヒープサイズを調整します。ヒープが小さすぎるとガベージコレクションの一時停止が頻繁に発生し、大きすぎると問題が発生する可能性があります。多くのワークロードでは、6GBまたは8GBが一般的な開始点です。
- 操作のオフロード: Kafkaブローカーマシンで他のリソース集約型アプリケーションを実行しないようにします。
2. 高レイテンシ
高レイテンシとは、イベントが生成されてから消費されるまでに顕著な遅延があることを意味します。
2.1. プロデューサーレイテンシ
- 症状: プロデューサーが
request.timeout.msまたはdelivery.timeout.msの値に達したと報告する。 - 診断: プロデューサーの設定とネットワーク状況を分析します。
- 解決策:
acks設定:acks=allは必要な同期レプリカを待機し、耐久性が重要な場合には通常正しい選択です。min.insync.replicasと組み合わせて、レプリケートされた本番トピックでは通常1より大きい値に設定します。acks=1は待機時間を削減できますが、ブローカー障害時の損失リスクが高まります。linger.ms:linger.msを小さな値(例:0-10ms)に設定すると、メッセージがすぐに送信され、レイテンシは削減されますが、リクエストのオーバーヘッドが増加する可能性があります。値を大きくすると、より多くのメッセージがバッチ処理され、スループットは向上しますが、レイテンシが増加します。batch.size: バッチサイズが大きいとスループットは向上しますが、レイテンシが増加する可能性があります。レイテンシ要件に基づいて調整します。- ネットワーク: プロデューサーとブローカー間の低レイテンシネットワークパスを確保します。
- ブローカー負荷: ブローカーが過負荷の場合、プロデューサーリクエストがキューイングされます。
2.2. コンシューマーレイテンシ(オフセットラグ)
- 症状: コンシューマーがコンシューマーグループの
OffsetLagMaxが大きいと報告する。 - 診断:
kafka-consumer-groups.shや監視ダッシュボードなどのツールを使用して、コンシューマーグループのラグを監視します。 - 解決策:
- コンシューマーのスケール: コンシューマーグループ内のコンシューマーインスタンスの数を、トピックのパーティション数まで増やします。各コンシューマーインスタンスは1つ以上のパーティションからのメッセージのみを処理でき、パーティションは同じグループ内の複数のコンシューマーで共有できません。
- パーティションの増加: トピックのパーティションが少なすぎてプロデューサーの書き込みレートに追いつけない場合は、パーティション数を増やします。注: これは永続的な変更であり、既存のコンシューマーとプロデューサーに影響を与えるため、慎重な検討が必要です。
# トピックのパーティションを増やす例 kafka-topics.sh --bootstrap-server localhost:9092 --alter --topic my-topic --partitions 12- コンシューマーロジックの最適化: コンシューマー内の処理ロジックが効率的であることを確認します。ブロッキング操作や長時間実行タスクを避けます。可能であれば、メッセージをバッチで処理します。
- フェッチ設定: コンシューマーの
fetch.min.bytesとfetch.max.wait.msを調整します。fetch.min.bytesを大きくするとスループットは向上しますがレイテンシが増加し、fetch.max.wait.msは最小バイト数に達していなくても、コンシューマーがデータを待機する最大時間を制御します。 - ブローカーパフォーマンス: ブローカーが問題を抱えている場合(ディスク、ネットワーク、CPU)、フェッチリクエストとコンシューマーラグに直接影響します。
3. ZooKeeperのボトルネック
KafkaはコントローラークォーラムとしてKRaft(Kafka Raft)に移行していますが、多くのデプロイメントは依然としてZooKeeperに依存しています。ZooKeeperの問題はKafkaの運用を麻痺させる可能性があります。
- 症状: ブローカーの起動が遅い、トピック/パーティションの再割り当てに問題がある、
zk_avg_latencyが高い、ブローカーがZooKeeperへの接続エラーを報告する。 - 診断: ZooKeeperのパフォーマンスメトリクスを監視します。ZooKeeperのログでエラーを確認します。
- 解決策:
- 専用のZooKeeperクラスタ: ZooKeeperをKafkaブローカーとは別の専用マシンで実行します。
- 十分なリソース: ZooKeeperノードに十分なCPU、メモリ、高速I/O(特にSSD)を確保します。
- ZooKeeperの調整: ネットワークとクラスタサイズに基づいて、ZooKeeperの
tickTime、syncLimit、initLimit設定を調整します。 - ZooKeeperトラフィックの削減: 頻繁なトピックの作成/削除や積極的なコントローラーフェイルオーバーなど、ZooKeeperを頻繁に更新する操作を最小限に抑えます。
- KRaftへの移行: ZooKeeperの依存関係を排除するために、KRaftモードへの移行を検討します。
パフォーマンス最適化のベストプラクティス
- 継続的な監視: すべての主要なKafkaおよびZooKeeperメトリクスに対して、堅牢な監視とアラートを実装します。
- 設定の調整: 各設定パラメータの影響を理解し、特定のワークロードとハードウェアに基づいて調整します。適切なデフォルトから始めて、反復的に改善します。
- パーティショニング戦略: トピックごとに適切な数のパーティションを選択します。少なすぎると並列性が制限され、多すぎるとオーバーヘッドが増加する可能性があります。
- ハードウェアの選択: Kafkaブローカーには、特に高速ディスクと十分なネットワーク帯域幅など、適切なハードウェアに投資します。
- プロデューサーとコンシューマーの調整: プロデューサーには
batch.size、linger.ms、acksを、コンシューマーにはfetch.min.bytes、fetch.max.wait.ms、max.poll.recordsを最適化します。 - Kafkaのアップデート: 新しいバージョンには、パフォーマンスの改善やバグ修正が含まれていることがよくあります。
- 負荷テスト: 本番トラフィックをシミュレートし、ライブシステムに影響を与える前に潜在的なボトルネックを特定するために、定期的に負荷テストを実施します。
パフォーマンス調査の実行方法
一度に1つのレイヤーを変更します。プロデューサーが遅い場合は、まずプロデューサーメトリクス(リクエストレイテンシ、バッチサイズ、圧縮率、リトライ、バッファ枯渇など)を確認します。ブローカーが遅い場合は、リクエストキュー時間、ネットワークスレッドアイドル率、ディスク待機時間、ページキャッシュプレッシャー、アンダーレプリケーションパーティション、コントローラーの安定性を確認します。コンシューマーが遅い場合は、パーティションごとのラグ、バッチあたりの処理時間、ダウンストリーム依存関係のレイテンシ、リバランス頻度を確認します。
実際の例:マーケティングキャンペーン後、注文トピックでラグが増加しています。ブローカーのCPUは問題なく、ディスク書き込みも問題なく、プロデューサーのリトライも正常です。kafka-consumer-groups.sh --describe を実行すると、1つのパーティションにほとんどのラグがあることがわかります。これはブローカーの容量ではなく、パーティションの偏りを示しています。レコードが顧客IDでキー付けされており、1つの大口顧客がほとんどのイベントを生成している場合、コンシューマーを追加してもそのパーティションはグループ内の1つのコンシューマーにしか割り当てられないため、役に立ちません。将来のデータのためにキー戦略を変更するか、トピックでワークロードを分割するか、そのコンシューマーパスを高速化する必要があるかもしれません。
別の例:すべてのパーティションが一緒にラグっており、コンシューマーログに支払いAPIの呼び出しに数秒かかっていることが示されています。Kafkaのフェッチ調整ではそれは修正できません。コンシューマー内で制限付き並行性を導入するか、Kafkaと低速な依存関係の間にキューを配置するか、一括書き込みを行うか、バックプレッシャーとリトライに関する製品上の決定を行う必要があります。
優れたKafkaの調整は、主に規律ある測定です。ベースラインを維持し、1つの変更を行い、現実的なレコードサイズとキーで負荷テストを実施し、p95およびp99レイテンシとスループットを比較します。平均レイテンシは問題なく見えても、少数のパーティションがすでに遅れている可能性があります。
設定を変更する前に確認すること
Kafkaを調整する前に、ボトルネックが実際にKafkaにあることを証明するのが好きです。1つの遅いパスを選び、エンドツーエンドでトレースします。生成されたイベントについて、プロデューサーが送信完了を待つのにどれくらい時間がかかるか?レコードがトピックに現れるまでどれくらい時間がかかるか?コンシューマーがそれをフェッチするまでどれくらい時間がかかるか?フェッチ後にコンシューマーが費やす時間はどれくらいか?これらの4つの数値は、多くのランダムな設定変更を防ぎます。
プロデューサーの送信時間が長い場合は、バッチ処理、圧縮、リトライ、acks、delivery.timeout.ms、ブローカーリクエストレイテンシを調査します。ブローカーの追記が遅い場合は、ディスク、ネットワーク、ISRの変動、コントローラーイベント、リクエストキューを調査します。コンシューマーのフェッチは速いが処理が遅い場合は、ブローカースレッドの調整をやめてアプリケーションコードを確認します。ダウンストリームのデータベース書き込みまで全てが速い場合、Kafkaはボトルネックではありません。
現実的なパターンがあります。チームがエンドツーエンドのレイテンシが高いことを確認し、ブローカーのメモリを増やします。何も変わりません。次にコンシューマーのタイミングを確認すると、各メッセージが3つのシリアルHTTP呼び出しを実行していることがわかります。Kafkaはバッチを迅速に配信していました。コンシューマーはほとんどの時間をクラスタ外での待機に費やしていました。有用な修正は、制限付き並行性、タイムアウト、およびダウンストリーム障害が繰り返し発生した場合のデッドレターパスでした。
もう1つの一般的なパターンは、小さなプロデューサーバッチです。サービスが1つの小さなJSONレコードを、lingerも圧縮もなしで一度に送信します。ブローカーのCPUが上昇し、ネットワークオーバーヘッドが増加し、単一のマシンが完全に飽和しているようには見えなくてもスループットが低下します。小さな linger.ms、大きな batch.size、より高速なシリアライゼーションフォーマットにより、ブローカーを追加するよりもスループットが向上する可能性があります。適切な値はレイテンシ許容度に依存するため、別のシステムからデフォルトをコピーするのではなく、実際のレコードサイズでテストしてください。
最も安全なパフォーマンス変更は、元に戻せて測定可能なものです。クライアント設定は通常、パーティション数の変更よりもロールバックが容易です。圧縮の変更は通常、ハードウェアの変更よりもテストが容易です。パーティションの増加は有用ですが、順序と将来のキー配布に影響を与えるため、通常のクライアント側の調整変更よりも注意が必要です。