OpenResty XRay がどのようにしてアプリケーションの問題特定と効率化を支援するかをご覧ください。

詳細はこちら LIVE DEMO

OpenResty や Nginx サーバーで Lua コードを実行することは、ノンブロッキングの Web サーバーに高いパフォーマンスと大きな柔軟性を求める人々の間で、今や非常に一般的になっています。Lua を使用して、リクエストヘッダーやレスポンスボディデータの確認や修正といった非常に単純なタスクを行う人もいれば、複雑な Web アプリケーション、CDN ソフトウェア、API ゲートウェイなどを作成する人もいます。Lua は、シンプルさ、メモリ使用量の少なさ、実行効率の高さで知られており、特に LuaJIT のようなジャストインタイム (JIT) コンパイラを使用する場合にその特徴が顕著です。しかし、OpenResty や Nginx サーバー上で実行される Lua コードが過剰な CPU リソースを消費することもあります。これは通常、プログラマーのコーディングエラー、高コストな C/C++ ライブラリコードの呼び出し、またはその他の理由によるものです。

稼働中の OpenResty や Nginx サーバーにおいて、すべての CPU パフォーマンスのボトルネックを迅速に特定する最良の方法は、OpenResty XRay 製品が提供する Lua 言語レベルの CPU フレームグラフサンプリングツールを使用することです。このツールは OpenResty や Nginx の対象プロセスに一切の変更を加える必要がなく、本番環境のプロセスに対して知覚可能な影響を与えることもありません。

本記事では、フレームグラフとは何か、そして Lua レベルの CPU フレームグラフとは何かを説明します。説明には、複数の小さく独立した Lua コードの例を用いて実演します。これらの例のフレームグラフを生成し、解説および分析するために OpenResty XRay を使用します。小さな例を選んだ理由は、様々なパフォーマンス分析の結果を予測し検証しやすいからです。同じ分析手法とツールは、最も複雑な Lua アプリケーションにも適用できます。過去数年間、我々はこの技術と可視化方法を使用して、多くの複雑なウェブサイトやアプリケーションを持つ企業クライアントを成功裏に支援してきました。

{<button xray “OpenResty XRay 製品でアプリケーションのパフォーマンスを向上させる” >}

フレームグラフとは何か

フレームグラフは、Brendan Gregg が発明した可視化手法で、特定のシステムリソースやパフォーマンス指標が、対象ソフトウェアのすべてのコードパスにどのように定量的に分布しているかを示すものです。

ここでいう「システムリソース」や指標には、CPU 時間、off-CPU 時間、メモリ使用量、ディスク使用量、遅延時間、あるいはその他考えられるあらゆるリソースが含まれます。

「コードパス」は、対象ソフトウェアコード内の呼び出しスタックトレースとして定義できます。 呼び出しスタックトレースは通常、一連の関数呼び出しフレームで構成され、GDB コマンド bt の出力や、Python や Java プログラムの例外エラーメッセージなどに見られます。以下は Lua の呼び出しスタックトレースの例です:

C:ngx_http_lua_ngx_timer_at
at
cache.lua:43
cache.lua:record_timing
router.lua:338
router.lua:route
v2_routing.lua:1214
v2_routing.lua:route
access_by_lua.lua:130

この例では、Lua スタックは基底フレーム access_by_lua.lua:130 から最上位フレーム C:ngx_http_lua_ngx_timer_at まで成長しています。これは、異なる Lua または C 関数がどのように相互に呼び出されているかを明確に示し、「コードパス」の近似表現を形成しています。

上記の「すべてのコードパス」という表現は、統計的な観点から見たものであり、実際にプログラム内のすべてのコードパスを列挙したり走査したりすることを意味するものではありません。後者は組み合わせ爆発の問題により、現実的には非常にコストが高くなります。我们の目標は、コストが小さくないすべてのコードパスが我々のグラフに現れる機会を確保し、十分に小さな誤差でそれらのコストを定量化できるようにすることです。

