APISIX 用 Lua と ChatGPT によるカスタムプラグイン開発
June 12, 2023
Apache APISIXの主要な特徴の1つは、プラグインを通じた拡張性です。APISIXでは、独自のカスタムプラグインを構築して、追加の機能を追加し、APIトラフィックをより効率的に管理することができます。多くの場合、新しいプラグインを実装するためにLuaプログラミング言語を使用したり、プラグインランナーを活用して好きなプログラミング言語でプラグインを開発したりします。しかし、APISIXはLuaに対して最も優れたサポートを提供しています。LuaでAPISIXのプラグインをいくつか書いてみた後、Luaプログラミングの基礎を知る必要がないこと、またはこの言語のエキスパートである必要がないことに気づきました。特に、ChatGPTの友達が常に一緒にいる場合です。例えば、**JavaやC#**のバックグラウンドを持つ私にとって、Luaで書かれたコードやロジックを理解することは可能であり、あなたも同じことができると信じています。
この記事では、LuaとChatGPTを使用してAPISIX用の新しいカスタムプラグインfile-proxyを開発するプロセスをガイドします(ChatGPTを使ってLuaコードを書いてもらいます)。このプラグインは、APIを通じて静的ファイルを公開し、指定されたURLからファイルを取得するために使用されます。
APISIXはNginxの既存の機能を強化するために構築され、NginxはAPISIXが活用する再利用可能なLuaモジュールのコレクションを提供しています。
学習目標
この記事を通じて、以下のことを学びます:
- 新しいfile-proxyプラグインを開発する方法。
- ChatGPTを効率的に使用してLuaコードを生成する方法。
- APISIX用の独自プラグインを構築する手順。
新しいfile-proxyプラグインのユースケース
実際のプラグイン実装に飛び込む前に、なぜこのプラグインが必要なのかを理解しましょう。この記事を書いている時点では、APISIXには同様のケースに対応する組み込みプラグインが提供されていないかもしれません。そのため、新しいプラグインを構築します。多くの場合、静的ファイル(Yaml、JSON、JavaScript、CSS、または画像ファイル)をAPIを通じて公開したいと考えます。
例えば、APISIX API Gatewayは、アプリケーションのフロントドアとして機能し、着信リクエストを複数のAPIエンドポイントにルーティングします。これは、すべてのサーバーURL、パス、パラメータ、各APIエンドポイントの説明、およびその入力と出力を定義する適切な場所です。そして、APIをドキュメント化するためにOpenAPI仕様を作成します。OpenAPI .yaml
ファイルは、APIユーザーがAPIを理解し、操作するための地図のようなものです。プラグインにopenapi.yaml
ファイルのパス(サーバー内の保存場所)を提供することで、APIゲートウェイを通じてファイルを直接取得し、API利用者に一貫したインターフェースを提供できます。これにより、APIユーザーは指定されたURL(https://example.com/openapi.yaml
)で.yaml
ファイルにアクセスできます。
他にも、このfile-proxyプラグインをシンプルなコンテンツデリバリーネットワーク(CDN)の代替として使用することを考えるかもしれません。小規模なアプリケーションがあり、本格的なCDNを使用したくない場合、特定の場所から静的ファイルを提供するためにfile-proxyプラグインを使用できます。file-proxyプラグインは、ファイルのキャッシュレイヤーとしても使用できます。取得や生成にコストがかかるファイルがある場合、プラグインを使用してファイルを一度取得し、その後のリクエストに対してキャッシュされたバージョンを提供できます。
file-proxyプラグインを開発する手順
APISIXをローカルで実行し、API Gatewayをhttp://localhost:9080でホストします。開発が完了したら、サーバーや任意のクラウドプロバイダーにデプロイできます。基本的に、openapi.yaml
ファイルをhttp://localhost:9080/openapi.yaml
パスに配置したいと考えます。これを実現する方法を学びます。
前提条件
- 開始する前に、APISIXの基本的な理解があると良いでしょう。APIゲートウェイとその主要な概念(ルート、アップストリーム、Admin API、プラグイン)に慣れていることが望ましいです。HTTPプロトコルの基本的な理解も役立ちます。
- Dockerを使用して、コンテナ化されたetcdとAPISIXをインストールします。
- curlを使用して、APISIX Admin APIにリクエストを送信します。Postmanなどのツールを使用してAPIとやり取りすることもできます。
デモプロジェクトとファイルの理解
GitHub上の既存のfile-proxyデモプロジェクトを活用します。これは、既存のApisix docker exampleリポジトリと非常に似た構造を持っていますが、デモをシンプルにするために不要なファイルを削除しています。プロジェクトには3つのフォルダ、docker-compose.yml、およびサンプルのopenapi.yamlファイルがあります。
- docker-compose.ymlは、APISIX用とetcd用(APISIXの設定ストレージ)の2つのコンテナを定義します。
- custom-pluginsフォルダには、Luaで実装されたfile-proxyプラグインがあります。次のセクションでこれを確認します。
- openapi.yamlは、公開するサンプルのOpenAPI仕様です。
file-proxyプラグインの実装
まず、ChatGPTにAPISIX用のカスタムfile-proxyプラグインをLuaで実装する方法を尋ねます。ChatGPTは、実際の実装とほぼ同じガイドを生成しますが、回答は抽象的すぎるため、プロセスに従うと動作しないプラグインになってしまいます。しかし、有用なLuaコードを抽出するのに役立ちます。プラグイン開発の実際のプロセスを知っていれば、両方の知識を実践で組み合わせることが容易になります。
1. Luaファイルの作成
プロジェクトの/custom-plugins
ディレクトリに新しい空のLuaファイルを作成します。ファイル名はプラグインの名前と同じにする必要があります。例えば、プラグインがfile-proxy
という名前の場合、file-proxy.lua
というファイルを作成します。
2. APISIXにプラグインを登録
APISIXはこのプラグインファイルがどこにあるかを知り、それに応じてプラグインを実行できる必要があります。そのため、まずfile-proxy.lua
ファイルのパスをAPISIXのextra_lua_path
属性に追加して、APISIXがファイルを見つけられるようにします。これはconfig.yamlで行います。
apisix:
extra_lua_path: "/opt/?.lua"
node_listen: 9080
なぜファイルパスが/opt/?.lua
に設定されているのか疑問に思うかもしれません。これは、dockerを使用してAPISIXを実行しているためです。docker-compose.ymlファイルに3つのボリュームがあることに気づくでしょう。./custom-plugins:/opt/apisix/plugins:ro
volumes:
- ./apisix_conf/config.yaml:/usr/local/apisix/conf/config.yaml:ro
- ./openapi.yaml:/usr/local/apisix/conf/openapi.yaml:ro
- ./custom-plugins:/opt/apisix/plugins:ro
これにより、ローカルディレクトリ./custom-plugins
(カスタムプラグインの実装を含むfile-proxy.lua
ファイルがある場所)が、dockerコンテナ内の/opt/apisix/plugins
パスに読み取り専用ボリュームとしてマウントされます。これにより、カスタムプラグインがAPISIXに追加され、Docker内の別のパス(/opt/?.lua
内)で実行できるようになります。同様に、他の2つのファイルもDockerフォルダにコピーされます。
次のステップとして、プラグインをAPISIXのプラグインリストで有効にします。これは、APISIX設定ファイル(config.yaml)のplugins
リストにプラグイン名を追加することで行います:
plugins:
- file-proxy
このアクションは、config-default.yamlで指定されているすべてのデフォルトプラグインを上書きすることに注意してください。カスタムプラグインを他のプラグインと組み合わせて使用したい場合は、手動で他のプラグインを名前で追加する必要があります。
3. file-proxyプラグインのLuaコードの解説
これまで、単に何もしないプラグインを登録しました。次に、それを実装します。プラグインのロジックはLua関数として実装されます。**file-proxy.lua**でどのように行われているかを確認できます。
**file-proxy.lua**ファイルを分解して、コードの構造とフローを理解し、自分で新しいプラグインを作成するのに役立てましょう。ChatGPTにLuaコードを説明するように依頼できます:
実際、コードの説明はかなり良いものでした(部分的にChatGPTによって書かれたためです)。
このコードの重要な部分を説明しますので、迷子になったり、AIに完全に依存したりしないようにします。
4. プラグインファイルの構造
すべてのプラグインLuaファイルは以下の構造を持つ必要があります:
1. モジュール: プラグインに必要なモジュール/ライブラリをインポートします。
local core = require("apisix.core")
...
2. プラグイン名: すべてのプラグインには一意の名前があり、Luaファイル名と同じにすることができます。
local plugin_name = "file-proxy"
3. プラグインスキーマ: すべてのプラグインにはプラグインスキーマがあり、通常はプラグインへの入力を指定します。入力はAPISIXルート設定から渡されますが、これは後でプラグインをテストする際に確認できます。file-proxy
プラグインの場合、プラグインはファイルを読み取り、レスポンスを返すためにファイルパスが必要なので、パラメータはpath
で、文字列型です。スキーマは、他のプログラミング言語でのメソッド宣言とパラメータのように理解できます。
local plugin_schema = {
type = "object",
properties = {
path = {
type = "string" -- 提供するファイルのパス
},
},
required = {"path"} -- pathは必須フィールド
}
4. プラグイン定義: プラグイン実装の非常に重要な部分で、version
、priority
、name
、schema
のプロパティを持つテーブルとして定義します。name
とschema
は、先に定義したプラグインの名前とスキーマです。version
とpriority
は、APISIXがプラグインを管理するために使用されます。バージョンは通常、現在使用されているバージョンを指します(APIのバージョニングのようなものです)。プラグインロジックを公開して更新する場合、1.1
になるかもしれません(任意のバージョンを設定できます)。ただし、優先度を選択する際には非常に注意が必要です。priority
フィールドは、プラグインが実行される順序とフェーズを定義します。例えば、優先度が3000の'ip-restriction'プラグインは、優先度が0の'example-plugin'よりも前に実行されます。これは、'ip-restriction'プラグインの優先度が高いためです。独自のプラグインを開発する場合、既存のプラグインの順序を乱さないように注意してください。既存のプラグインの順序はconfig-default.yamlファイルで確認でき、Apache APISIX Plugin Development Guideを開いて決定できます。
local _M = {
version = 1.0,
priority = 1000,
name = plugin_name,
schema = plugin_schema
}
5. スキーマチェック: check_schema
Lua関数は、ルート設定(後でテストセクションで確認します)のプラグインを、先に定義したプラグインスキーマに対して検証するために使用されます。
-- プラグイン設定が正しいかどうかをチェックする関数
function _M.check_schema(conf)
-- 設定をスキーマに対して検証
local ok, err = core.schema.check(plugin_schema, conf)
-- 検証が失敗した場合、falseとエラーを返す
if not ok then
return false, err
end
-- 検証が成功した場合、trueを返す
return true
end
6. プラグインロジック: **access
**関数は、主要なプラグインロジックを記述するコア関数です。これはNginxリクエスト処理パイプラインのアクセスフェーズ中に呼び出され、トラフィックを制御し、カスタム命令を記述します。file-proxyの場合、プラグイン設定で指定されたファイルを開き、その内容を読み取り、レスポンスとして内容を返す必要があります。ファイルを開けない場合、エラーをログに記録し、404 Not Foundステータスを返します。これは、ChatGPTにこの作業を任せる正確な場所です:
コードを構造化してリファクタリングした後、以下のようになります:
function _M.access(conf, ctx)
-- 設定で指定されたファイルを開く
local fd = io.open(conf.path, "rb")
-- ファイルが正常に開かれた場合、その内容を読み取り、レスポンスとして返す
if fd then
local content = fd:read("*all")
fd:close()
ngx.header.content_length = #content
ngx.say(content)
ngx.exit(ngx.OK)
else
-- ファイルを開けない場合、エラーをログに記録し、404 Not Foundステータスを返す
ngx.exit(ngx.HTTP_NOT_FOUND)
core.log.error("File is not found: ", conf.path, ", error info: ", err)
end
end
7. ロギングロジック: プラグイン設定をログに記録することは常に望ましいです。これにより、プラグインが期待通りに動作しているかどうかをデバッグして確認できます。プラグインへのリクエストとレスポンスをログに記録できます。
-- ログフェーズ中に呼び出される関数
function _M.log(conf, ctx)
-- プラグイン設定とリクエストコンテキストをログに記録
core.log.warn("conf: ", core.json.encode(conf))
core.log.warn("ctx: ", core.json.encode(ctx, true))
end
Apache APISIXのインストール
カスタムfile-proxyプラグインを開発し、APISIXに登録する方法を学びました。次に、プラグインをテストします。apisix-file-proxy-plugin-demoプロジェクトをフォーク/クローンした後、プロジェクトのルートフォルダからdocker compose up
を実行して簡単にインストールできます。
file-proxyプラグインを使用したルートの作成
新しいfile-proxyプラグインを使用してテストするために、APISIXでプラグインを使用するルートを作成する必要があります:
curl "http://127.0.0.1:9180/apisix/admin/routes/open-api-definition" -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"name":"OpenAPI Definition",
"desc":"Route for OpenAPI Definition file",
"uri":"/openapi.yaml",
"plugins":{
"file-proxy":{
"path":"/usr/local/apisix/conf/openapi.yaml"
}
}
}'
ChatGPTに上記の設定を説明するように依頼できます:
プラグインのテスト
次に、ルートにcURLリクエストを送信するか、ブラウザでリンクhttp://127.0.0.1:9080/openapi.yamlを開きます。レスポンスは、指定されたURLにある`openapi.yaml`ファイルの内容であるはずです。
curl -i http://127.0.0.1:9080/openapi.yaml
プラグインは期待通りに動作します。このプラグイン設定により、指定されたルートを使用して任意のファイルにアクセスできるようになります。
まとめ
Luaを使用してAPISIX用のカスタムプラグインを開発することは、APIゲートウェイの機能を拡張する強力な方法です。この記事では、file-proxyプラグインを作成する方法をデモンストレーションし、プラグイン定義とスキーマを定義し、プラグイン設定を検証し、APISIXのリクエスト処理パイプラインのアクセスフェーズとログフェーズ中にカスタムロジックを実装しました。ChatGPTは、このプログラミング言語の知識が不足している部分を補うためにLuaコードを書くのに役立ちました。楽しいコーディングを!