本文へ移動

7. CPU使用率

スレッド、CPUアフィニティ、飽和、プロファイリング、性能特性
警告

バージョンについて:この章には旧版のマルチプロセス運用に関する説明が残っています。HAProxy 2.5以降、nbprocとbind processはサポートされていません。この版では、グローバル設定 のnbthread、thread-groups、cpu-mapでスレッドを設定してください。

HAProxyは通常、ほとんどの時間をシステム内で費やし、ユーザー空間で費やす時間はそれより短くなります。適切に調整された3.5 GHzのCPUは、1コアのCPU使用率100%で、エンドツーエンドの接続確立と終了を毎秒約80000回処理できます。1コアが飽和している場合の典型的な内訳は、次のとおりです。

  • 長時間のTCP接続や大きなHTTPオブジェクトでは、システム95%、ユーザー5%
  • 短時間のTCP接続やcloseモードの小さなHTTPオブジェクトでは、システム85%、ユーザー15%
  • keep-aliveモードの小さなHTTPオブジェクトでは、システム70%、ユーザー30%

ルール処理や正規表現の量が増えると、ユーザー空間の割合が増えます。一方、システム内のファイアウォールルール、接続追跡、複雑なルーティングテーブルは、システムの割合を増やします。

ほとんどのシステムでは、ネットワーク転送中に観測されるCPU時間は4つに分けられます。

  • 割り込み:I/Oを受信してから、対象プロセスが判明するまでに行われるすべての処理です。通常、Rxパケットは割り込みに計上されます。Linuxなど、割り込み処理を専用スレッドに遅延できるシステムでは、softirqとして表示される場合があり、スレッド名はksoftirqd/0(CPU 0の場合)です。この負荷を処理するCPUは通常、ハードウェアの設定で決まりますが、softirqでは処理先を別のCPUに変更できることがよくあります。この割り込み部分は、どのプロセスにも関連付けられないため、余分な負荷と見なされがちですが、実際にはプロセスの仕事を準備するための処理です。

  • システム:ユーザー空間から呼び出されたカーネルコードで行われるすべての処理です。たとえば、システムコールはシステムに計上されます。同期的に送出されるTxパケットは、すべてシステム時間に計上されます。キューが満杯になって一部のパケットを遅延させる必要がある場合は、後で割り込みコンテキストで処理されることがあります(TCPウィンドウを広げるACKを受信したときなど)。

  • ユーザー:ユーザー空間でアプリケーションコードだけを実行する部分です。HAProxyはシステムコールを多用しますが、それ自体はこの部分だけで動作します。ルール処理、正規表現、圧縮、暗号化はいずれもCPU消費のユーザー部分を増やします。

  • アイドル:CPUに処理すべきことがない状態です。たとえば、HAProxyが着信接続を待っている場合や、データが送出されるのを待っている場合です。後者では、システムはそのデータを送るためにクライアントからのACKを待っています。

実際のHAProxyの動作については、割り込み/softirqはカーネルドライバーでのRx処理、ユーザー空間はHAProxyでのレイヤー7処理、システム時間はTx経路のネットワーク処理によるものと考えると、厳密にはまったく正しくありませんが、一般にはおおむね実態を捉えられます。

HAProxyはイベントループを中心に動作するため、poll()(または代替機構)で新しいイベントを待ち、受け取ったイベントをできるだけ速くすべて処理してから、再びpoll()に戻って次のイベントを待ちます。poll()で待機した時間と、イベントの処理に費やした時間を測定します。合計時間に対するポーリング時間の比率を"idle"時間と呼び、何かが起こるのを待っていた時間の割合を表します。この比率は、統計ページでは"idle"行、CLIでは"Idle_pct"に表示されます。100%に近ければ、負荷が極めて低いことを意味します。0%に近ければ、常に何らかの処理があることを意味します。過負荷のシステムでは、ほかのプロセスがhaproxyプロセスからCPUを奪う場合があるため、あまり正確にはなりません。それでも、HAProxy自身がどの程度仕事をしていると認識しているかを把握する有用な目安になります。負荷が低いのにアイドル比率も低い場合は、非常に処理コストの高いルールなどにより、HAProxyに多くの仕事がある可能性を示します。逆に、動作が遅いのにHAProxyのアイドル比率が100%に近い場合は、すでに処理するデータの到着を待っている状態であり、HAProxy側ではそれ以上高速化できないことを意味します。次の例では、haproxyは完全にアイドル状態です。