本文では、特定のタイプのフレームグラフに焦点を当てます。このタイプのフレームグラフは、CPU 時間(または CPU リソース)がすべての Lua コードパスにどのように定量的に分布しているかを示すために特別に設計されています。特に、ここでは OpenResty または Nginx ターゲットプロセス内の Lua コードのみに注目します。自然に、このタイプのフレームグラフは「Lua レベル CPU フレームグラフ」(Lua-land CPU Flame Graphs)と名付けられています。

本文のタイトル画像はフレームグラフの例であり、後続のセクションでさらに多くの例を提供します。

なぜフレームグラフが必要か

フレームグラフは、ターゲットソフトウェアがどれほど複雑であっても、1枚の小さな画像ですべてのパフォーマンスボトルネックの全体像を定量的に示すことができます。

従来のパフォーマンス分析ツールは、通常ユーザーに大量の詳細情報とデータを提示しますが、ユーザーは全体像を把握しにくく、重要でない部分の最適化に時間と労力を費やしてしまい、明確な効果が見られないことがよくあります。従来の分析ツールのもう一つの欠点は、各関数呼び出しの遅延を個別に表示することが多く、関数呼び出しの文脈を理解しにくいことです。また、ユーザーは現在の関数自体の実行時間(exclusive time)と、他の関数の呼び出しを含む総時間(inclusive time)を意識的に区別する必要があります。

一方、フレームグラフは大量の情報を比較的固定サイズの画像に圧縮することができます(通常、1 画面で全体を表示できます)。重要度の低いコードパスは自然と薄くなったり消えたりし、本当に重要なコードパスが自然と浮き彫りになります。重要なものほど、より顕著に表示されます。フレームグラフは常にユーザーに最適な量の情報を提供し、多すぎず、少なすぎることもありません。

フレームグラフの読み方

初心者にとって、フレームグラフを正しく解釈するのは難しいかもしれません。しかし、簡単な説明を通じて、ユーザーはフレームグラフが実際には非常に直感的で理解しやすいことに気づくでしょう。フレームグラフは 2 次元の図です。y 軸はコード(またはデータ)のコンテキスト、例えばターゲットプログラミング言語の呼び出しスタックトレースを示し、x 軸は各呼び出しスタックが占めるシステムリソースの割合を示します。x 軸全体は通常、ターゲットソフトウェアが消費する 100% のシステムリソース(例えば CPU 時間)を表します。x 軸上の各呼び出しスタックトレースの順序は通常重要ではありません。これらの呼び出しスタックは関数フレーム名のアルファベット順に並べられているだけだからです。もちろん、例外もあります。例えば、筆者が発明した時系列フレームグラフでは、x 軸が実際には時間軸となり、呼び出しスタックの順序が時間順序となります。本文では、古典的なフレームグラフタイプ、つまり図の x 軸上の順序が重要でないものに焦点を当てて議論します。

フレームグラフの読み方を学ぶ最良の方法は、実際のフレームグラフサンプルを解釈してみることです。以下のセクションでは、OpenResty と Nginx サーバー上で実行される Lua アプリケーションに関する複数のフレームグラフ例を提供し、詳細な説明を加えます。

簡単な Lua サンプル

このセクションでは、明確なパフォーマンス特性を持つ簡単な Lua サンプルプログラムをいくつか紹介し、OpenResty XRay を使用して実際の nginx プロセスを分析し、Lua レベルの CPU フレームグラフを生成して、グラフに表示されるパフォーマンス状況を検証します。JIT 即時コンパイルが有効な Lua コード、JIT コンパイルが無効な Lua コード(つまり解釈実行される)、外部 C ライブラリコードを呼び出す Lua コードなど、さまざまなケースを検討します。

JIT コンパイルされた Lua コード

