REST Proxy の本稼働環境のデプロイ

このセクションは、Confluent REST Proxy を実行するための網羅的なガイドではありませんが、プロキシの本稼働環境の準備ができているかどうかを確認する前に考慮すべき重要事項について説明するものです。

以下の 3 つの主要な領域を扱います。

  • ハードウェアの推奨事項やデプロイ戦略など、計画面に関する考慮事項
  • 本稼働環境により適した構成変更
  • デプロイ後の考慮事項

ハードウェア

標準的な開発パスを踏襲している方であれば、ノート PC で REST Proxy に触れたことがあるかと思います。しかし、本稼働環境にデプロイする場合には、考慮すべきいくつかの推奨事項があります。

メモリー

REST Proxy のメモリー使用量は、主にコンシューマーの数に関連しています。その理由としては、これらのコンシューマーがプロキシによって管理される唯一のステートフルリソースであるためです。コンシューマーがメッセージをバッファする方法は 2 つありますが、それらの方法は合計メモリー使用量に影響することがあります。1 つ目の方法として、基礎となる Java コンシューマーが fetch.max.message.bytes x queued.max.message.chunks バイトまでデータをバッファします。デフォルト値は、コンシューマーごとに 2 MB になります。2 つ目の方法では、各コンシューマーリクエスト中に、consumer.request.max.bytes までのバイトがバッファされてから応答が返されます。デフォルト値は 64 MB です。実際には、ほとんどのコンシューマーはデータの安定したストリームを持っており、その場合、consumer.request.max.bytes までバッファリングされるのではなくリクエストがすぐに返ってくるか、またはデータがほとんどないためバッファスペースの大部分が使用されず、コンシューマーあたりの平均メモリー使用量は最初の値に近くなります。

すべての生成リクエストは、データフォーマットごとに 1 つのプロデューサーセットによって処理されます。それぞれに、送信されるのを待機しているレコードのバッファがあります。デフォルトでは、それぞれ 32 MB です。現在のデフォルトのプロデューサー設定およびデータフォーマット(バイナリ、Avro、JSON スキーマ、Protobuf)では、バッファに必要なのは 64 MB のみです。Avro、JSON スキーマ、または Protobuf を使用している場合、プロデューサーのシリアライザーおよびコンシューマーのデシリアライザーもスキーマのキャッシュを保持します。ただし、スキーマは比較的小さいため、メモリー使用量に大きな影響が及ぶことはありません。

主に管理アクションや Kafka へのデータの生成に REST Proxy を使用する場合、メモリー要件は控えめで、ヒープサイズは 1 GB で十分です。多くのコンシューマーを使用する予定であれば、大雑把な計算を実行して、予想されるコンシューマーの最大数と、デフォルト構成を使用した場合のコンシューマーごとの平均メモリー使用量(~16 MB)に基づいて妥当なヒープサイズを決定できます。

CPU

REST Proxy の CPU 要件は、通常のクライアントの CPU 要件を反映しています。主要な演算処理コストは、メッセージの圧縮とシリアル化によって発生します。REST Proxy は多くのリクエストを同時に処理でき、使用可能な場合はより多くのコアを利用できます。少なくとも 16 のコアを使用することをお勧めします。これらのコアによって、HTTP リクエストを並行して処理するのに十分なリソースを用意し、プロデューサーおよびコンシューマーにバックグラウンドスレッドを提供できます。ただし、この数はワークロードに合わせて調整する必要があります。スループットが低いデプロイではコアの使用数が少なくて済む可能性がありますが、多くのコンシューマーを実行するプロキシでは、各コンシューマーが専用スレッドを持つため、コアの使用数を増やす必要があります。

ディスク

REST Proxy はディスクにステートを保存しません。ディスク使用量に影響するのは、log4j ログに必要なものに限られます。

ネットワーク

高速で信頼性の高いネットワークは、REST Proxy のパフォーマンスに最も大きな影響を与える可能性があります。このネットワークは、同じデータセンター内の Kafka クラスターのプロキシとしてのみ使用し、ZooKeeper と Kafka ブローカーの両方への低レイテンシのアクセスを保証する必要があります。大半の用途では、標準のデータセンターネットワーキング(1 GbE、10 GbE)であれば問題ありません。

Java 仮想マシン

最新バージョンの JDK 1.8 と G1 コレクターを実行するようお勧めします(無料で提供されている以前のバージョンには、セキュリティ上の脆弱性が見つかっています)。

現在 JDK 1.7(サポート対象)を使用していて、今後 G1(現在のデフォルト)の使用を検討している場合は、必ず u51 を適用してください。テスト時に u21 を試しましたが、そのバージョンの GC の実装に関して数多くの問題に遭遇しました。