$ echo "show info" | socat - /var/run/haproxy.sock | grep ^Idle
Idle_pct: 100

アイドル比率が非常に低くなり始めたら、すべてのタスクにできるだけ多くのCPUリソースを残すため、システムを調整し、プロセスと割り込みを適切に配置することが重要です。ファイアウォールがある場合は、それが性能制限の大きな原因になっていないかを確認するため、無効化や調整を試す価値があります。ステートフルファイアウォールはRxとTxの両方の経路で動作するため、アンロードすると通常、割り込み/softirqとシステム使用率の両方が減少します。Linuxでは、nf_conntrackとip_conntrackモジュールをアンロードすると、改善の余地があるか確認できます。改善する場合、モジュールはデフォルト設定で動作しているため、性能を向上させる調整方法を調べる必要があります。一般には、ハッシュテーブルのサイズを大幅に増やします。FreeBSDでは、“pfctl -d"で"pf"ファイアウォールとそのステートフルエンジンを同時に無効にできます。

割り込み/softirqに多くの時間を費やしている場合は、それらとHAProxyが同じCPUで動作しないようにすることが重要です。ほとんどのシステムは、ネットワークトラフィックを受信するCPUにタスクを固定する傾向があります。特定のワークロードでは、そのほうが性能が向上するためです。しかし、ネットワークが大きな制約になるワークロードでは逆効果で、haproxyプロセスがカーネル側の処理とCPUを奪い合うことになります。haproxyを1つのCPUコアに、割り込みを別のコアに固定し、両方が同じL3キャッシュを共有するようにすると、ネットワーク性能は大きく向上する傾向があります。実際には、haproxyとネットワークスタックの仕事量はかなり近く、それぞれがほぼCPU全体を使い切れるためです。Linuxでは、haproxyにはtaskset、またはhaproxy設定のcpu-mapを使い、割り込みは/proc/irq配下で割り当てます。多くのネットワークインターフェースは複数のキューと割り込みをサポートしています。一般には、同じL3キャッシュを共有する少数のCPUコアに分散すると効果があります。この種のワークロードではirq_balanceは常に最悪の配置を行うため、必ず停止してください。

大量のSSLトラフィックや圧縮によりCPUが制約になるワークロードでは、特定のタスク専用の複数プロセスを使う価値がある場合があります。ただし、普遍的な規則はないため、実験が必要です。

CPU処理能力を増やすため、globalセクションの"nbproc"ディレクティブでHAProxyを複数のプロセスとして動作させることができます。ただし、いくつか制限があります。

  • ヘルスチェックはプロセスごとに実行されるため、対象サーバーは実行中のプロセス数と同じ数のチェックを受けます。
  • maxconnの値とキューはプロセスごとに適用されるため、サーバーに過負荷をかけないよう、正しい値を設定しなければなりません。
  • 競合を避けるため、送信接続でのポート範囲の使用は避けることを推奨します。
  • スティックテーブルはプロセスごとに存在し、プロセス間で共有されません。
  • 各peersセクションは、一度に1つのプロセスでしか実行できません。
  • CLI操作は、一度に1つのプロセスにのみ作用します。

これらを考慮すると、最も簡単な構成は、多くの場合、負荷の高い処理を担当する第1層を複数プロセスで動作させ、単一プロセスで動作する第2層にトラフィックを渡すものです。この仕組みは、CPU負荷の高い2つの機能であるSSLと圧縮に適しています。インスタンスは、UNIXソケット(TCPソケットより低コストで、ポートを消費しません)と、次の段階へクライアント情報を渡すのに便利なproxyプロトコルを使って簡単に連結できます。この場合は通常、単一プロセスのタスクをすべてプロセス番号1に、追加のタスクをそれ以降のプロセスに割り当てるとよいでしょう。異なるマシン向けに同様の設定を生成しやすくなります。

Linuxバージョン3.9以降では、マルチプロセスモードでHAProxyを動かす際、各プロセスが同じIP:port上で個別のリッスンソケットを使うと、はるかに効率的です。カーネルがすべてのプロセスを起こす代わりに、全プロセスに負荷を均等に分散するようになるためです。詳細は、設定マニュアルの"bind"キーワード行の"process"オプションを確認してください。