まず、JIT 即時コンパイルが有効な Lua サンプルプログラムを検討します(LuaJIT はデフォルトで JIT が有効です)。

以下の独立した OpenResty 小規模アプリケーションを考えてみましょう。このセクションでは一貫してこの例を使用しますが、異なる状況の議論に応じて、この例に少し修正を加えることがあります。

まず、このアプリケーションのディレクトリ構造を準備します:

mkdir -p ~/work
cd ~/work
mkdir conf logs lua

次に、以下のような conf/nginx.conf 設定ファイルを作成します:

master_process on;
worker_processes 1;

events {
    worker_connections 1024;
}

http {
    lua_package_path "$prefix/lua/?.lua;;";

    server {
        listen 8080;

        location = /t {
            content_by_lua_block {
                require "test".main()
            }
        }
    }
}

location /t の Lua ハンドラーでは、test という名前の外部 Lua モジュールを読み込み、そのモジュールの main 関数を即座に呼び出しています。lua_package_path 設定ディレクティブを使用して、lua/ ディレクトリを Lua モジュールの検索パスリストに追加しています。これは、先ほど言及した test Lua モジュールファイルを lua/ ディレクトリに配置するためです。

この test Lua モジュールは lua/test.lua ファイルで定義されています:

local _M = {}

local N = 1e7

local function heavy()
    local sum = 0
    for i = 1, N do
        sum = sum + i
    end
    return sum
end

local function foo()
    local a = heavy()
    a = a + heavy()
    return a
end

local function bar()
    return (heavy())
end

function _M.main()
    ngx.say(foo())
    ngx.say(bar())
end

return _M

ここでは、計算量の多い Lua 関数 heavy() を定義しており、1 から 1000 万(1e7)までの数字の合計を計算します。そして、foo() 関数内で heavy() 関数を 2 回呼び出し、bar() 関数内では heavy() 関数を 1 回だけ呼び出しています。 最後に、モジュールのエントリーポイントである _M.main() 関数で foobar をそれぞれ 1 回ずつ呼び出し、ngx.say を使用してそれらの戻り値を HTTP レスポンスボディに出力しています。

明らかに、この Lua ハンドラーでは、foo() 関数が消費する CPU 時間は bar() 関数の 2 倍になるはずです。なぜなら、foo() 関数は heavy() 関数を 2 回呼び出しているのに対し、bar() は 1 回しか呼び出していないからです。以下で OpenResty XRay によってサンプリングされた Lua レベルの CPU フレームグラフを見ることで、この観察結果を簡単に検証できます。

このサンプルでは、LuaJIT の JIT コンパイラーオプションには触れていないため、JIT コンパイルはデフォルトの有効状態で使用されています。また、最新の OpenResty プラットフォームバージョンでは常に LuaJIT のみを使用しています(標準 Lua 5.1 インタープリターのサポートは既に削除されています)。

これで、以下のコマンドで OpenResty アプリケーションを起動できます:

cd ~/work/
/usr/local/openresty/bin/openresty -p $PWD/

OpenResty がシステムの /usr/local/openresty/ ディレクトリにインストールされていると仮定します(これはデフォルトのインストール場所です)。

OpenResty アプリケーションをビジー状態にするために、abweighttp などのベンチマークツールを使用して URI http://localhost:8080/t にリクエスト負荷をかけるか、OpenResty XRay 製品に付属の負荷生成ツールを使用することができます。どの方法を使用しても、対象の OpenResty アプリケーションの nginx ワーカープロセスがアクティブな状態を維持している間、OpenResty XRay の Web コンソールで以下のような Lua レベルの CPU フレームグラフを得ることができます:

