mod_luaを使い倒すためにリソース取得するLuaライブラリを作った

前回記事(Apache2.4.1のmod_luaをいきなり弄ってフックできる箇所を増やしてみたよ)と前々回記事(Apache 2.4.1のmod_luaでApacheに介入する(mod_rewriteの終焉?))とmod_luaがあればこれまで敷居の高かったApacheモジュール開発も人気が出てくるのではないかと思いだしている。それを促していくためにも、今回は少し使えそうなLuaのライブラリを作った。ライブラリの実装はC言語。C言語のライブラリと簡単に連携できるところもLuaの強みである。

やれること

  • luaが動いているプロセスのCPUやメモリのカウンター値をkernelから取得できる
    • mod_luaの場合はサーバプロセスやCGIのプロセス
  • mod_lua経由で使うのを意図しているがもちろん単体のLuaスクリプトでも使える

文章で書くと少しわかりにくいので、実際にどう動かすかをいくつの例で説明していく。

Luaのスクリプト内で動かしてみる

例えば、「このコードのこのルーチンはどれくらいCPUやメモリを使ってるのかなぁ」と思った時などに使うのが一番わかりやすい。CPUのユーザー使用時間をどれくらい使っているかをコード内の各ルーチンの前後で取得して、その差分を見てやれば良い。まずはサンプルコード。

require "resources"

print("before: " .. resources.get("ALL", "cpu_utime"))
n = 0
while n <= 2000000 do
    n = n + 1
end
print("after: " .. resources.get("ALL", "cpu_utime"))

このように、適当なループの前後でCPUのユーザー使用時間を取得してみる。実際に実行すると以下のようになる。

$ lua test.lua
before: 0
after: 0.220966

また、nを十倍の20000000にしてやると

$ lua test.lua
before: 0.000999
after: 2.188667

となる。ふむ、確かに10倍になっている。得られる数値はカウンター値なので、差分をとってやれば大体のルーチンが使用したリソース量がわかる。使える引数は現状、以下の3通りにしている。

  • 第一引数
    • ALL
      • 実行中のプロセスかつそこからforkされたもの全ての合計
    • SELF
      • 実行中のプロセスのみ
    • CHILD
      • 子プロセスのみ
  • 第二引数
    • cpu_stime
      • CPUのシステム使用時間
    • cpu_utime
      • CPUのユーザー使用時間
    • shared_mem
      • 使用されたメモリ使用量

ライブラリの実装にはgetrusage()を使ってみたので、詳しくはmanのgetrusage()を参照。使い方は以下のように、resources.get()にほしい情報を渡してやれば、Cのライブラリ経由でプロセスのリソースが保持されている構造体から各種リソースカウンター値を取得してきて返す。

