Lua は軽量で高効率なスクリプト言語です。ゲートウェイ開発において、Lua は API ゲートウェイ、メッセージゲートウェイ、リバースプロキシなど、さまざまなゲートウェイプログラムの記述と実行に使用できます。開発者は Lua を使用して、リクエストルーティング、フィルタリング、認証のためのスクリプトを記述し、カスタマイズされた処理を実行できます。Lua は、NGINX や Envoy などのプロキシに組み込んで、リクエストとレスポンスの処理、ログ出力、その他のカスタマイズされた操作を行うことができます。このトピックでは、Lua を Envoy プロキシに組み込んでリクエストとレスポンスを処理し、そのヘッダーとボディをログに記録する方法について説明します。
制限事項
お使いの Microservices Engine (MSE) クラウドネイティブゲートウェイのバージョンは 1.2.11 以降である必要があります。
セキュリティ上の理由から、MSE では、デフォルトで以下の Lua ライブラリと関数が無効になっています。
debug.debug
debug.getfenv
debug.getregistry
dofile
io
loadfile
os.execute
os.getenv
os.remove
os.rename
os.tmpname
ルーティングルールの設定
MSE コンソールにログオンし、上部のナビゲーションバーでリージョンを選択します。
左側のナビゲーション ウィンドウで、Cloud-Native Gateway > ゲートウェイリスト を選択します。 [ゲートウェイ] ページで、ゲートウェイの名前をクリックします。
[ゲートウェイの詳細] ページの左側のナビゲーションウィンドウで、Plug-in Marketplace をクリックします。
-
All Plug-ins タブで、Custom タブをクリックし、次に [lua] リソースカードをクリックします。
-
Plug-in Configuration タブで、ルールレベルを選択し、次のように設定します。
説明ゲートウェイが受信したすべてのリクエストは、ルートレベルのプラグインルール → ドメインレベルのプラグインルール → インスタンスレベルのプラグインルールという優先順位でプラグインルールにマッチします。
ルートレベルのプラグインルール
-
Add Rule をクリックします。表示されたページで、ルールを有効にしてターゲットルートを選択し、[ルールを設定] エディターに Lua スクリプトを入力します。
-
OK をクリックします。
ドメインレベルのプラグインルール
-
Add Rule をクリックします。表示されたページで、ルールを有効にし、ターゲットドメインを選択してから、[ルールの設定] エディターに Lua スクリプトを入力します。
重要アスタリスク (*) を含むドメイン名は、Lua プラグインルールではサポートされていません。
-
OK をクリックします。
インスタンスレベルのプラグインルール
Lua プラグインには、デフォルトでインスタンスレベルのプラグインルールが設定されています。ルールを編集するには、まず編集ロックを解除する必要があります。
スイッチをオンにして、[ルール設定] エディターに Lua スクリプトを入力します。
-
保存 をクリックします。
-
API リファレンス
クラウドネイティブゲートウェイが提供する Lua API の詳細については、「Lua」をご参照ください。
シリアル化またはデシリアル化中にエラーが発生した場合、Lua はエラー関数を呼び出してエラーを返し、現在のプロセスを終了します。
一般的なユースケース
プラグインログでの完全なリクエストとレスポンス情報の表示
-
ルーティングルールを設定する際に、次の Lua コードを使用します。
サンプルコードでは、次の
content typeのリクエストボディとレスポンスボディのみが表示されます。ボディサイズは 1,024 バイト (1 KB) を超えることはできません。application/x-www-form-urlencoded
application/json
text/plain
local maxBodySize = 1024 function check_content_readable(type) if type == nil then return false end if string.find(type, "application/x-www-form-urlencoded",1,true) or string.find(type, "application/json",1,true) or string.find(type, "text/plain",1,true) then return true end return false end function envoy_on_request(request_handle) local headers = request_handle:headers() local headersStr = "" local contentType for key, value in pairs(headers) do if key == "content-type" then contentType = value end headersStr = headersStr .. key .. "=" .. value .. ", " end request_handle:streamInfo():dynamicMetadata():set("envoy.lua","request_headers",headersStr) local requestBody = "" if check_content_readable(contentType) then for chunk in request_handle:bodyChunks() do if (chunk:length() > 0) then requestBody = requestBody .. chunk:getBytes(0, chunk:length()) end if (#requestBody > maxBodySize) then requestBody = requestBody .. "<truncated>" break end end end request_handle:streamInfo():dynamicMetadata():set("envoy.lua","request_body",string.gsub(requestBody,"\n","\\n")) end function envoy_on_response(response_handle) local headers = response_handle:headers() local headersStr = "" local contentType local contentEncoding = false for key, value in pairs(headers) do if key == "content-type" then contentType = value elseif key == "content-encoding" then contentEncoding = true end headersStr = headersStr .. key .. "=" .. value .. ", " end local responseBody = "" if check_content_readable(contentType) and not contentEncoding then for chunk in response_handle:bodyChunks() do if (chunk:length() > 0) then responseBody = responseBody .. chunk:getBytes(0, chunk:length()) end if (#responseBody > maxBodySize) then responseBody = responseBody .. "<truncated>" break end end end local reqHeaders = "" local reqBody = "" local metadata = response_handle:streamInfo():dynamicMetadata():get("envoy.lua") if metadata ~= nil then local headers = response_handle:streamInfo():dynamicMetadata():get("envoy.lua")["request_headers"] if headers ~= nil then reqHeaders = headers end local body = response_handle:streamInfo():dynamicMetadata():get("envoy.lua")["request_body"] if body ~= nil then reqBody = body end end response_handle:logInfo("request Headers: [" .. reqHeaders .. "] request Body: [" .. string.gsub(reqBody,"\n","\\n") .. "] response Headers: [" .. headersStr .. "] response Body: [" .. string.gsub(responseBody,"\n","\\n") .. "]") end -
プラグインのログを表示します。
この機能では、ゲートウェイプラグインのログ転送を有効にする必要があります。この機能が有効になっていない場合は、Immediately Enable Log Shipping をクリックします。ログ転送を有効にすると、ゲートウェイはプラグインログを Log Service (SLS) に転送し、そこで表示できます。アクセスログの
x-request-idを使用して、Lua プラグインログで完全なリクエストとレスポンスの情報を検索できます。[プラグインログ] タブをクリックします。検索バーに、クエリステートメント (例:
module:lua and cluster_id:<your-gateway-instance-ID>) を入力し、アクセスログのx-request-idの値をフィルターとして追加します。raw ログセクションのクエリ結果には、一致するリクエストの完全な HTTP リクエストヘッダー、リクエストボディ、レスポンスヘッダー、およびレスポンスボディが表示され、x-request-idフィールドがハイライトされます。
アクセスログでの完全なリクエストとレスポンス情報の表示
-
ルーティングルールを設定する際に、次の Lua コードを使用します。
ゲートウェイのアクセスログのパラメーターにカスタムの動的メタデータを設定できます。この場合、プラグインでメタデータを定義する必要があります。次のサンプルコードでは、リクエストヘッダー、リクエストボディ、レスポンスヘッダー、レスポンスボディに対応する 4 種類のメタデータ情報を定義します。
envoy.lua:request_headers
envoy.lua:request_body
envoy.lua:response_headers
envoy.lua:response_body
local maxBodySize = 1024 function check_content_readable(type) if type == nil then return false end if string.find(type, "application/x-www-form-urlencoded",1,true) or string.find(type, "application/json",1,true) or string.find(type, "text/plain",1,true) then return true end return false end function envoy_on_request(request_handle) local headers = request_handle:headers() local headersStr = "" local contentType for key, value in pairs(headers) do if key == "content-type" then contentType = value end headersStr = headersStr .. key .. "=" .. value .. ", " end request_handle:streamInfo():dynamicMetadata():set("envoy.lua","request_headers",headersStr) local requestBody = "" if check_content_readable(contentType) then for chunk in request_handle:bodyChunks() do if (chunk:length() > 0) then requestBody = requestBody .. chunk:getBytes(0, chunk:length()) end if (#requestBody > maxBodySize) then requestBody = requestBody .. "<truncated>" break end end end request_handle:streamInfo():dynamicMetadata():set("envoy.lua","request_body",string.gsub(requestBody,"\n","\\n")) end function envoy_on_response(response_handle) local headers = response_handle:headers() local headersStr = "" local contentType local contentEncoding = false for key, value in pairs(headers) do if key == "content-type" then contentType = value elseif key == "content-encoding" then contentEncoding = true end headersStr = headersStr .. key .. "=" .. value .. ", " end response_handle:streamInfo():dynamicMetadata():set("envoy.lua","response_headers",headersStr) local responseBody = "" if check_content_readable(contentType) and not contentEncoding then for chunk in response_handle:bodyChunks() do if (chunk:length() > 0) then responseBody = responseBody .. chunk:getBytes(0, chunk:length()) end if (#responseBody > maxBodySize) then responseBody = responseBody .. "<truncated>" break end end end response_handle:streamInfo():dynamicMetadata():set("envoy.lua","response_body",string.gsub(responseBody,"\n","\\n")) end -
ログフォーマットを調整します。
ゲートウェイインスタンスの左側のナビゲーションペインで、Parameters をクリックし、次に Default Format (Manual Input) をクリックします。アクセスログに情報を表示するには、対応するメタデータをカスタムログフィールドに追加します。
4 つのカスタムログフィールドを追加します。リクエスト情報については、[フィールドタイプ] に [Request dynamic metadata] を選択し、フィールド値を
envoy.lua:request_headersとenvoy.lua:request_bodyに設定します。レスポンス情報については、[フィールドタイプ] に [Response dynamic metadata] を選択し、フィールド値をenvoy.lua:response_headersとenvoy.lua:response_bodyに設定します。