グラフから以下の観察結果が得られます:

  1. グラフ内のすべての Lua コールスタックは、同じエントリーポイント content_by_lua(nginx.conf:24) から発生しています。これは予想通りです。

  2. グラフには主に 2 つのコードパスが表示されています:

    content_by_lua -> test.lua:main -> test.lua:bar -> test.lua:heavy -> trace#2:test.lua:8
    

    及び

    content_by_lua -> test.lua:main -> test.lua:foo -> test.lua:heavy -> trace#2:test.lua:8
    

    この 2 つのコードパスの唯一の違いは、中間の foo 関数フレームと bar 関数フレームです。これも予想外ではありません。

  3. 左側の bar 関数に関連するコードパスの幅は、右側の foo に関連するコードパスの幅の半分です。 言い換えると、これら 2 つのコードパスのグラフの x 軸上の幅の比率は 1:2 で、bar コードパスが消費する CPU 時間は foo コードパスの 50% にすぎません。マウスをグラフ内の test.lua:bar フレーム(つまり四角形)に移動すると、それが全サンプル数(つまり総 CPU 時間)の 33.3% を占めていることがわかります。一方、test.lua:foo は 66.7% を占めています。明らかに、以前の予測と比較すると、このフレームグラフが提供する比率の数字は非常に正確です。サンプリングと統計分析の手法を採用しているにもかかわらずです。

  4. グラフ内に ngx.say() などの他のコードパスが見られないのは、それらが heavy() を呼び出す 2 つの Lua コードパスと比較して、消費する CPU 時間が無視できるほど小さいためです。フレームグラフでは、そのような取るに足らないコードパスは単なる小さなノイズであり、私たちの注目を引くことはありません。常に本当に重要な部分に集中でき、他のものに気を取られることはありません。

  5. 2 つのホットコードパス(つまりコールスタックトレース)の最上位フレームは完全に同じで、どちらも trace#2:test.lua:8 です。 これは実際の Lua 関数呼び出しフレームではなく、JIT コンパイルされた Lua コードパスを実行中であることを示す「疑似関数フレーム」です。LuaJIT の用語では、このパスは「trace」と呼ばれます(LuaJIT はトレーシング JIT コンパイラーの一種であるため)。この「trace」の番号は 2 で、対応するコンパイルされた Lua コードパスは test.lua ファイルの 8 行目から始まります。test.lua:8 が指す Lua コード行は次のとおりです:

            sum = sum + i
    

私たちは、この非侵襲的なサンプリングツールが、外部モジュールを使用せず、修正されておらず、特別なコンパイルオプションも使用していない標準の OpenResty バイナリから、このように正確なフレームグラフを生成できることを喜ばしく思います。このツールは、LuaJIT ランタイムの特別な機能やインターフェースを一切使用せずLUAJIT_USE_PERFTOOLS 機能や LuaJIT の組み込みプロファイラさえも使用していません。代わりに、このツールは先進的な動的トレース技術を使用し、元のターゲットプロセスから既存の情報のみを読み取ります。JIT コンパイルされた Lua コードからでさえ、十分に有用な情報を取得することができます。

インタープリタ実行される Lua コード

インタープリタ実行される Lua コードは通常、最も完璧なコールスタックトレースとフレームグラフサンプルを生成します。 私たちのサンプリングツールが JIT コンパイルされた Lua コードを正しく処理できるのであれば、インタープリタ実行される Lua コードの分析はさらに効果的です。 LuaJIT には JIT コンパイラとインタープリタの両方があります。興味深いことに、そのインタープリタはほぼ完全に手書きのアセンブリコードで実装されています(もちろん、LuaJIT は独自のアセンブリ言語記法である DynASM を導入しています)。

これまで使用してきた Lua サンプルプログラムに対して、ここで少し修正を加える必要があります。具体的には、server {} 設定ブロック内に以下の nginx.conf 設定フラグメントを追加します:

    init_by_lua_block {
        jit.off()
    }

その後、サーバープロセスをリロードまたは再起動し、トラフィック負荷を維持します。

今回、以下のような Lua レベルの CPU フレームグラフが得られました:

この新しいグラフは、前のグラフと以下の点で非常に類似しています:

  1. 依然として、bar コードパスと foo コードパスという2つの主要なコードパスのみが見られます。

  2. bar コードパスは依然として全 CPU 時間の約 3 分の 1 を占め、foo は残りのすべて(約 3 分の 2)を占めています。

  3. グラフに表示されているすべてのコードパスのエントリーポイントは content_by_lua フレームです。

しかし、このグラフには前のグラフと比較して重要な違いがあります:コードパスの最上位フレームが “trace” 疑似フレームではなくなっています。 この変化は予想通りです。今回は JIT コンパイルされた Lua コードパスがないため、コードパスの最上位または最上位フレームが lj_BC_IFORLlj_BC_ADDVV などの関数フレームになっています。C: プレフィックスでマークされたこれらの C 関数フレームは実際には C 言語関数ではなく、アセンブリコードフレームに属し、各 LuaJIT バイトコードを実装するアセンブリルーチンに対応しており、lj_BC_IFORL などのシンボルとしてマークされています。当然、lj_BC_IFORLLuaJIT バイトコード命令 IFORL を実装するために使用され、lj_BC_ADDVV はバイトコード命令 ADDVV のために使用されます。 IFORL は Lua コードの for ループのインタープリタ実行に使用され、ADDVV は算術加算に使用されます。これらのバイトコードの出現は、私たちの Lua 関数 heavy() の実装方法と一致しています。さらに、lj_meta_arithlj_vm_foldarith などの補助的なアセンブリルーチンも見ることができます。

これらの関数フレームの比率を観察することで、LuaJIT 仮想マシンとインタープリタ内部での CPU 時間の分布を垣間見ることができ、この仮想マシンとインタープリタ自体の最適化への道を開くことができます。

外部 C/C++ 関数の呼び出し

Lua コードが外部 C/C++ ライブラリ関数を呼び出すことは一般的です。私たちは Lua レベルの CPU フレームグラフを通じて、これらの外部 C 関数が占める CPU 時間の割合を理解したいと考えています。結局のところ、これらの C 言語関数呼び出しも Lua コードによって開始されるものです。 これこそが動的トレースに基づくパフォーマンス分析の真の強みです:これらの外部 C 言語関数呼び出しがパフォーマンス分析においてブラックボックスになることは決してありません1

これまで使用してきた Lua サンプルは、ここでも少し修正が必要です。具体的には、heavy() という Lua 関数を以下のように変更する必要があります:

local ffi = require "ffi"
local C = ffi.C

ffi.cdef[[
    double sqrt(double x);
]]

local function heavy()
    local sum = 0
    for i = 1, N do
        -- sum = sum + i
        sum = sum + C.sqrt(i)
    end
    return sum
end

ここでは、LuaJIT の FFI API を使用して、まず標準 C ライブラリ関数 sqrt() を宣言し、Lua 関数 heavy() 内部で直接この C ライブラリ関数を呼び出しています。これは対応する Lua レベルの CPU フレームグラフに表示されるはずです。

今回、以下のようなフレームグラフが得られました:

興味深いことに、確かに 2 つの主要な Lua コードパスの頂点に C 言語関数フレーム C:sqrt が見られます。 また注目すべきは、頂点付近に依然として trace#N のような疑似フレームが見られることです。これは FFI を通じて C 関数を呼び出す Lua コードも JIT コンパイルが可能であることを示しています(今回は init_by_lua_block ディレクティブから jit.off() ステートメントを削除しました)。

コード行レベルのフレームグラフ

上記で示したフレームグラフは実際には関数レベルのフレームグラフです。これらのフレームグラフに表示されているすべての呼び出しフレームには関数名のみが含まれており、関数呼び出しを行ったソースコード行の情報は含まれていません。