resources.get("ALL", "cpu_stime")
resources.get("SELF", "cpu_utime")
resources.get("CHILD, "shared_mem")

これによって、好きなところでプロセスのリソースカウンタを取得できるので、プログラマーがアプリケーション等をLuaで作るときのデバッグにも最適だと思う。使い方は色々あると思うので、オリジナルの使い方を見つけて欲しい。

ではmod_luaで使ってみよう

動機としては、こっちが目的で作ったので本記事の肝はここにある。妄想していたのは、例えば、モジュール版のperlやphp等はサーバプロセスにインタプリタを組み込む仕様上、各スクリプトのリソース使用量をモニターするのが難しい。そういった理由でCGI版のperlやphpを使っているレンサバも多い(さくらはそういっているみたいな記事があったような気がする)と思う。しかし、このライブラリを使えば、mod_luaと連携して好きなタイミングでリソースのカウンター値を簡単に取得できる。この簡単にが大事なのだ。

今回はサンプルなので、適当にmod_luaのフック関数に登録するスクリプトを以下のようなものにする。

require "resources"

function rget(r)
    now_scpu = resources.get("ALL", "cpu_stime")
    now_ucpu = resources.get("ALL", "cpu_utime")
    now_mem  = resources.get("ALL", "shared_mem")
    r:debug("Current System CPU: " .. now_scpu)
    r:debug("Current User CPU: " .. now_ucpu)
    r:debug("Current Memory: " .. now_mem)
    return apache.DECLINED
end

そして、mod_luaの設定は以下のように好きなところでフックさせる。今回はとりあえず、URIをファイル名に変換するタイミングとレスポンスが完了してログが書かれるタイミング(ここでのフックは僕がmod_luaを改良した:全回の記事を参照)でフックさせてみる。Apacheの設定は以下。

Loadmodule lua_module modules/mod_lua.so
LuaPackageCPath /usr/local/lib/lua/5.1/?.so
LuaHookTranslateName resource.lua rget
LuaLogTransaction resource.lua rget

こうしてやれば、モジュール版だろうがCGI版だろうが何だろうがluaがカーネルからサーバプロセスのリソースのカウンター値を取ってこれるので、PerlやPHPの実行方式に関わらずリソース消費を計測できる。また、Apache自体にモジュール等を組み込み過ぎてパフォーマンスが落ちた場合等でも、このスクリプトを様々なタイミングでフックしてやれば、Apacheのどのフェーズでリソースを多く消費しているのかもわかる。

以上から、アプリケーションを実装するプログラマーにも、サーバを管理するインフラエンジニアにも有用な機能だと言えそうだ。

導入方法

結構はまった。プログラム自体はすぐかけたんだけど、いかんせんハマった時の資料がmod_luaは少ない(自分のブログばっかりでてくる)ので大変だった。とりあえずはうまくいったので、困ったところを共有しておきたい。まずは導入の仕方から。

ハマりどころ1: Luaのdynamic shared libralyをenableにしておく

Lua自体をコンパイルする際に、dynamicライブラリを使えるようにコンパイルしておく必要がある。Linuxの場合、デフォルトではdynamicライブラリを呼び出せないオプションになっているので、LUA_USE_DLOPENを指定しておく。具体的には、「lua-5.1.4/src/Makefile」の98と99行目を以下のように変更しておく。

  • 変更前
 98 linux:
 99     $(MAKE) all MYCFLAGS=-DLUA_USE_LINUX MYLIBS="-Wl,-E -ldl -lreadline -lhistory -lncurses"
  • 変更後
 98 linux:
 99     $(MAKE) all MYCFLAGS="-DLUA_USE_LINUX -DLUA_USE_DLOPEN" MYLIBS="-Wl,-E -ldl -lreadline -lhistory -lncurses"

これで、make linuxしてやればOK。

その後、Apache2.4.1のコンパイル時に以下のようにLuaのディレクトリを指定しておく。

--enable-lua --with-lua=/usr/local/lua5.1/

ハマりどころ2:Luaのライブラリ実装にaprを使ったのだがコンパイルがうまくいかない

get_resources()をLuaから呼び出すためのLuaライブラリ(C言語で実装した)をコンパイルする。ソースはこちら。Lua用のライブラリをCで実装する方法はまた次の記事にでも書こうと思う。今回は気が変わって、aprを使って実装してみたのだが、これが良くなかった。ここでハマりにハマった。まぁ、結果的には色々理解が深まったので、よしとする。話すと長くなっちゃうので完結に。aprをgccでコンパイルする時に、こういう事象が発生する。なんでこうなっているのかは、この記事の人と同じでわかっていないので、知ってる人はぜひ教えて下さい。なので、以下のようにapr.hを変更する。

  • 変更前
358 typedef  off64_t           apr_off_t;
  • 変更後
358 typedef  __off64_t           apr_off_t;

これでようやくコンパイルができる。aprを使っているので以下のようにコンパイル。

gcc -I/usr/local/apr/include/apr-1 -lapr-1 /usr/local/apr/lib/libapr-1.a -llua -o resources.so resources.c -shared -O

これで、これで作られたresources.soをluaまたはmod_luaから読み込まれるライブラリ置き場にコピーしておけばよい。

最後に

以上が、mod_luaを使い倒すために実装してみた機能だ。大体コードは1、2時間くらいで書いたので、あまり中の汚いところのツッコミは無しということで。時間があれば、Luaで呼び出せるライブラリのCでの実装の仕方を解説したいと思っている。このあたりも参考にする情報が少なかったので、モジュールのソースを参考に書いていった。とにかく、mod_luaを使えば、C言語との連携も含めてこういうことも簡単にできるので、ぜひこのCのライブラリのソースをパクって自分のオリジナルのライブラリを作り、mod_luaでApacheにフックさせてみて欲しい。きっとApacheが自分の思い通りに動きだして楽しくなるよ!

「mod_luaを使い倒すためにリソース取得するLuaライブラリを作った」への1件のフィードバック

コメントは受け付けていません。