LuaルールとNGINX設定が競合した場合どうなるか?

API7.ai

October 13, 2022

OpenResty (NGINX + Lua)

1. OpenRestyの名前と言語

Q: OpenRestyという名前の由来が今まで理解できていませんでした。また、OpenRestyはLua言語を利用して飛躍していますが、なぜ他のスクリプト言語ではないのでしょうか?例えば、Shellなど。

A: OpenRestyはYahoo!の企業プロジェクトとして2007年10月に始まりました。「Open」という名前はOpenAPIから、「Resty」はrest APIから取られています。当初、OpenRestyはウェブサーバーや開発プラットフォームではなく、ウェブサイトのようなアプリケーションとしての利用を想定していました。

OpenRestyが10年以上前にオープンソース化された当時、同期非ブロッキングをサポートする言語は非常に少なかったです。現在でも、OpenRestyと同等のパフォーマンスを実現できるバックエンド言語は多くありません。現在、多くの開発者がAPIゲートウェイやソフトWAFでOpenRestyを使用しており、これは開発者にとって自然な選択です。

言語に関しては、OpenRestyはNGINXに他の開発言語を組み込む唯一のプロジェクトではありません。例えば、NGINXは公式にJSを組み込んでいますし、PHPをNGINXに組み込むオープンソースプロジェクトもあります。

通常、利用する言語の選択は、並行性、JIT、言語の人気などの要素に基づいています。OpenRestyにとって、2007年当時、Luaは確かに最良の選択でした。OpenRestyは最初のバージョンでLuaではなくperlを選択したことで少し遠回りをしました。

2. 設定の優先順位

Q: OpenRestyのLuaルールとNGINX設定ファイルが競合する場合、例えばNGINXがrewriteルールを設定し、同時にrewrite_by_lua_fileを参照している場合、これらの2つのルールの優先順位はどうなりますか?

A: 実際には、NGINX設定のrewriteルールがどのように書かれているかによります。breaklastかによって異なり、これはOpenRestyの公式ドキュメントに記載されています。サンプルコードは以下の通りです:

 location /foo {
     rewrite ^ /bar;
     rewrite_by_lua 'ngx.exit(503)';
 }
 location /bar {
     ...
 }

このサンプルコードの設定では、ngx.exit(503)は実行されません。

しかし、以下のように書くと、ngx.exit(503)が実行されます。

rewrite ^ /bar break.

ただし、私はNGINX設定ではなくOpenRestyを利用してrewriteを行うことをお勧めします。この曖昧さを避けるためです。NGINX設定の多くは比較的難解で、ドキュメントを繰り返し参照して理解する必要があります。

3. なぜ私のコードはエラーを出すのか?

Q: LuaJIT拡張のtable関数で、なぜ以下の2行のコードをLuaJITで実行すると「moudule not found」というエラーが出るのでしょうか?私はLuaJITバージョン2.0.5を使用しています。

local new_tab = require('table.new')
# or
require('table.clear')

# 実行するとエラーが出る
luajit: table_luajit.lua:1: module 'table.new' not found:

A: これらのコードはLuaJITバージョン2.1以上で実行する必要があります。ドキュメントはこちらにあります:https://github.com/LuaJIT/LuaJIT/blob/v2.1/doc/extensions.html#L218。これで確認できます。

前述したように、OpenRestyを使用する際には特定のバージョンのLuaJITが必要です。OpenRestyはLuaJIT 2.1ブランチに基づいており、LuaJITに対して多くの拡張を行っています。

したがって、このコラムのコードを実行する際には、公式のOpenRestyインストールを使用することを忘れないでください。もしNGINXの上にlua-nginx-moduleを追加してコンパイルすると、多くの問題に遭遇するでしょう!

4. null値に関する混乱

Q: 私が遭遇した混乱の1つは、ngx.nullnilnull""です。ウェブで検索すると、nullngx.nullの定義であると書かれているのを見ました。Redisが返す際、返された結果がnullかどうかを判断することがよくありますが、判断する際にどの値と比較されるのでしょうか?これらの値の使用に関して他に注意すべき点はありますか?これらの値がよく理解できていません。

A: 質問に答える前に、以下のコードを使用してlua-resty-redisでキーを検索することをお勧めします。

local res, err = red:get("dog")

これは、Luaのnilはテーブルの値として使用できないため、OpenRestyはテーブル内のnull値としてngx.nullを導入しています。

以下のコードでngx.nullとその型を出力できます。

# ngx.nullを出力
$ resty -e 'print(ngx.null)'
null
# 型を出力
$ resty -e 'print(type(ngx.null))'
userdata

ご覧の通り、ngx.nullnilではなく、userdata型です。

さらに、OpenRestyには多くのnull値があります。例えば、cjson.nullcdata:NULLなどです。これらについては後で説明します。

OpenRestyで唯一の偽の値はnilfalseです。したがって、if not res thenのようなコードを書く際には注意が必要で、明示的にif res ~= nil and res ~= false thenのように変更し、対応するテストケースでカバーすることが望ましいです。

5. APIゲートウェイとは何か?

Q: 記事で言及されているAPIゲートウェイとは何ですか?そしてNGINX、Tomcat、Apacheなどのウェブサーバーとはどう違うのですか?

A: APIゲートウェイは、サービスを統一管理するためのゲートウェイです。例えば、支払い、ユーザーログインなど、すべてAPIの形で提供されるサービスであり、それらを統一してセキュリティと認証を行うためのゲートウェイが必要です。

APIゲートウェイは、従来のNGINXやApacheを置き換えて南北トラフィックを処理するだけでなく、マイクロサービス環境で東西トラフィックを処理することもできます。これは、ビジネスに近いミドルウェアであり、基盤となるウェブサーバーではありません。

APIゲートウェイの紹介と実装についての詳細は、Apache APISIXを参照してください。Apache APISIXはOpenRestyを基盤に実装されています。