幸いなことに、OpenResty XRay の Lua レベルパフォーマンス分析ツールは、コード行レベルのフレームグラフの生成をサポートしており、Lua ソースコード行のファイル名と行番号をグラフに追加します。これにより、ユーザーは大きな Lua 関数本体内で特定の Lua ソースコード行を直接特定することができます。以下は、これまで使用してきた Lua サンプルプログラムに対応する Lua コード行レベルの CPU フレームグラフです:

各関数フレームの上に、ソースコード行の疑似フレームが追加されているのがわかります。例えば、main 関数が含まれる test.lua ソースファイルの 32 行目の Lua コードで foo() 関数が呼び出されています。また、foo() 関数が含まれる test.lua:22 の行で heave() 関数が呼び出されています。

コード行レベルのフレームグラフは、最もホットな Lua ソースコード行と Lua ステートメントを正確に特定するのに非常に役立ちます。対応する Lua 関数本体が大きい場合、コード行レベルのフレームグラフはコード行の位置を特定する時間を大幅に節約するのに役立ちます。

マルチプロセス

マルチコア CPU システムでは、単一の OpenResty または Nginx サーバーインスタンスに複数の nginx ワーカープロセスを設定するのが一般的です。 OpenResty XRay の分析ツールは、指定されたプロセスグループ内のすべてのプロセスを同時にサンプリングすることをサポートしています。入ってくるトラフィックがそれほど多くなく、任意の 1 つまたは複数の nginx ワーカープロセスに分散される可能性がある場合、このようなプロセスグループ全体のサンプリング分析は非常に実用的です。

複雑な Lua アプリケーション

非常に複雑な OpenResty/Lua アプリケーションからも Lua レベルの CPU フレームグラフを得ることができます。 例えば、以下の Lua レベル CPU フレームグラフは、OpenResty Edge 製品を実行している「ミニ CDN」サーバーのサンプリングから得られたものです。これは複雑な Lua アプリケーションで、完全に動的な CDN ゲートウェイ、地理感応型 DNS 権威サーバー、Web アプリケーションファイアウォール(WAF)を含んでいます:

我们的迷你-CDN 服务器的 Lua-land CPU 火焰图

グラフから、Web アプリケーションファイアウォール(WAF)が最も多くの CPU 時間を占めており、組み込みの DNS サーバーも大きな CPU 時間を占めていることがわかります。私たちが世界中に展開している「ミニ CDN」ネットワークは、openresty.orgopenresty.com などの自社運営の複数のウェブサイトにセキュリティと高速化のサポートを提供しています。

また、Kong などの OpenResty ベースの API ゲートウェイソフトウェアも分析することができます。

サンプリングのオーバーヘッド

私たちはフルインストルメンテーションではなく、サンプリングベースの手法を使用しているため、Lua レベルの CPU フレームグラフを生成するためのランタイムオーバーヘッドは通常無視できる程度です。データ量と CPU 消費の両方が非常に小さいため、このようなツールは本番環境やオンライン環境に非常に適しています。

固定レートのリクエストで nginx ターゲットプロセスにアクセスし、同時に Lua レベルの CPU フレームグラフツールが集中的にサンプリングを行っている場合、ターゲットプロセスの CPU 使用率の時間変化は以下のようになります:

この CPU 使用率の変化曲線も OpenResty XRay によって自動的に生成・レンダリングされています。

ツールのサンプリングを停止した後も、同じ nginx ワーカープロセスの CPU 使用量曲線は非常に類似しています:

目視では、前後の2つの曲線の違いをほとんど見分けることができません。 したがって、ツールによる分析とサンプリングのオーバーヘッドは実際に非常に低いことがわかります。

ツールがサンプリングを行っていない時は、ターゲットプロセスのパフォーマンスへの影響は厳密にゼロです。結局のところ、ターゲットプロセスにカスタマイズや変更を加える必要はありません。

セキュリティ