弊社では、次のような GC チューニングを推奨しています。

-Xms1g -Xmx1g -XX:MetaspaceSize=96m -XX:+UseG1GC -XX:MaxGCPauseMillis=20 \
       -XX:InitiatingHeapOccupancyPercent=35 -XX:G1HeapRegionSize=16M \
       -XX:MinMetaspaceFreeRatio=50 -XX:MaxMetaspaceFreeRatio=80

多くのコンシューマーを使用するプロキシの場合は、1 GB のヒープサイズ設定を増やす必要があります。ただし、8 GB を超えるヒープサイズに設定する代わりに REST Proxy の複数のインスタンスを実行することをお勧めします。これは、リクエストのタイムアウトやコンシューマーの切断の原因となる、長期間にわたる GC 一時停止を回避するためです。ヒープサイズを設定するには、環境変数 KAFKAREST_HEAP_OPTS を使用します。

デプロイとロードバランシング

REST Proxy では、インスタンス間の調整は必要ないため、デプロイを簡単に拡大または縮小できます。各インスタンスに一意の id を設定することが、複数のインスタンスを作成するための唯一の要件です。

REST Proxy の複数のインスタンスを実行する場合、ロードバランシングのためのなんらかのメカニズムを提供する必要があります。最も単純なものは、起動時にアプリケーションプロセスごとに 1 つのインスタンスを選択し、そのインスタンスにすべてのトラフィックを送信するラウンドロビン DNS または検出サービスを使用するアプローチです。HTTP ロードバランサーを使用することもできますが、コンシューマー読み取り操作やオフセットコミット操作で使用するために返される絶対 URL をサポートするために、個々のインスタンスがアドレス指定可能でなければならない点は同じです。

一般的に、コンシューマーはステートフルであるため、ロードバランサーのスティッキーセッションが必要です。したがって、初期状態の REST プロキシで、ロードバランサーがスティッキーである必要があります。つまり、個々のコンシューマーインスタンスは常に同じ REST プロキシインスタンスにルーティングされる必要があります。

ただし、コンシューマーが最初のリクエストのみをロードバランサーに送信し、返されたホスト名を後続のリクエストで使用する場合は、ステートレス接続を使用できます。

高可用性

複数の REST Proxy インスタンスをデプロイする場合、失敗したコンシューマーインスタンスから返された例外をコンシューマークライアントが処理することが重要です。例外の発生後、クライアントはロードバランサーのアドレスを使用して新しいコンシューマーインスタンスの作成を試行して、アクティブな REST Proxy インスタンスに切り替える必要があります。前述したように、クライアントはコンシューマーインスタンスを作成した後に返された絶対 URL を使用してその後の通信を行います。複数の REST Proxy インスタンスを介してメッセージを生成する場合は、このレベルの例外処理は必要ありません。

クライアントは、終了する前にすべてのコンシューマーインスタンスを削除しようとします。これにより、REST Proxy 構成プロパティの consumer.instance.timeout.ms の値に基づいて消費遅延を引き起こすゾンビ状態のコンシューマーインスタンスを回避できます。デフォルトは 5 分(300,000 ミリ秒)です。クライアントの安定性によっては、この値を小さくしたほうがよい場合もあります。

重要な構成オプション

すべての構成オプションは、 こちら に記載されています。

ただし、本稼働環境では、一部の構成に変更を加える必要があります。構成によってはクラスターのレイアウトに依存するため、変更が 必須 となります。

bootstrap.servers

Kafka クラスターへの初期接続の確立に使用するための、ホストとポートのペアのリスト。クライアントは、ここでブートストラップ用に指定されるサーバーに関係なくすべてのサーバーを使用します。このリストは、すべてのサーバーを検出するために使用する最初のホストにのみ影響します。このリストは、host1:port1,host2:port2,... という形式にする必要があります。これらのサーバーは、初期接続ですべてのクラスターメンバーシップを検出するためにのみ使用されます。これは動的に変わる可能性があるので、このリストにすべてのサーバーセットを含める必要はありません。所定のサーバーの障害に備えて、複数指定しておくこともできます。

  • 型: list
  • デフォルト:
  • 指定可能な値:
  • 重要度: 高
schema.registry.url

シリアライザーで使われる Schema Registry のベース URL。

id

この REST サーバーインスタンスの一意の ID。これは、ID を指定しないコンシューマーの一意の ID を生成する際に使用されます。ID はデフォルトでは空です。これにより、単一のサーバーを容易にセットアップして実行させることができますが、自動生成のコンシューマー ID が使用されるマルチサーバーのデプロイにおいては安全ではありません。

  • 型: string
  • デフォルト: ""
  • 重要度: 高

