マイクロ秒レベルのリバースプロキシ処理において、特定の条件下で 93% の性能劣化を引き起こし、にもかかわらず通常の監視指標は、見かけ上は「すべて正常」と示していました。これは典型的な監視の死角であり、システム全体のカスケード障害につながる潜在的なリスクです。本稿では、性能が 15 倍劣っていた状態から、目標とする 10% のオーバーヘッドまで最適化に至った、徹底的なチューニングについて解説します。単なる修正に留まらず、OpenResty XRay を用いて、コンパイラの動作や接続管理における根本的なボトルネックをいかに見抜くかに焦点を当てています。現代の高並列アーキテクチャにおいて、技術的な詳細を深く理解する能力が、インフラストラクチャの安定性の限界を直接的に決定します。

93% 性能低下の裏に潜む観測の死角

成熟したエンジニアリングチームにおいて、性能検証は通常、明確で体系化されたプロセスです。特にゲートウェイのような基盤コンポーネントの場合、そのアーキテクチャ(Client → OpenResty Gateway → Upstream)は、すでに十分に検証された確立されたパターンです。理論上、軽量な転送のみを行うゲートウェイであれば、その性能オーバーヘッドは予測可能で極めて低いものであるはずです。

しかし、新版ゲートウェイのベンチマークテストを実施した際、驚くべき性能劣化が確認されました。

  • アップストリームサービス基線 (直接接続テスト): 94,706 QPS
  • 新版ゲートウェイ (リバースプロキシ): 6,301 QPS

性能が 93% も消失したのです。単純なリバースプロキシが、まさか 15倍近くもの性能差を引き起こすとは。システムは一見すると正常で、エラーログもなく、設定も一般的な認識に合致していました。問題は明らかに、通常の観測範囲外に潜んでいたのです。

OpenResty XRay がコネクション再利用の問題を明らかにします

OpenResty XRay を使用してゲートウェイの CPU パフォーマンス分析を実施しました。その際、lj-c-on-cpu ツールを実行してフレームグラフを生成しました。

CPU フレームグラフは、connect()close() システムコールという、異常に幅の広い二つの領域によって占められていました。

高性能なネットワーク サービスでは、これら二つは通常、ほとんど目立たない「バックグラウンド ノイズ」であるべきです。それらが異常に顕著に現れていることは、致命的な問題を示唆しています。それは、アップストリーム コネクションが再利用されていないという点です。入ってくる各リクエストが、新しい TCP ハンドシェイクとティアダウンをトリガーしているのです。高並行な状況下では、これはまさに「コネクションの嵐」に他なりません。カーネルは、実際のデータ処理ではなく、コネクションの作成と破棄に膨大な時間を費やしてしまいます。

OpenResty XRay が提供する完全な呼び出しスタックにより、問題の根本原因を迅速に特定できました。

  • Connect パス: ngx_http_proxy_handlerngx_http_upstream_connectconnect()
  • Close パス: ngx_http_upstream_finalize_requestngx_close_connectionclose()

診断は明確でした。upstream 設定ブロックに keepalive ディレクティブが欠落していたのです。これは一見些細に見えますが、本番環境で雪崩のような壊滅的な影響を引き起こしかねない設定上の不備でした。

直ちにアップストリームの keepalive を有効にしたところ、効果は劇的でした。パフォーマンスは 6,301 QPS から 21,923 QPS へと、3.48 倍に向上しました!

Screenshot

connect()close() のスパイクは完全に解消されました。最初の性能ボトルネックは解決しましたが、物語はまだ終わりません。

比較分析により判明したコンパイルオプションの問題

性能は大幅に回復したものの、ベテランエンジニアの直感では、事態はそれほど単純ではないと感じられました。現在のバージョンの性能(21,923 QPS)を以前の安定版(24,115 QPS)と比較したところ、依然として約 10% の性能差があることが確認されました。

この 10% の差はどこに起因するのでしょうか?これは15倍もの大きな差ほど衝撃的ではありませんが、究極の性能を追求するインフラストラクチャにとって、原因不明の性能劣化は決して許容できるものではありません。

  • 現在のバージョンのフレームグラフは「より深く」なっている:呼び出しスタックの深さが安定版よりも明らかに増えています。
  • 特定の関数呼び出しが増加している:例えば、ngx_http_gunzip_body_filter のような関数がコールチェーンで頻繁に出現しますが、安定版ではほとんど見られません。

ngx_http_gunzip_body_filter のコードを確認します。

static ngx_int_t
ngx_http_gunzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
    int                     rc;
    ngx_uint_t              flush;
    ngx_chain_t            *cl;
    ngx_http_gunzip_ctx_t  *ctx;

    ctx = ngx_http_get_module_ctx(r, ngx_http_gunzip_filter_module);

    if (ctx == NULL || ctx->done) {
        return ngx_http_next_body_filter(r, in);
    }
}

理論的には、Nginx/OpenResty のフィルタチェーンは末尾呼び出し(tail-call)を多用しており、最適化コンパイル下では、これらの呼び出しはコンパイラによってフラット化され、ほとんど追加のオーバーヘッドがないはずです。しかし、現在のバージョンのフレームグラフの表示は、コンパイラの最適化が無効になっている可能性を示唆しています

コンパイラオプションが命令実行効率に与える影響