動的トレース技術を使用しているため、ターゲットプロセスの状態を一切変更せず、1 ビットの情報さえも修正しません2。 これにより、ターゲットプロセスがサンプリング中であっても、サンプリングされていない時でも、その動作が(ほぼ)完全に同一であることが保証されます。これはターゲットプロセス自体の信頼性(予期せぬ動作の変化やプロセスのクラッシュがない)を確保し、その動作が分析ツールの存在によって影響を受けないことを保証します。 ターゲットプロセスの挙動は全く変化せず、まるで生きている動物のX線写真を撮影するようなものです。

従来のアプリケーションパフォーマンス管理(APM)製品では、ターゲットソフトウェアに特別なモジュールやプラグインをロードすることを要求したり、ターゲットソフトウェアの実行ファイルやプロセス空間に強制的にパッチを当てたり、独自のマシンコードやバイトコードを注入したりすることがあり、これらはユーザーシステムの安定性と正確性に深刻な影響を与える可能性があります。

これらの理由から、私たちのツールは本番環境に安全に適用でき、オフライン環境では再現が難しい問題を分析することができます。

互換性

OpenResty XRay 製品が提供する Lua レベルの CPU フレームグラフのサンプリングツールは、LuaJITGC64 モード または非 GC64 モードをサポートしており、また任意の OpenResty や Nginx のバイナリプログラムをサポートしています。これには、ユーザーが任意のビルドオプションで自身でコンパイルした、最適化されたまたは最適化されていないバイナリプログラムも含まれます。

OpenResty XRay は、Docker や Kubernetes コンテナ内で実行されている OpenResty および Nginx サーバープロセスを透過的に分析し、完璧な Lua レベルの CPU フレームグラフを生成することもできます。問題は一切ありません。

私たちのツールは、restyluajit コマンドラインツールで実行されるコンソールベースのユーザー Lua プログラムも分析することができます。

また、2.6.32 カーネルを使用する CentOS 6 のような古い Linux オペレーティングシステムとカーネルもサポートしています。

その他の種類の Lua レベルのフレームグラフ

前述の通り、フレームグラフは CPU 時間だけでなく、任意のシステムリソースやパフォーマンス指標を可視化するために使用できます。 そのため、当社の OpenResty XRay 製品では、他の種類の Lua レベルのフレームグラフも提供しています。例えば、off-CPU フレームグラフ、ガベージコレクション(GC)オブジェクトサイズとデータ参照パスのフレームグラフ、新規 GC オブジェクト割り当てフレームグラフ、Lua コルーチン yield 時間フレームグラフ、ファイル I/O 遅延フレームグラフなどがあります。

これらの異なる種類のフレームグラフについては、当社のブログサイトで詳細に紹介する予定です。

結論

本記事では、任意のソフトウェアシステムのパフォーマンスを直感的に分析できる非常に実用的な可視化手法であるフレームグラフについて紹介しました。 特に、その中の一種である Lua レベルの CPU フレームグラフについて詳しく説明しました。このタイプのフレームグラフは、OpenResty や Nginx サーバー上で実行される Lua アプリケーションの分析に使用できます。単純なものから複雑なものまで、複数の Lua サンプルプログラムを分析し、同時に OpenResty XRay で生成された対応する Lua レベルの CPU フレームグラフを使用して、動的トレースツールの威力を示しました。最後に、サンプリング分析のパフォーマンスオーバーヘッド、およびオンライン使用時の安全性と信頼性について検証しました。

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 以上のオープンソースソフトウェアライブラリを執筆しております。

翻訳

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


  1. 同様に、仮想マシン内のプリミティブルーチンも分析のブラックボックスにはなりません。したがって、仮想マシン自体のパフォーマンス分析も同時に行うことができます。 ↩︎

  2. Linux カーネルの uprobes メカニズムは、安全性を確保しつつ、ターゲットプロセス内の少数のマシン命令のメモリ状態を軽微に変更して透明かつ安全な動的プローブを実現しますが、この変更はターゲットプロセスにとって完全に透明です。 ↩︎