OpenResty アプリケーションの開発・保守において、以下のような悩ましい状況に直面することが多いのではないでしょうか。Lua コード内のエラー追跡が困難であること、複雑なコールスタックに潜むパフォーマンスのボトルネック、本番環境で散発的に発生し開発環境では再現がほぼ不可能な問題、そしてアプリケーションがクラッシュした際に断片的なエラー情報しか残されず、完全なコンテキストを把握できない等、枚挙にいとまがありません。従来のデバッグツールでは多くの場合、有効な手立てを打つことが困難です。一度重要なタイミングを逃してしまうと、再びゼロからやり直すしかなく、非効率であるばかりか、問題の本質を見逃しがちになります。

UDB (Undo Debugger) と OpenResty XRay の強力な組み合わせにより、私たちはついにプログラムの実行履歴を自在に行き来する能力を手に入れることができるようになりました。それは、あたかもデバッグの世界における「タイムマシン」を手に入れたかのようです。メモリ割り当てパターンの追跡、特定リクエストの実行パスの分析、あるいはクラッシュを引き起こしたコード断片の特定など、どのような課題であっても、この革命的なデバッグ手法は、従来のツールでは捉えることのできなかった全体像(パノラマビュー)を可視化します。本稿では、OpenResty アプリケーションにおける実際のケーススタディを通じて、この画期的な技術が私たちの問題解決アプローチをいかに変革するかをご紹介いたします。

OpenResty XRay と UDB の連携による効率化

UDB は OpenResty アプリケーションにおける Lua コードをいかに解明するか

UDB は Undo 社が提供するタイムトラベルデバッグツールであり、開発チームにこれまでにない可視化機能を提供します。

  • 時間軸での探索:「再現 - 修正 - 検証」という従来の煩雑なサイクルから解放されます。プログラムの実行履歴を自由に移動し、あらゆる重要な瞬間を捉えることが可能になります。
  • 問題の本質の洞察:スマートブレークポイントとウォッチ機能を通じて、異常な振る舞いのトリガー条件を直接特定し、曖昧なエラー報告を明確な問題として可視化します。
  • 実行状態の可視化:Lua の変数とメモリ構造をリアルタイムで解析して OpenResty アプリケーションの内部動作メカニズムを解明し、異常なパターンを迅速に特定することが可能になります。
  • Lua エコシステムへの完全な対応:UDB は OpenResty 環境とシームレスに統合され、Lua 開発者にプロフェッショナルレベルのデバッグ体験を提供し、複雑なアプリケーションの多角的な分析をサポートします。

OpenResty XRay との強力な連携により、死角のないデバッグシステムを構築

OpenResty XRay は、OpenResty アプリケーションにおける Lua コードのランタイム挙動を自動的に分析し、様々な技術的課題を正確に特定します。UDB と連携することで、以下のメリットをご提供いたします。

  • 全体像を捉える問題診断:リクエスト処理から低レベル実行までを網羅する立体的な可視化
  • Lua コードのパフォーマンス分析:ホットスポットとなっている関数とリソース消費を直感的に可視化
  • ゼロ干渉の分析体験:アプリケーションの本来の挙動を維持したまま、詳細なインサイトを獲得
  • 実践指向の最適化ガイダンス:実際の負荷に基づき、具体的な改善策を提案

UDB と OpenResty XRay の相乗効果により、開発チームは技術的なボトルネックを迅速に解消し、複雑な障害調査を明確な解決への道筋へと転換させることが可能になります。この組み合わせは、問題解決のプロセスを加速させるだけでなく、OpenResty アプリケーションの品質と安定性を根本から向上させ、ビジネスの継続的なイノベーションに対し、強固な技術的基盤を提供します。

実践:UDB と OpenResty XRay を用いた OpenResty アプリケーションにおける Lua コード呼び出しスタックの分析

以下では、実際のケーススタディを通じ、UDB を用いて OpenResty の Lua アプリケーションにおける呼び出しスタックを精密に分析する手法について解説いたします。

ステップ 1:アプリケーション実行軌跡の記録とサンプルのリプレイ