その他の設定は、プロキシの正常性とパフォーマンスにとって重要です。ユースケースに応じてこれらを変更することを検討してください。

consumer.request.max.bytes

1 つのリクエストによって返されるメッセージキーおよび値の最大バイト数。値を小さくすると、1 つのコンシューマーで使用される最大メモリーが削減されるため、1 つの JSON ペイロードのデコードおよび処理に使用される最大メモリーが制限され、応答のストリーミングのデコードを実行できないクライアントにおいて効果があります。

反対に値を大きくすると、多数のメッセージを 1 つのリクエストでバッチ処理できるため、同じメッセージセットを消費するために必要な HTTP リクエスト(およびネットワークのラウンドトリップ)の数が減少するので、より効率的になる可能性があります。

max_bytes クエリパラメーターを使用して、リクエストごとにクライアントによってオーバーライドすることもできます。ただし、この設定は絶対最大値を制御します。この値を超える max_bytes 設定は無視されます。

  • 型: long
  • デフォルト: 67108864
  • 重要度: 中
fetch.min.bytes

consumer.request.timeout.ms のタイムアウトが経過するまでに 1 つのリクエストによって返されるメッセージキーおよび値の最小バイト数。

  • 型: int
  • デフォルト値: -1
  • 重要度: 中
consumer.request.timeout.ms

最大リクエストサイズにまだ達していない場合に、リクエストのメッセージを待機する最大合計時間。コンシューマーはタイムアウトを使用してバッチ処理を有効にします。値を大きくすると、コンシューマーはより長い時間待機できるようになり、より多くのメッセージが応答に含まれる可能性があります。ただし、この値は Kafka からのメッセージを消費する際のレイテンシの下限としても使用されます。コンシューマーに低レイテンシのメッセージ配信が必要な場合は、低い値を指定します。

  • 型: int
  • デフォルト: 1000
  • 重要度: 中
consumer.threads

コンシューマーリクエストを実行するスレッドの最大数。コンシューマーリクエストは、スレッドごとに 1 つずつ同期方式で実行されます。この値には、1 つのコンシューマーグループ内のコンシューマーの最大数よりも大きな値を設定する必要があります。そうしないと、バランス調整にデッドロックが発生します。

  • 型: int
  • デフォルト: 50
  • 重要度: 中
host.name

コンシューマーの絶対 URL を生成するために使用されるホスト名。空の場合、デフォルトの正規ホスト名が使用されます。ホストの FQDN を自動的に決定できない場合は、この値を設定する必要があります。

  • 型: string
  • デフォルト: ""
  • 重要度: 中

変更時に注意が必要な設定

以下の設定を変更すると、パフォーマンスが大幅に低下する可能性があります。これらは、パフォーマンスに関する重要なトレードオフを調整するために慎重に選択されています。これらを変更する必要がある場合、本稼働環境に導入する前に構成を徹底的にテストしてください。

consumer.iterator.backoff.ms

反復子がデータを使い果たしたときにバックオフする時間。コンシューマーに専用のワーカースレッドがある場合、これは事実上、リクエスト全体のタイムアウトにおける最大エラー値となります。厳密にタイムアウトを定める程度には小さく、ビジーの待機を避ける程度には大きくする必要があります。

  • 型: int
  • デフォルト: 50
  • 重要度: 低
consumer.iterator.timeout.ms

コンシューマー反復子操作のブロックのタイムアウト。これは、反復子で効果的に peek() を実行できるように十分小さい値に設定する必要があります。

  • 型: int
  • デフォルト: 1
  • 重要度: 低

デプロイ後

プロキシには永続的なステートはありませんが、コンシューマーインスタンスが特定のプロキシインスタンスに関連付けられているため、プロキシはステートフルです。コンシューマーグループの一部であるコンシューマーがプロキシのプロセスに含まれる場合、プロキシをシャットダウンまたは再起動すると、残りのコンシューマーに対してバランス調整操作が発生します。こうしたイベントは想定されるものであり、たとえば、ハードウェア障害やネットワークの停止が原因でインスタンスが孤立することで問題は発生しません。ただし、このバランス調整は即座に実施されるものではなく、すべての REST Proxy のローリング再起動によるアップデードなど、サイト全体のアップデートとして考慮する必要があることにオペレーターの方は注意してください。

新しいバージョンへのアップグレードは、永続的なステートがないため簡単です。前述のように、すべてのサーバーをローリング再起動するとバランス調整操作に十分な時間を確保でき、ダウンタイムなしのアップグレードを安全に実行できます。