OpenResty または Nginx プロセスにおける読み込み済み Lua モジュールの一覧表示
オンラインの Lua アプリケーション(OpenResty や Nginx で実行されているものを含む。Nginx の場合でも、OpenResty のコアコンポーネントである Lua Nginx モジュールを使用しています)のトラブルシューティング時に、永続的な Lua データ構造を検査することは一般的な要件です。Lua データ構造は、読み込まれた Lua モジュールの深部に埋め込まれたり、アップバリューを介して Lua 関数から参照されたりすることがあります。従来の Lua データ検査方法は、特別な API を暴露して対応する Lua データをシリアライズしダンプするというものでした。しかし、動的トレース技術を使用して実行中のプロセス内で Lua データを調べる方が、はるかに柔軟で効率的です。
OpenResty XRay は、YLua と呼ばれる Lua 互換言語を提供しており、100% 非侵襲的な方法で実行中のプロセス内の任意の Lua データを検査するためのツールを作成することができます。このチュートリアルでは、実行中の OpenResty および LuaJIT アプリケーションにおいて、読み込まれたすべての Lua モジュールの名前をダンプする簡単なユースケースを紹介します。今後のチュートリアルでは、YLua を使用したより複雑なユースケースを紹介する予定です。
システム環境
ここでは例として Red Hat Enterprise Linux 8 システムを使用します。OpenResty XRay がサポートする Linux ディストリビューションであれば、Ubuntu、Debian、Fedora、Rocky、Alpine などどれでも同様に動作するはずです。
対象アプリケーションとして、変更を加えていないオープンソースの OpenResty バイナリビルドを使用します。OpenResty や Nginx のバイナリは、自分でコンパイルしたものも含めて、何でも使えます。 既存のサーバーインストールやプロセスに特別なビルドオプション、プラグイン、ライブラリは必要ありません。これが 動的トレース 技術の美しさです。真に非侵襲的なのです。
また、同じシステム上で OpenResty XRay の Agent デーモンを実行し、openresty-xray-cli
パッケージからコマンドラインユーティリティをインストールして設定しています。
読み込み済み Lua モジュールの名前
標準ツールの使用
対象プロセスで読み込まれた Lua モジュールをダンプする最も簡単な方法は、OpenResty XRay の標準ツール lj-loaded-modules
を使用することです:
ps
コマンドを使用して、分析したい nginx ワーカープロセスを見つけます。ここでは 1046
です。
$ ps aux | grep nginx
1 S root 1059 1 0 80 0 - 73941 - 15:03 ? 00:00:00 nginx: master process /usr/local/openresty/nginx/sbin/nginx
5 S nobody 1046 1059 0 80 0 - 82123 - 15:03 ? 00:00:00 nginx: worker process
次に、openresty-xray-cli
パッケージの orxray
コマンドラインユーティリティを使用して、標準アナライザー lj-dump-loaded-mods
を実行します。-p
オプションは分析対象のプロセス ID(または PID)を指定します。
# 4096 is the target process's PID
$ orxray analyzer run lj-loaded-modules -p 4096
Start tracing...
jit: table
math: table
coroutine: table
debug: table
...
Go to https://x5vrki.xray.openresty.com/targets/68/history/751715 for charts.
カスタム YLua ツールの使用
標準ツールは、内部で YLua 言語を使用して実装されています。ここでは、YLua を使用して、このツールを自分で再作成する方法を見ていきましょう。LuaJIT VM(および標準 Lua 5.1 インタープリタの VM)では、ロードされた Lua モジュールが常に package.loaded
にキャッシュされることを知っています。キーはモジュール名で、値はモジュールデータです。ここで、例として my-dump-loaded-mods.ylua
という名前の新しい YLua ソースファイルを作成できます:
probe process.begin
for k, v in pairs(package.loaded) do
print(k, ": ", type(v))
end
exit()
end
ここで、probe
キーワードは、ツールがターゲットプロセスにアタッチされたときにトリガーされる process.begin
プローブポイントの新しいプローブハンドラを定義します1。そして、YLua で package.loaded
Lua テーブルを反復処理することで Lua モジュールをダンプします。これは Lua と全く同じ構文です。モジュール名とそのデータ型を出力した後、exit()
を呼び出してトレースプロセス全体を終了します。そして、openresty-xray-cli
パッケージの run-ylua
コマンドラインユーティリティを使用して、この YLua ソースファイルを実行できます。
OpenResty または Nginx Lua アプリケーションの検査
分析したい nginx ワーカープロセスを見つけるには ps
コマンドを使用します。ここでは 1046
です。
$ ps aux | grep nginx
1 S root 1059 1 0 80 0 - 73941 - 15:03 ? 00:00:00 nginx: master process /usr/local/openresty/nginx/sbin/nginx
5 S nobody 1046 1059 0 80 0 - 82123 - 15:03 ? 00:00:00 nginx: worker process
前回の YLua ソースファイルを、openresty-xray-cli
パッケージの run-ylua
コマンドラインユーティリティを使用して実行してみましょう。-p
オプションは、分析対象のプロセス ID(または PID)を指定します。
$ run-ylua -p 1046 my-dump-loaded-mods.ylua
Start tracing...
resty.core.uri: table
resty.core.exit: table
resty.core.base64: table
resty.core.request: table
resty.core.response: table
string: table
pgmoon: table
openresty_org.controller: table
ndk: table
table: table
resty.core.utils: table
resty.lrucache: table
table.new: function
debug: table
table.clear: function
...
package: table
math: table
resty.core.base: table
jit.opt: table
ngx: table
_G: table
resty.core.var: table
resty.core.worker: table
resty.core.regex: table
resty.core.shdict: table
resty.core.time: table
resty.core.hash: table
簡潔にするため、出力の大部分は省略しています。出力を wc -l
コマンドにパイプして、合計数を確認することができます:
$ run-ylua -p 1046 my-dump-loaded-mods.ylua | wc -l
Start tracing...
38
モジュールの大部分は table
型です。通常、Lua モジュールは Lua テーブルで表現されます。一部のモジュールは function
型であり、ユニークです。例えば、table.new
は LuaJIT によって特別に実装された関数型モジュールです。
io
、os
、string
、table
、math
、debug
は Lua 5.1 言語で定義された標準 Lua モジュールです。jit
、bit
、ffi
、jit.opt
、table.new
、table.clear
は標準 LuaJIT モジュールです。resty.*
、coroutine
、ndk
、ngx
は OpenResty によって導入されたモジュールです。
スタンドアロンの LuaJIT アプリケーションの検査
Nginx や OpenResty を使用せずに、スタンドアロンの luajit
プロセスを検査することもできます。
まず、いつものように luajit
プロセスの PID を見つけます:
$ ps aux | grep luajit
root 3371845 0.0 0.0 4428 2524 pts/3 S 22:17 0:00 luajit t.lua
そして、このPID 3371845
を使用して run-ylua
コマンドを実行します:
$ orxray analyzer run lj-loaded-modules -p 3371845
Start tracing...
jit: table
math: table
coroutine: table
debug: table
os: table
_G: table
package: table
string: table
jit.opt: table
bit: table
io: table
table: table
ご覧の通り、スタンドアロンの luajit
プログラムは通常、デフォルトで読み込む Lua モジュールの数が少なくなっています。
出力を wc -l
コマンドにパイプして、総数を確認することができます:
$ orxray analyzer run lj-loaded-modules -p 3371845 | wc -l
Start tracing...
12
Web コンソールで直接実行
ユーザーは、このチュートリアルで紹介したツールを OpenResty XRay の Web コンソールで直接実行することができます。高い CPU 使用率などの興味深いイベントが発生した際に、自動的にトリガーすることも可能です。openresty-xray-cli
のコマンドラインユーティリティはデモンストレーション目的に便利です。また、DevOps や SRE の担当者が他のシステムに容易に自動化・統合することができます。
コンテナ内のアプリケーションのトレース
OpenResty XRay ツールは、コンテナ化されたアプリケーションを透過的にトレースすることをサポートしています。Docker と Kubernetes (K8s) のコンテナは透過的に動作します。通常のアプリケーションプロセスと同様に、対象のコンテナにアプリケーションや追加の特権は必要ありません。OpenResty XRay エージェントデーモンは、対象のコンテナの外部(ホストオペレーティングシステム上や特権を持つ独自のコンテナ内など)で実行する必要があります。
例を見てみましょう。まず、docker ps
コマンドでコンテナ名またはコンテナ ID を確認します。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4465297209d9 openresty/openresty:1.19.3.1-2-alpine-fat "/usr/local/openrest…" 18 months ago Up 3 minutes angry_mclaren
ここでは、コンテナ名が angry_mclaren
です。次に、このコンテナ内の対象プロセスの PID を特定します。
$ docker top angry_mclaren
UID PID PPID C STIME TTY TIME CMD
root 3373047 3373026 0 22:23 ? 00:00:00 nginx: master process /usr/local/openresty/bin/openresty -g daemon off;
nobody 3373101 3373047 0 22:23 ? 00:00:00 nginx: worker process
openresty
ワーカープロセスの PID は 3373101
です。そして、通常通りこの PID に対して OpenResty XRay アナライザーを実行します。
$ orxray analyzer run lj-loaded-modules -p 3373101
Start tracing...
table: table
ngx: table
resty.core.var: table
table.new: function
resty.core.regex: table
resty.core.shdict: table
resty.core.time: table
...
table.clear: function
resty.core.worker: table
resty.lrucache: table
io: table
OpenResty XRay は、長時間実行されているプロセスを特定のタイプ(「OpenResty」、「Python」など)の「アプリケーション」として自動的に検出することも可能です。
ツールの実装方法
すべてのツールは Y 言語 で実装されています。OpenResty XRay は、OpenResty XRay の Stap+2 または eBPF3 バックエンドを使用してこれらを実行します。両者とも、Linux カーネルの uprobes
および kprobes
機能に基づく 100% 非侵襲的な動的トレーシング技術を使用しています。YLua スクリプトはまず Y 言語にコンパイルされ、その後実行可能な動的トレーシングツールにコンパイルされます。
対象アプリケーションやプロセスとの連携は一切必要ありません。ログデータやメトリクスデータは使用せず、必要ともしません。実行中のプロセスのプロセス空間を厳密に読み取り専用の方法で直接分析します。また、対象プロセスにバイトコードやその他の実行可能コードを一切注入しません。100% クリーンで安全です。
ツールのオーバーヘッド
このチュートリアルで紹介する動的トレーシングツールは非常に効率的で、オンライン実行に適しています。
ツールが実行されておらず、アクティブにサンプリングしていない場合、システムおよび対象プロセスへのオーバーヘッドは厳密にゼロです。対象アプリケーションやプロセスに追加のコードやプラグインを注入することは一切ないため、固有のオーバーヘッドはありません。
サンプリング中、このチュートリアルで取り上げるツールの平均リクエストレイテンシーとスループットへのオーバーヘッドは通常測定できません。ここで紹介するツールは、いずれにせよ一度限りの検査を行うだけです。
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(マイクロサービスおよび分散トラフィックに最適化された多機能
翻訳
英語版の原文と日本語訳版(本文)をご用意しております。読者の皆様による他の言語への翻訳版も歓迎いたします。全文翻訳で省略がなければ、採用を検討させていただきます。心より感謝申し上げます!