この手がかりをたどって、2つのバージョンのビルドスクリプトを確認したところ、以下の事実が判明しました。

現在の開発バージョンは -O0 コンパイルオプションを使用しています。

その理由は、開発プロセスにおいて、ある問題のデバッグを容易にするため、一時的にコンパイル最適化を無効化したためです。しかし、機能開発完了後、最適化コンパイルオプションへの復元を失念していました

-O0 は次のことを意味します:

  1. 関数インライン化の無効化 - 小さな関数呼び出しがインライン化されない
  2. 末尾呼び出し最適化の無効化 - 呼び出しスタックが深くなる
  3. 冗長コードの未削除 - より多くの不要な命令
  4. レジスタ使用効率の低下 - より多くのメモリアクセス

これら一見些細なコンパイラの動作の違いが積み重なることで、結果として、当社のパフォーマンスの10%を「奪って」いました。

コンパイルオプションを標準の -O2 に復元し、再構築およびデプロイを行いました。

その結果、性能は完全に回復し、安定バージョンと同水準に達しました。これにより、二つの性能問題は完全に解決されました。

15 倍 QPS の乖離から学ぶ技術的振り返り

今回、94K QPS から 6K QPS への性能低下を調査した結果、現代の複雑なシステムにおける二つの深刻な課題が浮き彫りになりました。

  1. 「隠れたコンテキスト」のドリフト:性能はコードロジックのみならず、ビルド時および実行時環境の「隠れたコンテキスト」——例えばコンパイルオプション、カーネルパラメータ、ランタイム設定——にも大きく依存します。これらのコンテキストの変化はコードレビューでは見過ごされがちですが、壊滅的な結果につながる可能性があります。今回の keepalive の欠如と -O0 コンパイルオプションがその最たる例です。

  2. 観測の死角を突破する:なぜ動的トレーシングが不可欠なのか?問題がカーネルやコンパイラのレベルにまで深く関わるようになると、従来のログやメトリクスでは情報が得られにくい(沈黙する)ことがしばしばあります。本事例は、システムレベルのボトルネックに直面した際、OpenResty XRay のような深い洞察を提供するツールが必要であることを証明しています。

    • エンドツーエンドサンプリング:アプリケーション層からカーネルまでを透過的に分析し、全体的なボトルネック(接続ストームなど)を迅速に特定します。
    • 異常パターン認識:フレームグラフ(Flame Graph)を通じて、予期せぬconnect/closeパスを迅速に特定します。
    • ベースライン比較:詳細な差異(コンパイラの挙動など)を特定します。

性能問題は、明確なバグとして現れるのではなく、白黒つけられない、曖昧な効率の低下として現れることが多いものです。複雑なシステムアーキテクチャにおいて、経験に基づく推測だけでは非効率的です。OpenResty XRay を通じて、アプリケーションロジックからカーネル呼び出しまでの完全な可観測性を構築することで、システム深くに隠された「グレーゾーン」を測定、分析、最適化する能力を獲得し、システムの長期的な安定性と卓越した性能を確保することが可能になります。

OpenResty XRay について

OpenResty XRay動的トレーシング製品であり、実行中のアプリケーションを自動的に分析して、パフォーマンスの問題、動作の問題、セキュリティの脆弱性を解決し、実行可能な提案を提供いたします。基盤となる実装において、OpenResty XRay は弊社の Y 言語によって駆動され、Stap+、eBPF+、GDB、ODB など、様々な環境下で複数の異なるランタイムをサポートしております。

著者について

章亦春(Zhang Yichun)は、オープンソースの OpenResty® プロジェクトの創始者であり、OpenResty Inc. の CEO および創業者です。

章亦春(GitHub ID: agentzh)は中国江蘇省生まれで、現在は米国ベイエリアに在住しております。彼は中国における初期のオープンソース技術と文化の提唱者およびリーダーの一人であり、Cloudflare、Yahoo!、Alibaba など、国際的に有名なハイテク企業に勤務した経験があります。「エッジコンピューティング」、「動的トレーシング」、「機械プログラミング」 の先駆者であり、22 年以上のプログラミング経験と 16 年以上のオープンソース経験を持っております。世界中で 4000 万以上のドメイン名を持つユーザーを抱えるオープンソースプロジェクトのリーダーとして、彼は OpenResty® オープンソースプロジェクトをベースに、米国シリコンバレーの中心部にハイテク企業 OpenResty Inc. を設立いたしました。同社の主力製品である OpenResty XRay動的トレーシング技術を利用した非侵襲的な障害分析および排除ツール)と OpenResty XRay(マイクロサービスおよび分散トラフィックに最適化された多機能ゲートウェイソフトウェア)は、世界中の多くの上場企業および大企業から高い評価を得ております。OpenResty 以外にも、章亦春は Linux カーネル、Nginx、LuaJITGDBSystemTapLLVM、Perl など、複数のオープンソースプロジェクトに累計 100 万行以上のコードを寄与し、60 以上のオープンソースソフトウェアライブラリを執筆しております。

翻訳

英語版の原文と日本語訳版(本文)をご用意しております。読者の皆様による他の言語への翻訳版も歓迎いたします。全文翻訳で省略がなければ、採用を検討させていただきます。心より感謝申し上げます!