Apache Kafkaの性能検証(1): 検証環境とパラメータチューニングの内容

初版: 2018/10/19
著者: 伊藤 雅博, 株式会社日立製作所

# はじめに

この投稿では[オープンソースカンファレンス2017.Enterprise](https://www.ospn.jp/osc2017.enterprise/)で発表した「[めざせ！Kafkaマスター　～Apache Kafkaで最高の性能を出すには～](https://www.ospn.jp/osc2017.enterprise/pdf/OSC2017.enterprise_Hitachi_Kafka.pdf)」の検証時に調査した内容を紹介します（全8回の予定）。本投稿の内容は2017年6月にリリースされたKafka 0.11.0 時点のものです。

第4回目となる今回からは、実際にKafkaの性能検証を行った際の測定結果を紹介していきます。今回はまず、検証環境のシステム構成と検証方法について紹介します。

**投稿一覧：**
1. [Apache Kafkaの概要とアーキテクチャ](https://qiita.com/sigmalist/items/5a26ab519cbdf1e07af3)
2. [Apache KafkaのProducer/Broker/Consumerのしくみと設定一覧](https://qiita.com/sigmalist/items/3b512e2ab49b07271665)
3. [Apache Kafkaの推奨構成と性能の見積もり方法](https://qiita.com/sigmalist/items/b3d25e914dc30539ece4)
4. Apache Kafkaの性能検証(1): 検証環境とパラメータチューニングの内容 (本投稿)
5. [Apache Kafkaの性能検証(2): Producerのチューニング結果](https://qiita.com/sigmalist/items/0cd76d7edb055e89d1e6)
6. [Apache Kafkaの性能検証(3): Brokerのチューニング結果](https://qiita.com/sigmalist/items/0795bde01bcbd78b1c47)
7. [Apache Kafkaの性能検証(4): Producerの再チューニングおよびConsumerのチューニング結果](https://qiita.com/sigmalist/items/b0bc222f449a9f3cc273)
8. [Apache Kafkaの性能検証(5): システム全体のレイテンシについて](https://qiita.com/sigmalist/items/111e96cdae69bb2d89e6)


# 検証の目的

Kafkaは非同期レプリケーションであれば、ディスク/ネットワーク性能の上限に近いスループットを出すことができます。一方、同期レプリケーション時の性能については情報があまりありません。一般的に、本番環境ではデータを保護するための同期レプリケーションが求められます。そこで、同期レプリケーション時のスループットを測定するために、性能検証を実施しました。

[前回の投稿](https://qiita.com/sigmalist/items/b3d25e914dc30539ece4#%E7%90%86%E8%AB%96%E4%B8%8A%E3%81%AE%E6%9C%80%E5%A4%A7%E3%82%B9%E3%83%AB%E3%83%BC%E3%83%97%E3%83%83%E3%83%88%E3%81%AE%E8%A6%8B%E7%A9%8D%E3%82%82%E3%82%8A)で説明した通り、Producerが送信したデータをConsumer Groupがすぐに読み出すストリームデータ処理システムでは、処理性能が最も低い箇所がボトルネックとなります。そのためKafkaで高いスループットを出すには、Producer-Broker間通信、Broker-Broker間通信、BrokerのディスクI/O、Broker-Consumer間通信の全てにおいて高いスループットを出す必要があります。

本検証では、データ処理のスループットに着目してチューニングを行いました。一般的に、スループットが増加するとレイテンシも増加しますが、本検証で行うチューニングはレイテンシについては考慮していません。なお、レイテンシの測定結果については今後の投稿で紹介します。

# 検証内容

検証システムの全体像を以下に示します。本検証では、Producerノードで生成した検証用データ（1KBのRecord）を、複数のProducerがBrokerへ600秒間送信し続けます。同時に、1つのConsumer GroupがBrokerからデータを取得し続けます。このシステムにおける、Consumerの600秒間の平均受信スループットを最大化するために、Producer、Broker、Consumerのパラメータチューニングを行いました。

![kafka04_01.png](https://qiita-image-store.s3.amazonaws.com/0/285851/ef125512-849a-67f2-7a46-f5d28561f61d.png)

今回は同期レプリケーション時の性能を測定するため、Replica複製数=3、最小ISR数=2、acks=allに設定しました。

Brokerの合計ディスク台数は24台のため、全ディスクを使い切って性能を出すためには最低でも24個のReplicaが必要となります。今回は1個のTopicを使用して性能検証を行うため、このTopicのPartition数は 24ディスク ÷ 3Replication = 8個 以上にする必要があります。

ただし、全てのPartitionに均等にデータが読み書きされるとは限らず、Partitionごとに偏りが出る場合もあります。そのため、今回は1ディスクあたり6個のReplicaを割り当てることにして、Partitions数を 24ディスク × 6Replica ÷ 3Replication = 48個 に設定しました。

| コンポーネント | パラメータ名                | デフォルト値 | 設定値 |
|:--------------|:---------------------------|:------------|:------|
| Broker        | num.partitions             | 2           | 48    |
| Broker        | default.replication.factor | 1           | 3     |
| Broker        | min.insync.replicas        | 1           | 2     |
| Producer      | acks                       | 1           | all   |

# 検証環境

Producer用ノード2台、Broker用ノード3台、Consumer用ノード1台には、以下に示すスペックの物理マシンを使用しました。また、ソフトウェアのパラメータ設定については、[前回の投稿](https://qiita.com/sigmalist/items/b3d25e914dc30539ece4#%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2%E3%81%AE%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E8%A8%AD%E5%AE%9A)で説明した推奨設定を使用しました。

| 項目 | 設定 |
|:------|:------|
| 	サーバ種別  | [HA8000/TS20AN](http://www.hitachi.co.jp/Prod/comp/OSD/pc/ha/products/hardware/tsseries/pdf/spec_ts20_e5-2600_n.pdf) |
| CPUコア数 | 20コア40スレッド |
| メモリ容量 | 384 GB |
| ディスク | 	1,200GB (SAS 10,000 rpm) を10台。<br/>OS用に2台でRAID1、Brokerは8台をJBODでLogディレクトリに割り当て。<br>検証前に測定したディスクのシーケンシャルアクセス速度は約150MB/s。 |
| OS | RHEL 6.7 |
| JDK | JDK 1.8.0.121 |
| ファイルシステム | EXT4（マウントオプション：defaults,noatime） |
| Kafkaバージョン | Kafka 0.11.0 |

今回は性能検証であるため、ZooKeeperは1台のみで冗長化はしていません。ZooKeeper用ノードのスペックを以下に示します。

| 項目 | 設定 |
|:------|:------|
| 	サーバ種別  | 仮想マシン |
| CPUコア数 | 4スレッド |
| メモリ容量 | 16 GB |
| ディスク | 	80GBを1台 |
| OS | RHEL 6.7 |
| JDK | JDK 1.8.0.121 |
| ZooKeeperバージョン | ZooKeeper 3.4.10 |

検証システムのネットワーク構成を以下に示します。Producer、Broker、Consumerノード間は10 Gbps回線で接続し、ZooKeeperは1 Gbps回線で接続しました。検証前にiperfコマンドでノード間のスループットを測定し、Producer、Broker、Consumerノード間で同時に約1,170MB/sのスループットが出ることを確認しました。

![kafka04_02.png](https://qiita-image-store.s3.amazonaws.com/0/285851/94414f2c-4b04-ddc3-a3c3-d1f6fa57f683.png)

# 理論上の最大スループット

上記の検証環境における、理論上の最大スループットを以下に示します。検証ではこの最大スループットをめざして、Producer、Broker、Consumerのパラメータチューニングを行いました。

今回の検証では、Producerが送信したデータをConsumer Groupがすぐに読み出すストリームデータ処理システムを想定します。このシステムでは、レプリケーションやConsumer Groupによる読み出しの遅延が発生しない限り、データはすべてページキャッシュ上にあります。そのため、ここではディスク読み出しは発生しないと仮定しています。

このシステムでは、Producerノード2台が合計1,170 MB/sでデータを送信すると、Brokerの送受信とConsumerの受信ネットワーク帯域が、理論上の最大スループットである1,170 MB/sに達します。これらの個所がボトルネックとなるため、Producerノードがこれ以上のスループットでデータを送信しても、システム全体のスループットは向上しません。よって、システム全体の理論上の最大スループットは 1,170 MB/sとなります。

![kafka04_03.png](https://qiita-image-store.s3.amazonaws.com/0/285851/6e970a9b-edaf-e744-08d3-fc9147343938.png)

# パラメータチューニング

## チューニングの順序

KafkaはProducer、Broker、Consumerの3コンポーネントに分かれていますが、ストリーム処理ではシステム全体を通した性能が重要であり、どれかがボトルネックとなってはいけません。しかし3コンポーネントの全パラメータの組み合わせを調べることは難しいため、本検証では以下の4つの範囲に分けて順番にチューニングを行いました。

**1. Producerの送信性能**
システム全体で十分な量のデータ（理論値である1,170MB/s）を流すため、最初にProducer-Broker間の通信スループットを最適化しました。この検証ではBroker側のLogフラッシュやレプリケーションの影響を排除するため acks=0 でチューニングを実施しました。

**2. BrokerのLogフラッシュ性能**
BrokerのLogフラッシュ（ディスクI/O）まで含めたスループットを最適化しました。この検証ではレプリケーションの影響を排除するため、acks=1で測定を実施しました。

**3. Broker間のレプリケーション性能**
Broker間のレプリケーションまで含めたスループットを最適化しました。この検証ではProduce/Replicateスループットの変化を見るため、まずはacks=1でチューニングを行い、最後にacks=allに変更しました。

**4. Consumerの取得性能**
acks=allでチューニングを行い、Consumerの取得まで含めたシステム全体のスループットを最適化しました。

## チューニング対象のパラメータ
本検証でチューニングしたパラメータを以下に示します。各パラメータの詳細は[第2回の投稿](https://qiita.com/sigmalist/items/3b512e2ab49b07271665)を参照してください。

| コンポーネント | パラメータ名 | デフォルト値 | 検証範囲 |
|:------------------|:----------------|:----------------|:--------|
| 	Producer         | batch.size                | 16KB | 16-1,024KB |
| 	Producer         | linger.ms | 0ms | 0-20ms |
| 	Producer         | compression.type | none | none, gzip, snappy, z4 |
| 	Broker             | num.network.threads | 3 | 3-16 |
| 	Broker             | log.flush.scheduler.interval.ms | Long.MAX_VALUE | 1-128秒 |
| 	Broker             | num.io.threads | 8 | 8-24 |
| 	Broker             | replica.fetch.response.max.bytes | 10MB | 10MB, 100MB |
| 	Broker             | num.replica.fetchers | 1 | 1-16 |
| 	Consumer       | fetch.max.bytes | 50MB | 50MB, 1,000MB |
| 	Consumer       | max.partition.fetch.bytes | 1MB | 1-10MB |
| 	Consumer       | Consumer数 | なし | 4-20個 |

## 固定したパラメータ

本検証で固定したパラメータを以下に示します。今回の性能検証はスループットを最大化することが目的であるため、上限値を定義するパラメータに対しては、その制限を受けないように大きな値を設定しました。本番環境では適切な上限値を設定する必要があります。各パラメータの詳細は[第2回の投稿](https://qiita.com/sigmalist/items/3b512e2ab49b07271665)を参照してください。

| コンポーネント | パラメータ名 | デフォルト値 | 設定値 |
|:------------------|:----------------|:----------------|:--------|
| 	Producer         | buffer.memory                 | 32MB | 1GB |
| 	Producer         | max.request.size | 1MB | Int.MAX_VALUE |
| 	Producer         | max.in.flight.requests.per.connection | 5 | Int.MAX_VALUE |
| 	Producer         | max.block.ms | 60秒 | Long.MAX_VALUE |
| 	Producer         | retries | 0 | Int.MAX_VALUE |
| 	Producer         | request.timeout.ms | 30秒 | Int.MAX_VALUE |
| 	Broker             | queued.max.requests | 500 | 5,000 |
| 	ZooKeeper      | zookeeper.connection.timeout.ms | 6秒 | 60秒 |
| 	ZooKeeper      | zookeeper.session.timeout.ms       | 6秒 | 60秒 |

# 性能メトリクスの収集方法

Kafkaは[性能情報などのメトリクスをJMXで公開](https://kafka.apache.org/0110/documentation/#monitoring)しています。今回の性能検証では、スループットなどのメトリクスの監視に[Prometheus](https://prometheus.io/)、メトリクスの収集に[JMX Exporter](https://github.com/prometheus/jmx_exporter)を使用しました。

スループットに関するメトリクスを以下に示します。なお、これらのスループットはProducer、Broker、Consumerが送受信したRecordサイズであり、メタデータ取得などのスループットは含みません。これらのメトリクスの他にも、スレッドごとのCPU使用率など様々なメトリクスを監視して、パラメータチューニングに役立てました。

| メトリクス | 説明 |
|:-------------|:------|
| kafka.producer.producer-topic-metrics.byte-rate | ProducerがProduceリクエストで送信した秒間Recordサイズ<br/>（以降、<u>Produceスループット</u>と呼ぶ） |
| kafka.server.replica-fetcher-metrics.incoming-byte-rate | BrokerのReplica fetcherがFetchリクエストで取得した秒間Recordサイズ<br/>（以降、<u>Replicateスループット</u>と呼ぶ） |
| kafka.consumer.consumer-fetch-manager-metrics.bytes-consumed-ratee | ConsumerがFetchリクエストで取得した秒間Recordサイズ<br/>（以降、<u>Consumeスループット</u>と呼ぶ） |

# 測定手順
1回の測定手順を以下に示します。この手順を、パラメータを変えて繰り返し実行しました。

1.  Topicを削除して再作成
2.	全ノードのページキャッシュをクリア
3.	Brokerを再起動（Brokerのパラメータ変更を適用）
4.	ProducerとConsumerを起動して処理を実行（ProducerとConsumerのパラメータ変更を適用）。Producer が1KBのRecordをBrokerに送信し続け、Consumerはそれを取得し続ける
5.	600秒経過した時点で、Prometheusが収集したメトリクスからスループットの平均値などを計算
6.	ProducerとConsumerを停止

# おわりに

本投稿では検証環境のシステム構成と検証方法について紹介しました。次回は、実際にProducerをチューニングして性能測定を行った結果を紹介します。

第5回：[Apache Kafkaの性能検証(2): Producerのチューニング結果](https://qiita.com/sigmalist/items/0cd76d7edb055e89d1e6)