まず、UDB の Live Record ツールを利用し、OpenResty アプリケーションの実行プロセスを記録します。

  1. Live Record ツールを用い、実行中の OpenResty アプリケーションのサンプルを記録します。 1.1 OpenResty XRay のコンソール画面で ライブ録画 を選択します。 1.2 対象のアプリケーションおよびプロセスを選択し、録画開始 をクリックします。 1.3 録画ファイルの生成 アイコンをクリックし、録画ファイルを生成します。 1.4 ファイル生成が完了しましたら、録画停止 をクリックして記録プロセスを終了します。 1.5 記録ファイルをダウンロードし、分析に備えます。

  2. OpenResty XRay 上で関連ツールをコンパイルし、生成されたツールをローカル環境にダウンロードします。

  3. UDB ツールで記録サンプルをロードし、デバッグ環境をセットアップします:

udb --sessions=no -ex "set pagination off" -ex "set python print-stack full" openresty.rec

ステップ 2:記録サンプルの分析

本稿では、Lua のメモリ割り当て分析を例に、udb を使用して OpenResty Lua の実行コールスタックを調査する方法について解説します。 ここでは lua_alloc_realloc 関数にブレークポイントを設定します。

0% 51> b lj_alloc_realloc
Breakpoint 1 at 0x55560961bca0: file lj_alloc.c, line 1520.
0% 51> c
Continuing.
Thread 1 "nginx" hit Breakpoint 1, lj_alloc_realloc (msp=0x7f5657a9d010, ptr=ptr@entry=0x7f549be49108, nsize=40) at lj_alloc.c:1520
1520    lj_alloc.c: No such file or directory.

ステップ 3:下層 C コールスタックの分析

  1. bt コマンドを使用して、現在の C レイヤーのコールスタックを確認します:
0% 853> bt
#0  lj_alloc_realloc (msp=0x7f5657a9d010, ptr=ptr@entry=0x7f549be49108, nsize=40) at lj_alloc.c:1520
#1  0x000055560961c053 in lj_alloc_f (msp=<optimized out>, ptr=0x7f549be49108, osize=<optimized out>, nsize=<optimized out>) at lj_alloc.c:1582
#2  0x0000555609625270 in lj_mem_realloc (L=0x7f54b4bd1628, p=p@entry=0x7f549be49108, osz=osz@entry=24, nsz=40) at lj_gc.c:883
#3  0x0000555609626e3d in lj_tab_resize (L=0x7f54b4bd1628, t=0x7f54b5065cf0, asize=5, hbits=0) at lj_tab.c:256
#4  0x0000555609626986 in rehashtab (ek=<optimized out>, t=<optimized out>, L=<optimized out>) at lj_tab.c:375
#5  lj_tab_newkey (L=0x7f54b4bd1628, t=0x7f54b5065cf0, key=0x7f549ea60380) at lj_tab.c:453
#6  0x00005556469832df in ?? ()
#7  0x00007ffe8a54b9f0 in ?? ()
#8  0x000055560957e8dd in ngx_lua_conf_ffi_get (keys=<optimized out>, keys_len=0x7f5656ab5dd0, values=0x7f5657a80c78 "x\003\250WV\177", values_len=0x7f5656ab5e70, 
    db=<optimized out>) at ../lua-resty-config-1.0.6/src/ngx_lua_config_module.c:389
#9  0x0000001200000000 in ?? ()
#10 0x00007f549bc917a8 in ?? ()
#11 0x00007f549bc917b6 in ?? ()
#12 0x00007f549cf27568 in ?? ()
#13 0x00007f54b547d508 in ?? ()
#14 0x00007f54b6825e30 in ?? ()
#15 0x00007f549eb0d1a0 in ?? ()
#16 0x00007f549cf275a8 in ?? ()

C コールスタックからは、システム下層の lj_alloc_realloc 関数の呼び出しがトリガーされたことが確認できます。しかし、これは C 言語レベルの情報のみであり、Lua のビジネスロジックコードの完全な呼び出しパスを直接把握することはできません。

ステップ 4:Lua コードの完全なコールスタックの分析

  1. OpenResty XRay が提供する Lua コールスタック分析ツールをロードします。
4% 7,955> source lj-lua-on-cpu.y.py
  1. lj_lua_on_cpu コマンドを使用して、完全な Lua コールスタックを取得します:
0% 853> lj_lua_on_cpu 
C:lj_alloc_realloc
trace#6786:http-runtime-20181201.lua:4028
@1:890
helper_5
@1:1538
[builtin#xpcall]
xpcall
@1:1424
run_access_phase
@edge.lua:187
access
@access_by_lua(nginx.conf:766):2

この完全な Lua コールスタックにより、ビジネスロジックが access_by_lua フェーズのコードを実行中であることが明確にわかります。 現在実行されているのは trace 6786 という、JIT (Just-In-Time/即時コンパイル) コンパイルされたコードです。これが、先の bt コマンドで取得した C 言語のコールスタックが ngx_lua_conf_ffi_get 関数以降、正しく展開されなかった理由となります。

時間遡行デバッグの利点

UDB の時間遡行デバッグ機能は、その最も強力な特長の一つです。この機能を用いることで、記録された実行トレース内を自由に進めたり遡ったりすることが可能となり、異なるメモリ割り当て操作の時点を正確に特定し、各割り当て操作の完全なコンテキストとコールスタックを包括的に分析できます。

この点を検証するため、プログラムの実行を継続し、次の lj_alloc_realloc の呼び出しを捕捉します:

0% 51> c
Continuing.
Thread 1 "nginx" hit Breakpoint 1, lj_alloc_realloc (msp=0x7f5657a9d010, ptr=ptr@entry=0x7f549be49108, nsize=40) at lj_alloc.c:1520
1520    lj_alloc.c: No such file or directory.
1% 849,555> lj_lua_on_cpu 
C:lj_alloc_realloc
trace#177:common-runtime-20181201.lua:393
@common-runtime-20181201.lua:383
bit32_to_strtbl
@common-runtime-20181201.lua:435
inet_to_bitstr
@1:1459
[builtin#xpcall]
xpcall
@1:1424
run_rewrite_phase
@edge.lua:187
access
@access_by_lua(nginx.conf:766):2

2回キャプチャしたコールスタックを比較することで、主要な差異を一目で明確に把握できます:

  1. 1回目のメモリ割り当ては trace 6786 に由来します
  2. 2回目のメモリ割り当ては trace 177 に由来します

このようなピンポイントでの特定能力は、メモリ割り当ての分析に留まらず、複雑な問題を解決するための強力なツールとなります。特定の URI リクエストにおける Lua コードの実行パス分析であれ、プロセスのクラッシュを引き起こした Lua コード断片の追跡であれ、この手法はいずれのケースにも柔軟かつ効果的に対応可能です。

従来の GDB coredump 分析が、最終結果しか確認できない事故現場の写真のようなものであるとすれば、UDB は、事故発生に至るまでのあらゆる詳細を観察できる、高解像度のリプレイ動画に例えることができます。元のプロセスが既に実行中でなくとも、プログラムの完全な実行履歴を自由に遡り、任意の時点におけるプログラムの状態と挙動の詳細を正確に検証できます。これにより、まさに「時間を巻き戻す」ような全方位でのデバッグ体験が実現します。

まとめ

本稿の実践的な探求を通じて、UDB の時間遡行デバッグ機能と OpenResty XRay 分析ツールとの優れた連携が、OpenResty アプリケーションにおける Lua コードの実行プロセスをいかに深く分析できるかを実証いたしました。この組み合わせにより、以下のような顕著な利点が得られます。

  1. 包括的な実行コンテキスト:低レベルの C 言語コールスタックだけでなく、JIT (Just-In-Time) コンパイル後のコード実行状況を含む、Lua ビジネスロジックの完全なコールパスを正確に追跡できます。
  2. 時間軸でのデバッグ能力:UDB を使用することで、プログラムの実行履歴を自由に行き来し、アプリケーションを再起動することなく異なる時点でのプログラム状態を分析できます。これは、断続的に発生する問題や複雑なシナリオのトラブルシューティングにおいて特に重要です。
  3. 詳細なパフォーマンス分析:OpenResty XRay の分析ツールと組み合わせることで、メモリ割り当てパターンやホットスポット関数といったパフォーマンス上のボトルネックを正確に特定できます。
  4. 事後分析能力:元のプロセスが既に終了した場合でも、記録されたサンプルを用いて包括的な分析が可能です。これは、クラッシュした瞬間の静的なスナップショットしか提供できない従来の coredump 分析の制約をはるかに超えるものです。

卓越性を追求するソフトウェア開発者や運用専門家の皆様にとって、このような最先端のデバッグ技術の習得は、もはや選択肢ではなく、競争の激しい技術の世界で優位性を維持するための必須要件と言えるでしょう。UDB と 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 以上のオープンソースソフトウェアライブラリを執筆しております。

翻訳

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